aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/alpine.yml16
-rw-r--r--.builds/archlinux.yml7
-rw-r--r--.builds/freebsd.yml6
-rw-r--r--.clang-format16
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md6
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml4
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--README.de.md8
-rw-r--r--README.dk.md67
-rw-r--r--README.es.md10
-rw-r--r--README.fr.md66
-rw-r--r--README.gr.md73
-rw-r--r--README.hi.md70
-rw-r--r--README.hu.md77
-rw-r--r--README.ir.md60
-rw-r--r--README.it.md73
-rw-r--r--README.ja.md8
-rw-r--r--README.ko.md8
-rw-r--r--README.md58
-rw-r--r--README.nl.md10
-rw-r--r--README.pl.md10
-rw-r--r--README.pt.md10
-rw-r--r--README.ro.md8
-rw-r--r--README.ru.md43
-rw-r--r--README.sv.md89
-rw-r--r--README.tr.md68
-rw-r--r--README.uk.md10
-rw-r--r--README.zh-CN.md51
-rw-r--r--README.zh-TW.md10
-rw-r--r--client/pool-buffer.c2
-rw-r--r--common/background-image.c2
-rw-r--r--common/cairo.c4
-rw-r--r--common/gesture.c350
-rw-r--r--common/meson.build1
-rw-r--r--common/pango.c30
-rw-r--r--common/util.c12
-rw-r--r--completions/bash/sway2
-rw-r--r--completions/bash/swaybar2
-rw-r--r--completions/bash/swaymsg2
-rw-r--r--config.in8
-rwxr-xr-xcontrib/_incr_version21
-rwxr-xr-xcontrib/autoname-workspaces.py28
-rwxr-xr-xcontrib/grimshot52
-rw-r--r--contrib/grimshot.112
-rw-r--r--contrib/grimshot.1.scd8
-rwxr-xr-xcontrib/inactive-windows-transparency.py7
-rw-r--r--include/background-image.h2
-rw-r--r--include/cairo_util.h (renamed from include/cairo.h)6
-rw-r--r--include/gesture.h104
-rw-r--r--include/ipc-client.h3
-rw-r--r--include/pango.h9
-rw-r--r--include/pool-buffer.h2
-rw-r--r--include/stringop.h1
-rw-r--r--include/sway/commands.h12
-rw-r--r--include/sway/config.h97
-rw-r--r--include/sway/criteria.h7
-rw-r--r--include/sway/desktop.h2
-rw-r--r--include/sway/desktop/idle_inhibit_v1.h1
-rw-r--r--include/sway/desktop/transaction.h12
-rw-r--r--include/sway/input/cursor.h8
-rw-r--r--include/sway/input/keyboard.h1
-rw-r--r--include/sway/input/libinput.h2
-rw-r--r--include/sway/input/seat.h48
-rw-r--r--include/sway/input/switch.h1
-rw-r--r--include/sway/input/tablet.h1
-rw-r--r--include/sway/input/text_input.h5
-rw-r--r--include/sway/layers.h8
-rw-r--r--include/sway/output.h9
-rw-r--r--include/sway/server.h55
-rw-r--r--include/sway/surface.h2
-rw-r--r--include/sway/tree/container.h66
-rw-r--r--include/sway/tree/root.h2
-rw-r--r--include/sway/tree/view.h22
-rw-r--r--include/sway/tree/workspace.h12
-rw-r--r--include/swaybar/bar.h1
-rw-r--r--include/swaybar/config.h3
-rw-r--r--include/swaybar/i3bar.h1
-rw-r--r--include/swaynag/swaynag.h5
-rw-r--r--include/swaynag/types.h4
-rw-r--r--include/util.h6
-rw-r--r--meson.build159
-rw-r--r--meson_options.txt2
-rw-r--r--protocols/meson.build5
-rw-r--r--sway/commands.c105
-rw-r--r--sway/commands/bar.c5
-rw-r--r--sway/commands/bar/colors.c2
-rw-r--r--sway/commands/bar/font.c15
-rw-r--r--sway/commands/bar/hidden_state.c2
-rw-r--r--sway/commands/bar/mode.c2
-rw-r--r--sway/commands/bind.c16
-rw-r--r--sway/commands/border.c8
-rw-r--r--sway/commands/client.c16
-rw-r--r--sway/commands/exec_always.c23
-rw-r--r--sway/commands/floating.c8
-rw-r--r--sway/commands/focus.c59
-rw-r--r--sway/commands/font.c29
-rw-r--r--sway/commands/fullscreen.c34
-rw-r--r--sway/commands/gaps.c14
-rw-r--r--sway/commands/gesture.c166
-rw-r--r--sway/commands/input.c4
-rw-r--r--sway/commands/input/map_to_region.c1
-rw-r--r--sway/commands/input/xkb_switch_layout.c36
-rw-r--r--sway/commands/layout.c12
-rw-r--r--sway/commands/mode.c5
-rw-r--r--sway/commands/move.c160
-rw-r--r--sway/commands/output.c11
-rw-r--r--sway/commands/output/background.c12
-rw-r--r--sway/commands/output/dpms.c22
-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/reload.c1
-rw-r--r--sway/commands/resize.c112
-rw-r--r--sway/commands/scratchpad.c21
-rw-r--r--sway/commands/seat.c4
-rw-r--r--sway/commands/seat/attach.c2
-rw-r--r--sway/commands/seat/cursor.c4
-rw-r--r--sway/commands/smart_gaps.c7
-rw-r--r--sway/commands/split.c23
-rw-r--r--sway/commands/sticky.c6
-rw-r--r--sway/commands/swap.c59
-rw-r--r--sway/commands/title_format.c1
-rw-r--r--sway/commands/unmark.c2
-rw-r--r--sway/commands/workspace.c23
-rw-r--r--sway/config.c115
-rw-r--r--sway/config/bar.c5
-rw-r--r--sway/config/output.c203
-rw-r--r--sway/criteria.c70
-rw-r--r--sway/desktop/desktop.c9
-rw-r--r--sway/desktop/idle_inhibit_v1.c16
-rw-r--r--sway/desktop/layer_shell.c186
-rw-r--r--sway/desktop/output.c275
-rw-r--r--sway/desktop/render.c211
-rw-r--r--sway/desktop/surface.c2
-rw-r--r--sway/desktop/transaction.c253
-rw-r--r--sway/desktop/xdg_shell.c185
-rw-r--r--sway/desktop/xwayland.c96
-rw-r--r--sway/input/cursor.c286
-rw-r--r--sway/input/input-manager.c8
-rw-r--r--sway/input/keyboard.c185
-rw-r--r--sway/input/libinput.c30
-rw-r--r--sway/input/seat.c225
-rw-r--r--sway/input/seatop_default.c366
-rw-r--r--sway/input/seatop_down.c48
-rw-r--r--sway/input/seatop_move_floating.c10
-rw-r--r--sway/input/seatop_move_tiling.c170
-rw-r--r--sway/input/seatop_resize_floating.c54
-rw-r--r--sway/input/seatop_resize_tiling.c28
-rw-r--r--sway/input/switch.c32
-rw-r--r--sway/input/tablet.c23
-rw-r--r--sway/input/text_input.c51
-rw-r--r--sway/ipc-json.c216
-rw-r--r--sway/ipc-server.c11
-rw-r--r--sway/lock.c184
-rw-r--r--sway/main.c178
-rw-r--r--sway/meson.build12
-rw-r--r--sway/realtime.c40
-rw-r--r--sway/server.c112
-rw-r--r--sway/sway-bar.5.scd2
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway-ipc.7.scd12
-rw-r--r--sway/sway-output.5.scd43
-rw-r--r--sway/sway.5.scd121
-rw-r--r--sway/swaynag.c6
-rw-r--r--sway/tree/arrange.c95
-rw-r--r--sway/tree/container.c765
-rw-r--r--sway/tree/node.c18
-rw-r--r--sway/tree/output.c37
-rw-r--r--sway/tree/root.c43
-rw-r--r--sway/tree/view.c405
-rw-r--r--sway/tree/workspace.c123
-rw-r--r--sway/xdg_activation_v1.c20
-rw-r--r--sway/xdg_decoration.c28
-rw-r--r--swaybar/bar.c34
-rw-r--r--swaybar/config.c4
-rw-r--r--swaybar/i3bar.c31
-rw-r--r--swaybar/input.c12
-rw-r--r--swaybar/ipc.c29
-rw-r--r--swaybar/main.c2
-rw-r--r--swaybar/render.c383
-rw-r--r--swaybar/status_line.c13
-rw-r--r--swaybar/tray/item.c42
-rw-r--r--swaybar/tray/tray.c4
-rw-r--r--swaymsg/main.c124
-rw-r--r--swaymsg/swaymsg.1.scd20
-rw-r--r--swaynag/config.c101
-rw-r--r--swaynag/main.c50
-rw-r--r--swaynag/render.c72
-rw-r--r--swaynag/swaynag.1.scd3
-rw-r--r--swaynag/swaynag.5.scd5
-rw-r--r--swaynag/swaynag.c111
-rw-r--r--swaynag/types.c6
192 files changed, 6725 insertions, 3279 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index dc5e7c11..324cf6ce 100644
--- a/.builds/alpine.yml
+++ b/.builds/alpine.yml
@@ -6,20 +6,23 @@ packages:
6 - json-c-dev 6 - json-c-dev
7 - libevdev-dev 7 - libevdev-dev
8 - libinput-dev 8 - libinput-dev
9 - libseat-dev
9 - libxcb-dev 10 - libxcb-dev
10 - libxkbcommon-dev 11 - libxkbcommon-dev
11 - mesa-dev 12 - mesa-dev
12 - meson 13 - meson
13 - pango-dev 14 - pango-dev
15 - pcre2-dev
14 - pixman-dev 16 - pixman-dev
15 - scdoc 17 - scdoc
16 - wayland-dev 18 - wayland-dev
17 - wayland-protocols 19 - wayland-protocols
18 - xcb-util-image-dev 20 - xcb-util-image-dev
19 - xorg-server-xwayland 21 - xcb-util-wm-dev
22 - xwayland
20sources: 23sources:
21 - https://github.com/swaywm/sway 24 - https://github.com/swaywm/sway
22 - https://github.com/swaywm/wlroots 25 - https://gitlab.freedesktop.org/wlroots/wlroots.git
23tasks: 26tasks:
24 - wlroots: | 27 - wlroots: |
25 cd wlroots 28 cd wlroots
@@ -28,7 +31,7 @@ tasks:
28 sudo ninja -C build install 31 sudo ninja -C build install
29 - setup: | 32 - setup: |
30 cd sway 33 cd sway
31 meson build -Dauto_features=enabled -Dtray=disabled 34 meson build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled
32 - build: | 35 - build: |
33 cd sway 36 cd sway
34 ninja -C build 37 ninja -C build
@@ -36,3 +39,10 @@ tasks:
36 cd sway 39 cd sway
37 meson configure build -Dxwayland=disabled 40 meson configure build -Dxwayland=disabled
38 ninja -C build 41 ninja -C build
42 - build-static: |
43 cd sway
44 mkdir subprojects
45 ln -s ../../wlroots subprojects/wlroots
46 rm -rf build
47 meson build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots
48 ninja -C build
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index c0f70186..ac5536e5 100644
--- a/.builds/archlinux.yml
+++ b/.builds/archlinux.yml
@@ -9,14 +9,17 @@ packages:
9 - libxkbcommon 9 - libxkbcommon
10 - meson 10 - meson
11 - pango 11 - pango
12 - pcre2
12 - scdoc 13 - scdoc
13 - wayland 14 - wayland
14 - wayland-protocols 15 - wayland-protocols
15 - xcb-util-image 16 - xcb-util-image
17 - xcb-util-wm
16 - xorg-xwayland 18 - xorg-xwayland
19 - seatd
17sources: 20sources:
18 - https://github.com/swaywm/sway 21 - https://github.com/swaywm/sway
19 - https://github.com/swaywm/wlroots 22 - https://gitlab.freedesktop.org/wlroots/wlroots.git
20tasks: 23tasks:
21 - wlroots: | 24 - wlroots: |
22 cd wlroots 25 cd wlroots
@@ -25,7 +28,7 @@ tasks:
25 sudo ninja -C build install 28 sudo ninja -C build install
26 - setup: | 29 - setup: |
27 cd sway 30 cd sway
28 meson build -Dauto_features=enabled -Dsd-bus-provider=libsystemd 31 meson build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd
29 - build: | 32 - build: |
30 cd sway 33 cd sway
31 ninja -C build 34 ninja -C build
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index 273badbc..aa3007d8 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -4,6 +4,7 @@ packages:
4- devel/json-c 4- devel/json-c
5- devel/libevdev 5- devel/libevdev
6- devel/meson 6- devel/meson
7- devel/pcre2
7- devel/pkgconf 8- devel/pkgconf
8- graphics/cairo 9- graphics/cairo
9- graphics/gdk-pixbuf2 10- graphics/gdk-pixbuf2
@@ -19,13 +20,14 @@ 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/seatd
22- x11/libinput 24- x11/libinput
23- x11/libX11 25- x11/libX11
24- x11/pixman 26- x11/pixman
25- x11/xcb-util-wm 27- x11/xcb-util-wm
26sources: 28sources:
27- https://github.com/swaywm/sway 29- https://github.com/swaywm/sway
28- https://github.com/swaywm/wlroots 30- https://gitlab.freedesktop.org/wlroots/wlroots.git
29tasks: 31tasks:
30- setup: | 32- setup: |
31 cd sway 33 cd sway
@@ -33,7 +35,7 @@ tasks:
33 cd subprojects 35 cd subprojects
34 ln -s ../../wlroots wlroots 36 ln -s ../../wlroots wlroots
35 cd .. 37 cd ..
36 meson build -Dtray=enabled -Dsd-bus-provider=basu 38 meson build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu
37- build: | 39- build: |
38 cd sway 40 cd sway
39 ninja -C build 41 ninja -C build
diff --git a/.clang-format b/.clang-format
deleted file mode 100644
index 5818da3c..00000000
--- a/.clang-format
+++ /dev/null
@@ -1,16 +0,0 @@
1BasedOnStyle: LLVM
2IndentWidth: 4
3TabWidth: 4
4UseTab: Always
5BreakBeforeBraces: Attach
6AllowShortIfStatementsOnASingleLine: false
7IndentCaseLabels: false
8SortIncludes: false
9ColumnLimit: 80
10AlignAfterOpenBracket: DontAlign
11BinPackParameters: false
12BinPackArguments: false
13ContinuationIndentWidth: 8
14AllowAllParametersOfDeclarationOnNextLine: false
15AllowShortLoopsOnASingleLine: true
16ReflowComments: false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 156accde..eba606e6 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -6,10 +6,9 @@ labels: 'bug'
6--- 6---
7 7
8### Please read the following before submitting: 8### Please read the following before submitting:
9- Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on irc.freenode.net. 9- Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on Libera Chat.
10- Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway. 10- Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway.
11- Problems with the Wayland version of Firefox are likely to be Firefox bugs. Start by submitting your issue to the Firefox Bugzilla and come back here only after they confirm otherwise. 11- Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or obsolete workarounds.
12- Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or osbolete workarounds.
13 If you fix a script or find outdated information, don't hesitate to adjust the wiki page. 12 If you fix a script or find outdated information, don't hesitate to adjust the wiki page.
14 13
15### Please fill out the following: 14### Please fill out the following:
@@ -19,6 +18,7 @@ labels: 'bug'
19- **Debug Log:** 18- **Debug Log:**
20 - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. 19 - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com.
21 - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. 20 - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway.
21 - Attach the **full** file, do not truncate it.
22 22
23- **Configuration File:** 23- **Configuration File:**
24 - Please try to produce with the default configuration. 24 - Please try to produce with the default configuration.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index f09cdf5b..0092609b 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
1blank_issues_enabled: false 1blank_issues_enabled: false
2contact_links: 2contact_links:
3 - name: Questions 3 - name: Questions
4 url: "http://webchat.freenode.net/?channels=sway&uio=d4" 4 url: "https://libera.chat"
5 about: "Please ask questions on IRC in #sway on irc.freenode.net" 5 about: "Please ask questions on IRC in #sway on Libera Chat"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c17afdda..4f043f7a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,9 +1,8 @@
1# Contributing to sway 1# Contributing to sway
2 2
3Contributing just involves sending a pull request. You will probably be more 3Contributing just involves sending a pull request. You will probably be more
4successful with your contribution if you visit 4successful with your contribution if you visit #sway-devel on Libera Chat
5[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on 5upfront and discuss your plans.
6irc.freenode.net upfront and discuss your plans.
7 6
8Note: rules are made to be broken. Adjust or ignore any/all of these as you see 7Note: rules are made to be broken. Adjust or ignore any/all of these as you see
9fit, but be prepared to justify it to your peers. 8fit, but be prepared to justify it to your peers.
diff --git a/README.de.md b/README.de.md
index 24e66a60..059fda89 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,8 +1,8 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway on irc.freenode.net; 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 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 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 veröffentlicht.
6 6
7## Installation 7## Installation
8### Mit der Paketverwaltung 8### Mit der Paketverwaltung
@@ -15,10 +15,10 @@ Falls du sway für deine eigene Distribution als Paket bereitstellen möchtest,
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
diff --git a/README.dk.md b/README.dk.md
index 535000c3..3a641295 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](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway på irc.freenode.net).
6 5
7## Udgivelses Signaturer 6## Udgivelses Signaturer
8 7
9Udgivelser er signeret med [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 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 "svay". 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,30 @@ 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): 48På systemer uden logind eller seatd skal du sætte SUID bit på sway filen:
47 49
48 sudo chmod a+s /usr/local/bin/sway 50 sudo chmod a+s /usr/local/bin/sway
49 51
50Sway vil frasige sig 'root' tilladelser kort efter opstart 52Sway dropper 'root' tilladelser kort efter opstart.
51 53
52## Konfiguration 54## Konfiguration
53 55
54Hvis du allerede bruger i3, bør du kopiere din i3-konfiguration til `~/.config/sway/config` og 56Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til
55det vil bare fungerer. Ellers skal du kopiere eksempel konfigurations filen til 57`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til
56`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. 58`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. Kør
57Kør `man 5 sway` for at få oplysninger om konfigurationen. 59`man 5 sway` for at få oplysninger om konfigurationen.
58 60
59## Kører 61## Eksekvering
60 62
61Kør `sway` fra en TTY. Nogle display managers fungerer muligvis, men understøttes ikke af 63Kør `sway` fra en TTY. Nogle display managers kan fungere, men Sway yder ikke
62Sway (gdm er kendt for at fungere temmelig godt). 64support til dem (gdm er kendt for at fungere temmelig godt).
63 65
66[i3]: https://i3wm.org/
67[Wayland]: http://wayland.freedesktop.org/
68[Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki
69[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway
70[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
71[GitHub releases]: https://github.com/swaywm/sway/releases
72[Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup
73[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
74[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.es.md b/README.es.md
index 53f11f68..5e598e1a 100644
--- a/README.es.md
+++ b/README.es.md
@@ -1,12 +1,12 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) (#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.freenode.net). 5irc.libera.chat).
6 6
7## Firmas de las versiones 7## Firmas de las versiones
8 8
9Las distintas versiones están firmadas con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 9Las distintas versiones están firmadas con [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
10y publicadas [en GitHub](https://github.com/swaywm/sway/releases). 10y publicadas [en GitHub](https://github.com/swaywm/sway/releases).
11 11
12## Instalación 12## Instalación
@@ -25,10 +25,10 @@ escriba un email a sir@cmpwn.com
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
diff --git a/README.fr.md b/README.fr.md
index a72696d6..e99160e8 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -1,48 +1,49 @@
1# sway 1# sway
2 2
3Sway est un compositeur [Wayland](http://wayland.freedesktop.org/) compatible 3Sway est un compositeur [Wayland] compatible avec [i3]. Lisez la
4avec [i3](https://i3wm.org/), **en cours de développement**. Lisez la 4[FAQ]. Rejoignez le [canal IRC] (#sway sur irc.libera.chat).
5[FAQ](https://github.com/swaywm/sway/wiki). Rejoignez le [canal
6IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur
7irc.freenode.net).
8 5
9## Aide en français 6## Aide en français
10 7
11[abdelq](//github.com/abdelq) fournit du support en français sur IRC et Github, dans le fuseau horaire UTC-4 (EST). 8[abdelq] fournit du support en français sur IRC et Github, dans le fuseau
9horaire UTC-4 (EST).
12 10
13## Signatures de nouvelles versions 11## Signatures de nouvelles versions
14 12
15Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 13Les nouvelles versions sont signées avec [E88F5E48] et publiées
16et publiées [sur GitHub](https://github.com/swaywm/sway/releases). 14[sur GitHub][versions GitHub].
17 15
18## Installation 16## Installation
19 17
20### À partir de paquets 18### À partir de paquets
21 19
22Sway est disponible sur plusieurs distributions. Essayez d'installer le paquet "sway" pour 20Sway est disponible sur beaucoup de distributions. Essayez d'installer le
23la vôtre. Si ce n'est pas disponible, consultez [cette page wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) 21paquet "sway" pour la vôtre.
24pour de l'information sur l'installation pour vos distributions.
25 22
26Si vous êtes intéressé à maintenir Sway pour votre distribution, passez par le canal 23Si vous êtes intéressé à maintenir Sway pour votre distribution, passez sur le
27IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des conseils. 24canal IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des
25conseils.
28 26
29### Compilation depuis la source 27### Compilation depuis les sources
28
29Consultez [cette page wiki][Configuration de développement] si vous souhaitez
30compiler la révision HEAD de sway et wlroots pour tester ou développer.
30 31
31Installez les dépendances : 32Installez les dépendances :
32 33
33* meson \* 34* meson \*
34* [wlroots](https://github.com/swaywm/wlroots) 35* [wlroots]
35* wayland 36* wayland
36* wayland-protocols \* 37* wayland-protocols \*
37* pcre 38* pcre2
38* json-c 39* json-c
39* pango 40* pango
40* cairo 41* cairo
41* gdk-pixbuf2 (optionnel: system tray) 42* gdk-pixbuf2 (optionnel : system tray)
42* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optionnel: requis pour les pages man) \* 43* [scdoc] (optionnel : requis pour les pages man) \*
43* git \* 44* git (optionnel : information de version) \*
44 45
45_\*Requis uniquement pour la compilation_ 46_\* Requis uniquement pour la compilation_
46 47
47Exécutez ces commandes : 48Exécutez ces commandes :
48 49
@@ -58,12 +59,25 @@ Sway se débarassera des permissions *root* peu de temps après le démarrage.
58 59
59## Configuration 60## Configuration
60 61
61Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et 62Si vous utilisez déjà i3, copiez votre configuration i3 vers
62cela va fonctionner. Sinon, copiez l'exemple de fichier de configuration à 63`~/.config/sway/config` et sway fonctionnera directement. Sinon, copiez
63`~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. 64l'exemple de fichier de configuration vers `~/.config/sway/config`. Il se
64Exécutez `man 5 sway` pour l'information sur la configuration. 65trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour lire la
66documentation pour la configuration de sway.
65 67
66## Exécution 68## Exécution
67 69
68Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, 70Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent
69mais ne sont pas supportés par Sway (gdm est réputé pour assez bien fonctionner). 71fonctionner, mais ne sont pas supportés par Sway (gdm est réputé pour assez
72bien fonctionner).
73
74[Wayland]: http://wayland.freedesktop.org/
75[i3]: https://i3wm.org/
76[FAQ]: https://github.com/swaywm/sway/wiki
77[canal IRC]: https://web.libera.chat/gamja/?channels=#sway
78[abdelq]: https://github.com/abdelq
79[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
80[versions GitHub]: https://github.com/swaywm/sway/releases
81[Configuration de développement]: 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.gr.md b/README.gr.md
new file mode 100644
index 00000000..f325cb88
--- /dev/null
+++ b/README.gr.md
@@ -0,0 +1,73 @@
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Σε συστήματα χωÏίς logind ή seatd, Ï€Ïέπει να κάνετε suid το sway binary:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Το Sway θα κάνει drop root δικαιώματα λίγο μετά την εκκίνηση.
52
53## Configuration
54
55Εάν ήδη χÏησιμοποιήτε το i3, αντιγÏάψτε το i3 config σας στο `~/.config/sway/config` και
56θα δουλέψει out of the box. Αλλιώς, αντιγÏάψτε το sample configuration αÏχείο στο
57`~/.config/sway/config`. Το οποίο συνήθως βÏίσκεται στο `/etc/sway/config`.
58Κάντε run `man 5 sway` για πληÏοφοÏίες Ï„Î¿Ï configuration.
59
60## ΤÏέχοντας
61
62ΤÏέξτε `sway` από ένα TTY. ΜεÏίκα display managers μποÏεί να δουλέψουν αλλά δέν είναι συμβατά με
63το sway (το gdm γνωÏίζεται να δουλέβει σχετικά καλά).
64
65[i3]: https://i3wm.org/
66[Wayland]: http://wayland.freedesktop.org/
67[FAQ]: https://github.com/swaywm/sway/wiki
68[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
69[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
70[GitHub releases]: https://github.com/swaywm/sway/releases
71[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
72[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
73[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..af64eada
--- /dev/null
+++ b/README.hi.md
@@ -0,0 +1,70 @@
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उन systems पर जिनमें ना तो logind है, ना ही seatd है, आपको sway की binary
52को suid करना पडेगा:
53
54 sudo chmod a+s /usr/local/bin/sway
55
56Sway अपनी root अनà¥à¤®à¤¤à¤¿à¤¯à¤¾à¤‚ पà¥à¤°à¤¾à¤°à¤‚भ होने के कà¥à¤› ही देर बाद छोड़ देगी।
57
58## Configuration
59
60अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को
61`~/.config/sway/config` में copy कर लीजिये और वह बिना किसी परिवरà¥à¤¤à¤¨ के काम
62करेगा। अनà¥à¤¯à¤¥à¤¾, नमूने configuration file को `~/.config/sway/config` में copy
63कर लीजिये। यह सामानà¥à¤¯à¤¤à¤ƒ `/etc/sway/config` में पाया जाता है। `man 5
64sway` से आप configuration के बारे में जानकारी पà¥à¤°à¤¾à¤ªà¥à¤¤ कर सकते हैं।
65
66## चलाना
67
68आप à¤à¤• tty से `sway` को चला सकते हैं। कà¥à¤› display managers काम करते हैं परनà¥à¤¤à¥ ये
69sway के दà¥à¤µà¤¾à¤°à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है (gdm के बारे में जाना गया है कि वह सही काम करता
70है)।
diff --git a/README.hu.md b/README.hu.md
new file mode 100644
index 00000000..9e3bc9e1
--- /dev/null
+++ b/README.hu.md
@@ -0,0 +1,77 @@
1# sway
2
3A Sway egy [i3]-kompatibilis [Wayland] kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en).
4
5## Csomag aláírások
6
7A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHub releases] publikálva.
8
9## Telepítés
10
11### Csomagból
12
13A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway"
14csomagot telepíteni az általad használt eszközzel.
15
16Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC
17csatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért.
18
19### Fordítás forráskódból
20
21Olvasd el [ezt a wiki oldalt][Development setup], ha szeretnéd tesztelési vagy
22fejlesztési célokból lefordítani az aktuális (HEAD) állapotát a `sway`-nek és a
23`wlroots`-nak.
24
25Telepítsd a függőségeket:
26
27* meson \*
28* [wlroots]
29* wayland
30* wayland-protocols \*
31* pcre2
32* json-c
33* pango
34* cairo
35* gdk-pixbuf2 (opcionális: system tray)
36* [scdoc] (opcionális: man pages) \*
37* git (opcionális: version info) \*
38
39_\*Fordításidejű függőség_
40
41Futtasd ezeket a parancsokat:
42
43 meson build
44 ninja -C build
45 sudo ninja -C build install
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ó
55
56Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a
57`~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold
58le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési
59útvonalon található.
60Futtasd a `man 5 sway` parancsot további információért a konfigurációval
61kapcsolatban.
62
63## Futtatás
64
65Futtasd a `sway` parancsot egy TTY felületről. Néhány bejelentkezéskezelő
66(display manager) működhet, de alapvetően nem támogatottak a sway által. (A
67gdm-ről ismeretes, hogy egész jól működik.)
68
69[i3]: https://i3wm.org/
70[Wayland]: http://wayland.freedesktop.org/
71[FAQ]: https://github.com/swaywm/sway/wiki
72[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
73[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
74[GitHub releases]: https://github.com/swaywm/sway/releases
75[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
76[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
77[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.ir.md b/README.ir.md
new file mode 100644
index 00000000..64956a9e
--- /dev/null
+++ b/README.ir.md
@@ -0,0 +1,60 @@
1# sway
2
3‏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) عضو شوید (‎#sway‏ در
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‏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روی سیستم‌های بدون logindØŒ باید Ùرمان زیر را برای suid کردن باینری sway اجرا کنید:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52‏sway پس از startup مجوزهای دسترسی root را رها می‌کند.
53
54### شخصی سازی و تنظیمات
55
56اگر در حال حاضر از i3 استÙاده می‌کنید، تنظیمات i3 خودتان را در Ùایل ‪`~/.config/sway/config`‬ Ú©Ù¾ÛŒ کنید Ùˆ بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، Ùایل نمونه تنظیمات را استÙاده کنید. این Ùایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید.
57
58## اجرا
59
60در محیط TTY کاÙیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طر٠sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد).
diff --git a/README.it.md b/README.it.md
new file mode 100644
index 00000000..b14b522d
--- /dev/null
+++ b/README.it.md
@@ -0,0 +1,73 @@
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
45Nei sistemi in cui non sono disponibili né logind né seatd, è necessario
46impostare il permesso suid al binario di sway:
47
48 sudo chmod a+s /usr/local/bin/sway
49
50Sway rinuncerà ai permessi di root poco dopo l'avvio.
51
52## Configurazione
53
54Se hai già usato i3, copia il tuo file di configurazione in
55`~/.config/sway/config` e sway funzionerà immediatamente. Altrimenti, copia il
56file d'esempio in `~/.config/sway/config`, generalmente è situato in
57`/etc/sway/config`. Consulta `man 5 sway` per ulteriori informazioni sulla
58configurazione.
59
60## Esecuzione
61
62Lancia `sway` da un TTY. Alcuni gestori d'accesso potrebbero funzionare ma non
63sono supportati da sway (gdm funziona abbastanza bene).
64
65[i3]: https://i3wm.org/
66[Wayland]: http://wayland.freedesktop.org/
67[FAQ]: https://github.com/swaywm/sway/wiki
68[canale di IRC]: https://web.libera.chat/gamja/?channels=#sway
69[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
70[GitHub releases]: https://github.com/swaywm/sway/releases
71[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
72[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
73[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.ja.md b/README.ja.md
index fa28f3da..ece9ce48 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 ãƒãƒ£ãƒ³ãƒãƒ«](http://webchat.freenode.net/?channels=sway&uio=d4) (irc.freenode.netã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ 5[IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚
6 6
7[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 7[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
8 8
@@ -12,7 +12,7 @@ SirCmpwnã¯ã€æ—¥æœ¬èªžã§ã®ã‚µãƒãƒ¼ãƒˆã‚’IRCã¨GitHubã§è¡Œã„ã¾ã™ã€‚タイ
12 12
13## リリースã®ç½²å 13## リリースã®ç½²å
14 14
15Swayã®ãƒªãƒªãƒ¼ã‚¹ã¯[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)ã§ç½²åã•ã‚Œã€[GitHub](https://github.com/swaywm/sway/releases)ã§å…¬é–‹ã•ã‚Œã¦ã„ã¾ã™ã€‚ 15Swayã®ãƒªãƒªãƒ¼ã‚¹ã¯[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)ã§ç½²åã•ã‚Œã€[GitHub](https://github.com/swaywm/sway/releases)ã§å…¬é–‹ã•ã‚Œã¦ã„ã¾ã™ã€‚
16 16
17## インストール 17## インストール
18 18
@@ -27,10 +27,10 @@ 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
diff --git a/README.ko.md b/README.ko.md
index 9c3dd323..7a1f4ce1 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -1,11 +1,11 @@
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 채ë„](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)ë„ ìžˆìŠµë‹ˆë‹¤. 4[FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤.
5 5
6## 릴리즈 서명 6## 릴리즈 서명
7 7
8릴리즈는 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)ì—ì„œ 서명ë˜ê³ , 8릴리즈는 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)ì—ì„œ 서명ë˜ê³ ,
9[GitHubì—ì„œ](https://github.com/swaywm/sway/releases) 공개ë˜ê³  있습니다. 9[GitHubì—ì„œ](https://github.com/swaywm/sway/releases) 공개ë˜ê³  있습니다.
10 10
11## 설치 11## 설치
@@ -24,10 +24,10 @@ IRC 채ë„ì„ ë°©ë¬¸í•˜ê±°ë‚˜ sir@cmpwn.com으로 ì´ë©”ì¼ì„ ë³´ë‚´ ìƒë‹´ ë°›
24ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: 24ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요:
25 25
26* meson \* 26* meson \*
27* [wlroots](https://github.com/swaywm/wlroots) 27* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
28* wayland 28* wayland
29* wayland-protocols \* 29* wayland-protocols \*
30* pcre 30* pcre2
31* json-c 31* json-c
32* pango 32* pango
33* cairo 33* cairo
diff --git a/README.md b/README.md
index 4698afbe..27918868 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
1# sway 1# sway
2 2
3**[English][en]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [中文-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [中文-ç¹é«”][zh-TW] - [Português][pt] - [Danish][dk] - [한국어][ko] - [Română][ro] 3**[English][en]** - [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 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.freenode.net). 6[IRC channel] \(#sway on irc.libera.chat).
7 7
8## Release Signatures 8## Release Signatures
9 9
@@ -16,9 +16,6 @@ Releases are signed with [E88F5E48] and published [on GitHub][GitHub releases].
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,7 +27,7 @@ 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
@@ -38,15 +35,15 @@ Install dependencies:
38* [scdoc] (optional: man pages) \* 35* [scdoc] (optional: man pages) \*
39* git (optional: version info) \* 36* git (optional: version info) \*
40 37
41_\*Compile-time dep_ 38_\* Compile-time dep_
42 39
43Run these commands: 40Run these commands:
44 41
45 meson build 42 meson build/
46 ninja -C build 43 ninja -C build/
47 sudo ninja -C build install 44 sudo ninja -C build/ install
48 45
49On systems without logind, you need to suid the sway binary: 46On systems without logind nor seatd, you need to suid the sway binary:
50 47
51 sudo chmod a+s /usr/local/bin/sway 48 sudo chmod a+s /usr/local/bin/sway
52 49
@@ -65,26 +62,33 @@ Run `sway` from a TTY. Some display managers may work but are not supported by
65sway (gdm is known to work fairly well). 62sway (gdm is known to work fairly well).
66 63
67[en]: https://github.com/swaywm/sway#readme 64[en]: https://github.com/swaywm/sway#readme
68[ja]: https://github.com/swaywm/sway/blob/master/README.ja.md 65[de]: README.de.md
69[fr]: https://github.com/swaywm/sway/blob/master/README.fr.md 66[dk]: README.dk.md
70[uk]: https://github.com/swaywm/sway/blob/master/README.uk.md 67[es]: README.es.md
71[es]: https://github.com/swaywm/sway/blob/master/README.es.md 68[fr]: README.fr.md
72[pl]: https://github.com/swaywm/sway/blob/master/README.pl.md 69[sv]: README.sv.md
73[zh-CN]: https://github.com/swaywm/sway/blob/master/README.zh-CN.md 70[gr]: README.gr.md
74[de]: https://github.com/swaywm/sway/blob/master/README.de.md 71[hi]: README.hi.md
75[nl]: https://github.com/swaywm/sway/blob/master/README.nl.md 72[hu]: README.hu.md
76[ru]: https://github.com/swaywm/sway/blob/master/README.ru.md 73[ir]: README.ir.md
77[zh-TW]: https://github.com/swaywm/sway/blob/master/README.zh-TW.md 74[it]: README.it.md
78[pt]: https://github.com/swaywm/sway/blob/master/README.pt.md 75[ja]: README.ja.md
79[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md 76[ko]: README.ko.md
80[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md 77[nl]: README.nl.md
81[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md 78[pl]: README.pl.md
79[pt]: README.pt.md
80[ro]: README.ro.md
81[ru]: README.ru.md
82[tr]: README.tr.md
83[uk]: README.uk.md
84[zh-CN]: README.zh-CN.md
85[zh-TW]: README.zh-TW.md
82[i3]: https://i3wm.org/ 86[i3]: https://i3wm.org/
83[Wayland]: http://wayland.freedesktop.org/ 87[Wayland]: http://wayland.freedesktop.org/
84[FAQ]: https://github.com/swaywm/sway/wiki 88[FAQ]: https://github.com/swaywm/sway/wiki
85[IRC channel]: http://webchat.freenode.net/?channels=sway&uio=d4 89[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
86[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 90[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
87[GitHub releases]: https://github.com/swaywm/sway/releases 91[GitHub releases]: https://github.com/swaywm/sway/releases
88[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 92[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
89[wlroots]: https://github.com/swaywm/wlroots 93[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
90[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 94[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.nl.md b/README.nl.md
index 86ebe398..8a344f03 100644
--- a/README.nl.md
+++ b/README.nl.md
@@ -2,12 +2,12 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway op 5kanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op
6irc.freenode.net). 6irc.libera.chat).
7 7
8## Releasehandtekeningen 8## Releasehandtekeningen
9 9
10Releases worden ondertekend met [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 10Releases worden ondertekend met [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
11en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases). 11en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases).
12 12
13## Installatie 13## Installatie
@@ -25,10 +25,10 @@ kanaal of stuur een e-mail naar sir@cmpwn.com voor advies.
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
diff --git a/README.pl.md b/README.pl.md
index b63b8567..ab99abb1 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -1,12 +1,12 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) 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.freenode.net). 5(#sway na irc.libera.chat).
6 6
7## Podpisy cyfrowe wydań 7## Podpisy cyfrowe wydań
8 8
9Wydania sÄ… podpisywane przy pomocy klucza [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 9Wydania sÄ… podpisywane przy pomocy klucza [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
10i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). 10i publikowane [na GitHubie](https://github.com/swaywm/sway/releases).
11 11
12## Instalacja 12## Instalacja
@@ -25,10 +25,10 @@ adres sir@cmpwn.com w celu uzyskania wskazówek.
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
diff --git a/README.pt.md b/README.pt.md
index ad7cab65..b22948aa 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -2,12 +2,12 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway em 5IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em
6irc.freenode.net). 6irc.libera.chat).
7 7
8## Assinatura das versões 8## Assinatura das versões
9 9
10As versões são assinadas com [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 10As versões são assinadas com [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
11e publicadas [no GitHub](https://github.com/swaywm/sway/releases). 11e publicadas [no GitHub](https://github.com/swaywm/sway/releases).
12 12
13## Instalação 13## Instalação
@@ -27,10 +27,10 @@ Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development
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
diff --git a/README.ro.md b/README.ro.md
index dd895b56..4ce05093 100644
--- a/README.ro.md
+++ b/README.ro.md
@@ -1,11 +1,11 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway pe irc.freenode.net). 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
8Noile versiuni sunt semnate cu [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 8Noile versiuni sunt semnate cu [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
9și postate [pe GitHub](https://github.com/swaywm/sway/releases). 9și postate [pe GitHub](https://github.com/swaywm/sway/releases).
10 10
11## Instalare 11## Instalare
@@ -22,10 +22,10 @@ Dacă sunteți interesați in a crea pachete pentru distribuția voastră, infor
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
diff --git a/README.ru.md b/README.ru.md
index a870ec89..fb6f3344 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -1,42 +1,41 @@
1# sway 1# sway
2 2
3sway - Ñто [i3](https://i3wm.org/)-ÑовмеÑтимый композитор [Wayland](http://wayland.freedesktop.org/). 3sway - Ñто [i3]-ÑовмеÑтимый композитор [Wayland].
4Больше информации в [FAQ](https://github.com/swaywm/sway/wiki). ПриÑоединÑйтеÑÑŒ к 4Больше информации в [FAQ]. ПриÑоединÑйтеÑÑŒ к
5[IRC-каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 5[IRC-каналу][IRC channel] (#sway на
6irc.freenode.net). 6irc.libera.chat).
7 7
8## ПодпиÑи релизов 8## ПодпиÑи релизов
9 9
10Релизы подпиÑываютÑÑ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 10Релизы подпиÑываютÑÑ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ [E88F5E48] и публикуютÑÑ [на GitHub][GitHub releases].
11и публикуютÑÑ [на GitHub](https://github.com/swaywm/sway/releases).
12 11
13## УÑтановка 12## УÑтановка
14 13
15### Из репозиториев 14### Из репозиториев
16 15
17sway доÑтупен во многих диÑтрибутивах. Попробуйте уÑтановить пакет "sway". 16Sway доÑтупен во многих диÑтрибутивах. Попробуйте уÑтановить пакет "sway".
18ЕÑли он вдруг недоÑтупен, проверьте [Ñту Ñтраницу на wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages)
19Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о подробноÑÑ‚ÑÑ… уÑтановки Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾
20диÑтрибутива.
21 17
22ЕÑли вы заинтереÑованы поддерживать sway в вашем диÑтрибутиве, заглÑните в наш IRC-канал 18ЕÑли Ð²Ð°Ñ Ð¸Ð½Ñ‚ÐµÑ€ÐµÑует Ñоздание пакета sway Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ диÑтрибутива, зайдите на [IRC-канал][IRC channel]
23или обратитеÑÑŒ на sir@cmpwn.com за Ñоветом. 19или отправьте пиÑьмо на sir@cmpwn.com за Ñоветом.
24 20
25### Сборка из иÑходников 21### Сборка из иÑходников
26 22
23ПоÑетите [Ñту Ñтраницу на вики][Development setup], еÑли вы хотите поÑтроить поÑледнюю верÑию
24sway и wlroots Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ разработки.
25
27УÑтановите завиÑимоÑти: 26УÑтановите завиÑимоÑти:
28 27
29* meson \* 28* meson \*
30* [wlroots](https://github.com/swaywm/wlroots) 29* [wlroots]
31* wayland 30* wayland
32* wayland-protocols \* 31* wayland-protocols \*
33* pcre 32* pcre2
34* json-c 33* json-c
35* pango 34* pango
36* cairo 35* cairo
37* gdk-pixbuf2 (необÑзательно: Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ треÑ) 36* gdk-pixbuf2 (опÑионально: Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ треÑ)
38* [scdoc](https://git.sr.ht/~sircmpwn/scdoc)½ÐµÐ¾Ð±Ñзательно: Ð´Ð»Ñ Ñборки man-Ñтраниц) \* 37* [scdoc] (опÑионально: Ð´Ð»Ñ man-Ñтраниц) \*
39* git \* 38* git (опционально: Ð´Ð»Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о верÑии) \*
40 39
41_\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_ 40_\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_
42 41
@@ -63,3 +62,13 @@ sway ÑброÑит root-права при запуÑке.
63 62
64Выполните команду `sway` прÑмо из TTY. Ðекоторые диÑплейные менеджеры могут работать, но они не поддерживаютÑÑ Ñо Ñтороны 63Выполните команду `sway` прÑмо из TTY. Ðекоторые диÑплейные менеджеры могут работать, но они не поддерживаютÑÑ Ñо Ñтороны
65sway (gdm работает довольно неплохо). 64sway (gdm работает довольно неплохо).
65
66[i3]: https://i3wm.org/
67[Wayland]: http://wayland.freedesktop.org/
68[FAQ]: https://github.com/swaywm/sway/wiki
69[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
70[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
71[GitHub releases]: https://github.com/swaywm/sway/releases
72[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
73[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
74[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.sv.md b/README.sv.md
new file mode 100644
index 00000000..28724270
--- /dev/null
+++ b/README.sv.md
@@ -0,0 +1,89 @@
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
44På system utan logind eller seatd måste du ge sways exekverbara fil root-privilegier:
45
46 sudo chmod a+s /usr/local/bin/sway
47
48Sway kommer att överge sina root-privilegier kort efter uppstart.
49
50## Konfiguration
51
52Ifall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska.
53Kopiera annars exemplarkonfigurationsfilen till `~/.config/sway/config`. Den ligger oftast i `/etc/sway/config`.
54Kör `man 5 sway` för mer information kring konfigurationen.
55
56## Att köra sway
57
58Kör `sway` från en TTY. Vissa inloggningahanterare kan fungera men inte vara stödda av sway (gdm ska fungera hyfsat bra).
59
60[en]: https://github.com/swaywm/sway#readme
61[de]: README.de.md
62[dk]: README.dk.md
63[es]: README.es.md
64[fr]: README.fr.md
65[sv]: README.sv.md
66[gr]: README.gr.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-kanal]: 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.tr.md b/README.tr.md
new file mode 100644
index 00000000..e09d2eaf
--- /dev/null
+++ b/README.tr.md
@@ -0,0 +1,68 @@
1# sway
2
3
4Sway, [i3]-uyumlu bir [Wayland] dizgicisidir. [SSS][FAQ]'yi okuyun.
5[IRC kanalı][IRC channel]na katılın \(irc.libera.chat'te #sway (İngilizce)).
6
7## Sürüm imzaları
8
9Sürümler [E88F5E48] ile imzalandı ve [GitHub][GitHub releases]'da yayınlandı.
10
11## Kurulum
12
13### Paketler ile
14
15Sway birçok dağıtımda mevcuttur. Sizinki için "sway" paketini yüklemeyi deneyin.
16
17Dağıtımınız için sway'i paketlemekle ilgileniyorsanız, IRC kanalına uğrayın veya tavsiye için sir@cmpwn.com adresine bir e-posta gönderin.
18
19### Kaynak koddan derleme
20
21Test veya geliştirme için sway ve wlroots'un HEAD'ini oluşturmak istiyorsanız [bu wiki sayfası][Development setup]na göz atın.
22
23Aşağıdaki bağımlılıkları yükleyin:
24
25* meson \*
26* [wlroots]
27* wayland
28* wayland-protocols \*
29* pcre2
30* json-c
31* pango
32* cairo
33* gdk-pixbuf2 (isteğe bağlı: system tray)
34* [scdoc] (isteğe bağlı: man pages) \*
35* git (isteğe bağlı: version info) \*
36
37_\*Derleme-anı bağımlılıkları_
38
39Şu komutları çalıştırın:
40
41 meson build
42 ninja -C build
43 sudo ninja -C build install
44
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
52
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.
54Yapılandırma hakkında bilgi almak için `man 5 sway` komutunu çalıştırın.
55
56## Çalıştırma
57
58TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manager) çalışabilir ama Sway tarafından desteklenmez. (gdm'nin oldukça iyi çalıştığı bilinmektedir.)
59
60[i3]: https://i3wm.org/
61[Wayland]: http://wayland.freedesktop.org/
62[FAQ]: https://github.com/swaywm/sway/wiki
63[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
64[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
65[GitHub releases]: https://github.com/swaywm/sway/releases
66[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
67[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.uk.md b/README.uk.md
index 95047cb8..d9adcd13 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -2,8 +2,8 @@
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](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 5IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на
6irc.freenode.net). 6irc.libera.chat).
7 7
8## Підтримка українÑькою мовою 8## Підтримка українÑькою мовою
9 9
@@ -15,7 +15,7 @@ Hummer12007 у IRC-Ñпільноті. Будьте терплÑчі, вам оÐ
15 15
16## ПідпиÑи випуÑків 16## ПідпиÑи випуÑків
17 17
18ВипуÑки підпиÑані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 18ВипуÑки підпиÑані ключем [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
19та публікуютьÑÑ Ð½Ð° Ñторінці [GitHub](https://github.com/swaywm/sway/releases). 19та публікуютьÑÑ Ð½Ð° Ñторінці [GitHub](https://github.com/swaywm/sway/releases).
20 20
21## Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ 21## Ð’ÑтановленнÑ
@@ -36,10 +36,10 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
36Ð’Ñтановіть залежноÑÑ‚Ñ–: 36Ð’Ñтановіть залежноÑÑ‚Ñ–:
37 37
38* meson \* 38* meson \*
39* [wlroots](https://github.com/swaywm/wlroots) 39* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
40* wayland 40* wayland
41* wayland-protocols \* 41* wayland-protocols \*
42* pcre 42* pcre2
43* json-c 43* json-c
44* pango 44* pango
45* cairo 45* cairo
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 9a3337ce..b057bfb8 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,63 +1,56 @@
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频é“](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
6irc.freenode.net).
7 5
8## å‘布签å 6## å‘行签å
9 7
10å‘布是以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) ç­¾å 8æ¯ä¸ªå‘行版都以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 的密钥签å并å‘布在 [GitHub](https://github.com/swaywm/sway/releases)上。
11并å‘布在 [GitHub](https://github.com/swaywm/sway/releases).
12 9
13## 安装 10## 安装
14 11
15### 从软件包中 12### 从包管ç†å™¨å®‰è£…
16 13
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 41
47在没有 logind 的系统上, 你需è¦ç»™ sway 二进制设置 suid: 42在没有logind或seatd的系统上, 你需è¦ç»™sway二进制文件设置suid:
48 43
49 sudo chmod a+s /usr/local/bin/sway 44 sudo chmod a+s /usr/local/bin/sway
50 45
51Sway 将会在å¯åŠ¨åŽå°½å¿«ä¸¢æŽ‰ root æƒé™. 46å¯åŠ¨åŽï¼ŒSway会尽快放弃rootæƒé™ã€‚
52 47
53## é…ç½® 48## é…ç½®
54 49
55如果你已ç»åœ¨ä½¿ç”¨ i3, 接下æ¥å¤åˆ¶ä½ çš„ i3 é…置到 `~/.config/sway/config` 50如果你已ç»åœ¨ä½¿ç”¨i3,直接å¤åˆ¶i3é…置文件到 `~/.config/sway/config`,这是开箱å³ç”¨çš„。或者,你å¯ä»¥å¤åˆ¶é…置样例到`~/.config/sway/config`。它通常ä½äºŽ `/etc/sway/config`。
56它å¯ä»¥ç›´æŽ¥å·¥ä½œ. 或者, å¤åˆ¶æ ·æœ¬é…置文件到 51è¿è¡Œ `man 5 sway` 获å–关于é…置的更多信æ¯ã€‚
57`~/.config/sway/config`. 它通常ä½äºŽ `/etc/sway/config`.
58è¿è¡Œ `man 5 sway` 获å–关于é…置的信æ¯.
59 52
60## è¿è¡Œ 53## è¿è¡Œ
61 54
62从 TTY 中è¿è¡Œ `sway` . æŸäº›æ˜¾ç¤ºç®¡ç†å™¨å¯èƒ½ä¼šå·¥ä½œä½†å¹¶ä¸è¢« sway æ”¯æŒ 55从 TTY 中è¿è¡Œ `sway`。 æŸäº›æ˜¾ç¤ºç®¡ç†å™¨ï¼ˆDisplay Manager)也许å¯ä»¥å·¥ä½œä½†ä¸è¢« sway 支æŒã€‚
63(已知的 gdm 工作得éžå¸¸å¥½). 56(已知 gdm 工作得éžå¸¸å¥½)。
diff --git a/README.zh-TW.md b/README.zh-TW.md
index 13a9d2f4..a7168244 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -2,12 +2,12 @@
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é »é“](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 5é »é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on
6irc.freenode.net) 6irc.libera.chat)
7 7
8## 發行簽章 8## 發行簽章
9 9
10所有發行的版本都會以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 簽署 10所有發行的版本都會以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 簽署
11並發佈於 [GitHub](https://github.com/swaywm/sway/releases) 11並發佈於 [GitHub](https://github.com/swaywm/sway/releases)
12 12
13## å®‰è£ 13## 安è£
@@ -25,10 +25,10 @@ Sway 在許多發行版都有æä¾›ã€‚è«‹è‡ªå·±å˜—è©¦æ–¼ä½ çš„ç™¼è¡Œç‰ˆå®‰è£ ã€
25相ä¾å¥—件: 25相ä¾å¥—件:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre2
32* json-c 32* json-c
33* pango 33* pango
34* cairo 34* cairo
diff --git a/client/pool-buffer.c b/client/pool-buffer.c
index fd500c49..ea31edd3 100644
--- a/client/pool-buffer.c
+++ b/client/pool-buffer.c
@@ -1,6 +1,6 @@
1#define _POSIX_C_SOURCE 200809 1#define _POSIX_C_SOURCE 200809
2#include <assert.h> 2#include <assert.h>
3#include <cairo/cairo.h> 3#include <cairo.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>
diff --git a/common/background-image.c b/common/background-image.c
index de42e8e9..994a0805 100644
--- a/common/background-image.c
+++ b/common/background-image.c
@@ -1,6 +1,6 @@
1#include <assert.h> 1#include <assert.h>
2#include "background-image.h" 2#include "background-image.h"
3#include "cairo.h" 3#include "cairo_util.h"
4#include "log.h" 4#include "log.h"
5#if HAVE_GDK_PIXBUF 5#if HAVE_GDK_PIXBUF
6#include <gdk-pixbuf/gdk-pixbuf.h> 6#include <gdk-pixbuf/gdk-pixbuf.h>
diff --git a/common/cairo.c b/common/cairo.c
index 403dcf49..7c59d48c 100644
--- a/common/cairo.c
+++ b/common/cairo.c
@@ -1,6 +1,6 @@
1#include <stdint.h> 1#include <stdint.h>
2#include <cairo/cairo.h> 2#include <cairo.h>
3#include "cairo.h" 3#include "cairo_util.h"
4 4
5void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 5void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
6 cairo_set_source_rgba(cairo, 6 cairo_set_source_rgba(cairo,
diff --git a/common/gesture.c b/common/gesture.c
new file mode 100644
index 00000000..8c2efe99
--- /dev/null
+++ b/common/gesture.c
@@ -0,0 +1,350 @@
1#define _POSIX_C_SOURCE 200809L
2#include "gesture.h"
3
4#include <math.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include "list.h"
10#include "log.h"
11#include "stringop.h"
12
13const uint8_t GESTURE_FINGERS_ANY = 0;
14
15// Helper to easily allocate and format string
16static char *strformat(const char *format, ...) {
17 va_list args;
18 va_start(args, format);
19 int length = vsnprintf(NULL, 0, format, args) + 1;
20 va_end(args);
21
22 char *result = malloc(length);
23 if (result) {
24 va_start(args, format);
25 vsnprintf(result, length, format, args);
26 va_end(args);
27 }
28
29 return result;
30}
31
32char *gesture_parse(const char *input, struct gesture *output) {
33 // Clear output in case of failure
34 output->type = GESTURE_TYPE_NONE;
35 output->fingers = GESTURE_FINGERS_ANY;
36 output->directions = GESTURE_DIRECTION_NONE;
37
38 // Split input type, fingers and directions
39 list_t *split = split_string(input, ":");
40 if (split->length < 1 || split->length > 3) {
41 return strformat(
42 "expected <gesture>[:<fingers>][:direction], got %s",
43 input);
44 }
45
46 // Parse gesture type
47 if (strcmp(split->items[0], "hold") == 0) {
48 output->type = GESTURE_TYPE_HOLD;
49 } else if (strcmp(split->items[0], "pinch") == 0) {
50 output->type = GESTURE_TYPE_PINCH;
51 } else if (strcmp(split->items[0], "swipe") == 0) {
52 output->type = GESTURE_TYPE_SWIPE;
53 } else {
54 return strformat("expected hold|pinch|swipe, got %s",
55 split->items[0]);
56 }
57
58 // Parse optional arguments
59 if (split->length > 1) {
60 char *next = split->items[1];
61
62 // Try to parse as finger count (1-9)
63 if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
64 output->fingers = atoi(next);
65
66 // Move to next if available
67 next = split->length == 3 ? split->items[2] : NULL;
68 } else if (split->length == 3) {
69 // Fail here if argument can only be finger count
70 return strformat("expected 1-9, got %s", next);
71 }
72
73 // If there is an argument left, try to parse as direction
74 if (next) {
75 list_t *directions = split_string(next, "+");
76
77 for (int i = 0; i < directions->length; ++i) {
78 const char *item = directions->items[i];
79 if (strcmp(item, "any") == 0) {
80 continue;
81 } else if (strcmp(item, "up") == 0) {
82 output->directions |= GESTURE_DIRECTION_UP;
83 } else if (strcmp(item, "down") == 0) {
84 output->directions |= GESTURE_DIRECTION_DOWN;
85 } else if (strcmp(item, "left") == 0) {
86 output->directions |= GESTURE_DIRECTION_LEFT;
87 } else if (strcmp(item, "right") == 0) {
88 output->directions |= GESTURE_DIRECTION_RIGHT;
89 } else if (strcmp(item, "inward") == 0) {
90 output->directions |= GESTURE_DIRECTION_INWARD;
91 } else if (strcmp(item, "outward") == 0) {
92 output->directions |= GESTURE_DIRECTION_OUTWARD;
93 } else if (strcmp(item, "clockwise") == 0) {
94 output->directions |= GESTURE_DIRECTION_CLOCKWISE;
95 } else if (strcmp(item, "counterclockwise") == 0) {
96 output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
97 } else {
98 return strformat("expected direction, got %s", item);
99 }
100 }
101 list_free_items_and_destroy(directions);
102 }
103 } // if optional args
104
105 list_free_items_and_destroy(split);
106
107 return NULL;
108}
109
110const char *gesture_type_string(enum gesture_type type) {
111 switch (type) {
112 case GESTURE_TYPE_NONE:
113 return "none";
114 case GESTURE_TYPE_HOLD:
115 return "hold";
116 case GESTURE_TYPE_PINCH:
117 return "pinch";
118 case GESTURE_TYPE_SWIPE:
119 return "swipe";
120 }
121
122 return NULL;
123}
124
125const char *gesture_direction_string(enum gesture_direction direction) {
126 switch (direction) {
127 case GESTURE_DIRECTION_NONE:
128 return "none";
129 case GESTURE_DIRECTION_UP:
130 return "up";
131 case GESTURE_DIRECTION_DOWN:
132 return "down";
133 case GESTURE_DIRECTION_LEFT:
134 return "left";
135 case GESTURE_DIRECTION_RIGHT:
136 return "right";
137 case GESTURE_DIRECTION_INWARD:
138 return "inward";
139 case GESTURE_DIRECTION_OUTWARD:
140 return "outward";
141 case GESTURE_DIRECTION_CLOCKWISE:
142 return "clockwise";
143 case GESTURE_DIRECTION_COUNTERCLOCKWISE:
144 return "counterclockwise";
145 }
146
147 return NULL;
148}
149
150// Helper to turn combination of directions flags into string representation.
151static char *gesture_directions_to_string(uint32_t directions) {
152 char *result = NULL;
153
154 for (uint8_t bit = 0; bit < 32; bit++) {
155 uint32_t masked = directions & (1 << bit);
156 if (masked) {
157 const char *name = gesture_direction_string(masked);
158
159 if (!name) {
160 name = "unknown";
161 }
162
163 if (!result) {
164 result = strdup(name);
165 } else {
166 char *new = strformat("%s+%s", result, name);
167 free(result);
168 result = new;
169 }
170 }
171 }
172
173 if(!result) {
174 return strdup("any");
175 }
176
177 return result;
178}
179
180char *gesture_to_string(struct gesture *gesture) {
181 char *directions = gesture_directions_to_string(gesture->directions);
182 char *result = strformat("%s:%u:%s",
183 gesture_type_string(gesture->type),
184 gesture->fingers, directions);
185 free(directions);
186 return result;
187}
188
189bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) {
190 // Check that gesture type matches
191 if (target->type != type) {
192 return false;
193 }
194
195 // Check that finger count matches
196 if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) {
197 return false;
198 }
199
200 return true;
201}
202
203bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) {
204 // Check type and fingers
205 if (!gesture_check(target, to_match->type, to_match->fingers)) {
206 return false;
207 }
208
209 // Enforce exact matches ...
210 if (exact && target->directions != to_match->directions) {
211 return false;
212 }
213
214 // ... or ensure all target directions are matched
215 return (target->directions & to_match->directions) == target->directions;
216}
217
218bool gesture_equal(struct gesture *a, struct gesture *b) {
219 return a->type == b->type
220 && a->fingers == b->fingers
221 && a->directions == b->directions;
222}
223
224// Return count of set bits in directions bit field.
225static uint8_t gesture_directions_count(uint32_t directions) {
226 uint8_t count = 0;
227 for (; directions; directions >>= 1) {
228 count += directions & 1;
229 }
230 return count;
231}
232
233// Compare direction bit count of two direction.
234static int8_t gesture_directions_compare(uint32_t a, uint32_t b) {
235 return gesture_directions_count(a) - gesture_directions_count(b);
236}
237
238// Compare two direction based on their direction bit count
239int8_t gesture_compare(struct gesture *a, struct gesture *b) {
240 return gesture_directions_compare(a->directions, b->directions);
241}
242
243void gesture_tracker_begin(struct gesture_tracker *tracker,
244 enum gesture_type type, uint8_t fingers) {
245 tracker->type = type;
246 tracker->fingers = fingers;
247
248 tracker->dx = 0.0;
249 tracker->dy = 0.0;
250 tracker->scale = 1.0;
251 tracker->rotation = 0.0;
252
253 sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture",
254 gesture_type_string(type), fingers);
255}
256
257bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) {
258 return tracker->type == type;
259}
260
261void gesture_tracker_update(struct gesture_tracker *tracker,
262 double dx, double dy, double scale, double rotation) {
263 if (tracker->type == GESTURE_TYPE_HOLD) {
264 sway_assert(false, "hold does not update.");
265 return;
266 }
267
268 tracker->dx += dx;
269 tracker->dy += dy;
270
271 if (tracker->type == GESTURE_TYPE_PINCH) {
272 tracker->scale = scale;
273 tracker->rotation += rotation;
274 }
275
276 sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f",
277 gesture_type_string(tracker->type),
278 tracker->fingers,
279 tracker->dx, tracker->dy,
280 tracker->scale, tracker->rotation);
281}
282
283void gesture_tracker_cancel(struct gesture_tracker *tracker) {
284 sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture",
285 gesture_type_string(tracker->type), tracker->fingers);
286
287 tracker->type = GESTURE_TYPE_NONE;
288}
289
290struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
291 struct gesture *result = calloc(1, sizeof(struct gesture));
292
293 // Ignore gesture under some threshold
294 // TODO: Make configurable
295 const double min_rotation = 5;
296 const double min_scale_delta = 0.1;
297
298 // Determine direction
299 switch(tracker->type) {
300 // Gestures with scale and rotation
301 case GESTURE_TYPE_PINCH:
302 if (tracker->rotation > min_rotation) {
303 result->directions |= GESTURE_DIRECTION_CLOCKWISE;
304 }
305 if (tracker->rotation < -min_rotation) {
306 result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
307 }
308
309 if (tracker->scale > (1.0 + min_scale_delta)) {
310 result->directions |= GESTURE_DIRECTION_OUTWARD;
311 }
312 if (tracker->scale < (1.0 - min_scale_delta)) {
313 result->directions |= GESTURE_DIRECTION_INWARD;
314 }
315 __attribute__ ((fallthrough));
316 // Gestures with dx and dy
317 case GESTURE_TYPE_SWIPE:
318 if (fabs(tracker->dx) > fabs(tracker->dy)) {
319 if (tracker->dx > 0) {
320 result->directions |= GESTURE_DIRECTION_RIGHT;
321 } else {
322 result->directions |= GESTURE_DIRECTION_LEFT;
323 }
324 } else {
325 if (tracker->dy > 0) {
326 result->directions |= GESTURE_DIRECTION_DOWN;
327 } else {
328 result->directions |= GESTURE_DIRECTION_UP;
329 }
330 }
331 // Gesture without any direction
332 case GESTURE_TYPE_HOLD:
333 break;
334 // Not tracking any gesture
335 case GESTURE_TYPE_NONE:
336 sway_assert(false, "Not tracking any gesture.");
337 return result;
338 }
339
340 result->type = tracker->type;
341 result->fingers = tracker->fingers;
342
343 char *description = gesture_to_string(result);
344 sway_log(SWAY_DEBUG, "end tracking gesture: %s", description);
345 free(description);
346
347 tracker->type = GESTURE_TYPE_NONE;
348
349 return result;
350}
diff --git a/common/meson.build b/common/meson.build
index c653dd72..3756075a 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -3,6 +3,7 @@ lib_sway_common = static_library(
3 files( 3 files(
4 'background-image.c', 4 'background-image.c',
5 'cairo.c', 5 'cairo.c',
6 'gesture.c',
6 'ipc-client.c', 7 'ipc-client.c',
7 'log.c', 8 'log.c',
8 'loop.c', 9 'loop.c',
diff --git a/common/pango.c b/common/pango.c
index fc3d0688..e04bf80f 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -1,4 +1,4 @@
1#include <cairo/cairo.h> 1#include <cairo.h>
2#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
3#include <stdarg.h> 3#include <stdarg.h>
4#include <stdbool.h> 4#include <stdbool.h>
@@ -6,7 +6,7 @@
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include "cairo.h" 9#include "cairo_util.h"
10#include "log.h" 10#include "log.h"
11#include "stringop.h" 11#include "stringop.h"
12 12
@@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) {
50 return length; 50 return length;
51} 51}
52 52
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,16 +73,14 @@ 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);
@@ -99,7 +97,7 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
99 vsnprintf(buf, length, fmt, args); 97 vsnprintf(buf, length, fmt, args);
100 va_end(args); 98 va_end(args);
101 99
102 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 100 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
103 pango_cairo_update_layout(cairo, layout); 101 pango_cairo_update_layout(cairo, layout);
104 pango_layout_get_pixel_size(layout, width, height); 102 pango_layout_get_pixel_size(layout, width, height);
105 if (baseline) { 103 if (baseline) {
@@ -109,7 +107,21 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
109 free(buf); 107 free(buf);
110} 108}
111 109
112void pango_printf(cairo_t *cairo, const char *font, 110void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) {
111 cairo_t *cairo = cairo_create(NULL);
112 PangoContext *pango = pango_cairo_create_context(cairo);
113 // When passing NULL as a language, pango uses the current locale.
114 PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
115
116 *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
117 *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
118
119 pango_font_metrics_unref(metrics);
120 g_object_unref(pango);
121 cairo_destroy(cairo);
122}
123
124void render_text(cairo_t *cairo, const PangoFontDescription *desc,
113 double scale, bool markup, const char *fmt, ...) { 125 double scale, bool markup, const char *fmt, ...) {
114 va_list args; 126 va_list args;
115 va_start(args, fmt); 127 va_start(args, fmt);
@@ -126,7 +138,7 @@ void pango_printf(cairo_t *cairo, const char *font,
126 vsnprintf(buf, length, fmt, args); 138 vsnprintf(buf, length, fmt, args);
127 va_end(args); 139 va_end(args);
128 140
129 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 141 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
130 cairo_font_options_t *fo = cairo_font_options_create(); 142 cairo_font_options_t *fo = cairo_font_options_create();
131 cairo_get_font_options(cairo, fo); 143 cairo_get_font_options(cairo, fo);
132 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); 144 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
diff --git a/common/util.c b/common/util.c
index 5ea94f48..5d4c0673 100644
--- a/common/util.c
+++ b/common/util.c
@@ -10,12 +10,6 @@
10#include "log.h" 10#include "log.h"
11#include "util.h" 11#include "util.h"
12 12
13uint32_t get_current_time_msec(void) {
14 struct timespec now;
15 clock_gettime(CLOCK_MONOTONIC, &now);
16 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
17}
18
19int wrap(int i, int max) { 13int wrap(int i, int max) {
20 return ((i % max) + max) % max; 14 return ((i % max) + max) % max;
21} 15}
@@ -86,6 +80,12 @@ enum movement_unit parse_movement_unit(const char *unit) {
86 80
87int parse_movement_amount(int argc, char **argv, 81int parse_movement_amount(int argc, char **argv,
88 struct movement_amount *amount) { 82 struct movement_amount *amount) {
83 if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) {
84 amount->amount = 0;
85 amount->unit = MOVEMENT_UNIT_INVALID;
86 return 0;
87 }
88
89 char *err; 89 char *err;
90 amount->amount = (int)strtol(argv[0], &err, 10); 90 amount->amount = (int)strtol(argv[0], &err, 10);
91 if (*err) { 91 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/config.in b/config.in
index 08703bef..3eda7ac7 100644
--- a/config.in
+++ b/config.in
@@ -14,7 +14,7 @@ set $down j
14set $up k 14set $up k
15set $right l 15set $right l
16# Your preferred terminal emulator 16# Your preferred terminal emulator
17set $term alacritty 17set $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.
@@ -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 %I:%M:%S %p'; do sleep 1; done
209 209
210 colors { 210 colors {
211 statusline #ffffff 211 statusline #ffffff
diff --git a/contrib/_incr_version b/contrib/_incr_version
deleted file mode 100755
index a4fa2654..00000000
--- a/contrib/_incr_version
+++ /dev/null
@@ -1,21 +0,0 @@
1#!/bin/sh -eu
2old_version="$1"
3new_version="$2"
4
5if [ "$new_version" != "${new_version#v}" ]
6then
7 echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
8 exit 1
9fi
10
11sed -i meson.build -e "s/^ version: .*#release_version/ version: '$new_version', #release_version/g"
12
13printf "Minimum wlroots version? "
14read wlr_version_min
15printf "Maximum wlroots version? "
16read wlr_version_max
17
18sed -i meson.build -e "s/wlroots_version =.*/wlroots_version = ['>=$wlr_version_min', '<$wlr_version_max']/"
19
20git add meson.build
21git commit -m "Update version to $new_version"
diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py
index 297d91b2..3ec39928 100755
--- a/contrib/autoname-workspaces.py
+++ b/contrib/autoname-workspaces.py
@@ -22,24 +22,17 @@ DEFAULT_ICON = "ó°€"
22 22
23 23
24def icon_for_window(window): 24def icon_for_window(window):
25 app_id = window.app_id 25 name = None
26 if app_id is not None and len(app_id) > 0: 26 if window.app_id is not None and len(window.app_id) > 0:
27 app_id = app_id.lower() 27 name = window.app_id.lower()
28 if app_id in WINDOW_ICONS: 28 elif window.window_class is not None and len(window.window_class) > 0:
29 return WINDOW_ICONS[app_id] 29 name = window.window_class.lower()
30 logging.info("No icon available for window with app_id: %s" % str(app_id)) 30
31 else: 31 if name in WINDOW_ICONS:
32 # xwayland support 32 return WINDOW_ICONS[name]
33 class_name = window.window_class
34 if len(class_name) > 0:
35 class_name = class_name.lower()
36 if class_name in WINDOW_ICONS:
37 return WINDOW_ICONS[class_name]
38 logging.info(
39 "No icon available for window with class_name: %s" % str(class_name)
40 )
41 return DEFAULT_ICON
42 33
34 logging.info("No icon available for window with name: %s" % str(name))
35 return DEFAULT_ICON
43 36
44def rename_workspaces(ipc): 37def rename_workspaces(ipc):
45 for workspace in ipc.get_tree().workspaces(): 38 for workspace in ipc.get_tree().workspaces():
@@ -128,3 +121,4 @@ if __name__ == "__main__":
128 rename_workspaces(ipc) 121 rename_workspaces(ipc)
129 122
130 ipc.main() 123 ipc.main()
124
diff --git a/contrib/grimshot b/contrib/grimshot
index 461a5eef..1ec19def 100755
--- a/contrib/grimshot
+++ b/contrib/grimshot
@@ -13,18 +13,32 @@
13## See `man 1 grimshot` or `grimshot usage` for further details. 13## See `man 1 grimshot` or `grimshot usage` for further details.
14 14
15getTargetDirectory() { 15getTargetDirectory() {
16 test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \ 16 test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" && \
17 . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs 17 . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
18 18
19 echo ${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}} 19 echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
20} 20}
21 21
22if [ "$1" = "--notify" ]; then 22NOTIFY=no
23 NOTIFY=yes 23CURSOR=
24 shift 1 24
25else 25while [ $# -gt 0 ]; do
26 NOTIFY=no 26 key="$1"
27fi 27
28 case $key in
29 -n|--notify)
30 NOTIFY=yes
31 shift # past argument
32 ;;
33 -c|--cursor)
34 CURSOR=yes
35 shift # past argument
36 ;;
37 *) # unknown option
38 break # done with parsing --flags
39 ;;
40 esac
41done
28 42
29ACTION=${1:-usage} 43ACTION=${1:-usage}
30SUBJECT=${2:-screen} 44SUBJECT=${2:-screen}
@@ -32,13 +46,13 @@ FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
32 46
33if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then 47if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
34 echo "Usage:" 48 echo "Usage:"
35 echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE]" 49 echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]"
36 echo " grimshot check" 50 echo " grimshot check"
37 echo " grimshot usage" 51 echo " grimshot usage"
38 echo "" 52 echo ""
39 echo "Commands:" 53 echo "Commands:"
40 echo " copy: Copy the screenshot data into the clipboard." 54 echo " copy: Copy the screenshot data into the clipboard."
41 echo " save: Save the screenshot to a regular file." 55 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." 56 echo " check: Verify if required tools are installed and exit."
43 echo " usage: Show this message and exit." 57 echo " usage: Show this message and exit."
44 echo "" 58 echo ""
@@ -67,7 +81,7 @@ notifyError() {
67 MESSAGE=${1:-"Error taking screenshot with grim"} 81 MESSAGE=${1:-"Error taking screenshot with grim"}
68 notify -u critical "$TITLE" "$MESSAGE" 82 notify -u critical "$TITLE" "$MESSAGE"
69 else 83 else
70 echo $1 84 echo "$1"
71 fi 85 fi
72} 86}
73 87
@@ -91,12 +105,12 @@ takeScreenshot() {
91 FILE=$1 105 FILE=$1
92 GEOM=$2 106 GEOM=$2
93 OUTPUT=$3 107 OUTPUT=$3
94 if [ ! -z "$OUTPUT" ]; then 108 if [ -n "$OUTPUT" ]; then
95 grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" 109 grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
96 elif [ -z "$GEOM" ]; then 110 elif [ -z "$GEOM" ]; then
97 grim "$FILE" || die "Unable to invoke grim" 111 grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim"
98 else 112 else
99 grim -g "$GEOM" "$FILE" || die "Unable to invoke grim" 113 grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
100 fi 114 fi
101} 115}
102 116
@@ -113,7 +127,7 @@ elif [ "$SUBJECT" = "area" ] ; then
113 GEOM=$(slurp -d) 127 GEOM=$(slurp -d)
114 # Check if user exited slurp without selecting the area 128 # Check if user exited slurp without selecting the area
115 if [ -z "$GEOM" ]; then 129 if [ -z "$GEOM" ]; then
116 exit 130 exit 1
117 fi 131 fi
118 WHAT="Area" 132 WHAT="Area"
119elif [ "$SUBJECT" = "active" ] ; then 133elif [ "$SUBJECT" = "active" ] ; then
@@ -132,7 +146,7 @@ elif [ "$SUBJECT" = "window" ] ; then
132 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) 146 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 147 # Check if user exited slurp without selecting the area
134 if [ -z "$GEOM" ]; then 148 if [ -z "$GEOM" ]; then
135 exit 149 exit 1
136 fi 150 fi
137 WHAT="Window" 151 WHAT="Window"
138else 152else
@@ -147,7 +161,7 @@ else
147 TITLE="Screenshot of $SUBJECT" 161 TITLE="Screenshot of $SUBJECT"
148 MESSAGE=$(basename "$FILE") 162 MESSAGE=$(basename "$FILE")
149 notifyOk "$MESSAGE" "$TITLE" 163 notifyOk "$MESSAGE" "$TITLE"
150 echo $FILE 164 echo "$FILE"
151 else 165 else
152 notifyError "Error taking screenshot with grim" 166 notifyError "Error taking screenshot with grim"
153 fi 167 fi
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1
index f6c8a377..2c4c6a95 100644
--- a/contrib/grimshot.1
+++ b/contrib/grimshot.1
@@ -1,11 +1,11 @@
1.\" Generated by scdoc 1.11.1 1.\" Generated by scdoc 1.11.2
2.\" Complete documentation for this program is not available as a GNU info page 2.\" Complete documentation for this program is not available as a GNU info page
3.ie \n(.g .ds Aq \(aq 3.ie \n(.g .ds Aq \(aq
4.el .ds Aq ' 4.el .ds Aq '
5.nh 5.nh
6.ad l 6.ad l
7.\" Begin generated content: 7.\" Begin generated content:
8.TH "grimshot" "1" "2020-12-20" 8.TH "grimshot" "1" "2022-03-31"
9.P 9.P
10.SH NAME 10.SH NAME
11.P 11.P
@@ -13,7 +13,7 @@ grimshot - a helper for screenshots within sway
13.P 13.P
14.SH SYNOPSIS 14.SH SYNOPSIS
15.P 15.P
16\fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE] 16\fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE]
17.br 17.br
18\fBgrimshot\fR check 18\fBgrimshot\fR check
19.br 19.br
@@ -26,11 +26,17 @@ grimshot - a helper for screenshots within sway
26Show notifications to the user that a screenshot has been taken.\& 26Show notifications to the user that a screenshot has been taken.\&
27.P 27.P
28.RE 28.RE
29\fB--cursor\fR
30.RS 4
31Include cursors in the screenshot.\&
32.P
33.RE
29\fBsave\fR 34\fBsave\fR
30.RS 4 35.RS 4
31Save the screenshot into a regular file.\& Grimshot will write images 36Save the screenshot into a regular file.\& Grimshot will write images
32files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined 37files 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.\& 38in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
39Set FILE to '\&-'\& to pipe the output to STDOUT.\&
34.P 40.P
35.RE 41.RE
36\fBcopy\fR 42\fBcopy\fR
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd
index 4ab58532..e356f99d 100644
--- a/contrib/grimshot.1.scd
+++ b/contrib/grimshot.1.scd
@@ -6,7 +6,7 @@ grimshot - a helper for screenshots within sway
6 6
7# SYNOPSIS 7# SYNOPSIS
8 8
9*grimshot* [--notify] (copy|save) [TARGET] [FILE]++ 9*grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++
10*grimshot* check++ 10*grimshot* check++
11*grimshot* usage 11*grimshot* usage
12 12
@@ -15,10 +15,14 @@ grimshot - a helper for screenshots within sway
15*--notify* 15*--notify*
16 Show notifications to the user that a screenshot has been taken. 16 Show notifications to the user that a screenshot has been taken.
17 17
18*--cursor*
19 Include cursors in the screenshot.
20
18*save* 21*save*
19 Save the screenshot into a regular file. Grimshot will write images 22 Save the screenshot into a regular file. Grimshot will write image
20 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined 23 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
21 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. 24 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
25 Set FILE to '-' to pipe the output to STDOUT.
22 26
23*copy* 27*copy*
24 Copy the screenshot data (as image/png) into the clipboard. 28 Copy the screenshot data (as image/png) into the clipboard.
diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py
index 77b1f221..b81134dd 100755
--- a/contrib/inactive-windows-transparency.py
+++ b/contrib/inactive-windows-transparency.py
@@ -15,8 +15,13 @@ def on_window_focus(inactive_opacity, ipc, event):
15 global prev_focused 15 global prev_focused
16 global prev_workspace 16 global prev_workspace
17 17
18 focused_workspace = ipc.get_tree().find_focused()
19
20 if focused_workspace == None:
21 return
22
18 focused = event.container 23 focused = event.container
19 workspace = ipc.get_tree().find_focused().workspace().num 24 workspace = focused_workspace.workspace().num
20 25
21 if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 26 if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859
22 focused.command("opacity 1") 27 focused.command("opacity 1")
diff --git a/include/background-image.h b/include/background-image.h
index 15935ffd..a97ef375 100644
--- a/include/background-image.h
+++ b/include/background-image.h
@@ -1,6 +1,6 @@
1#ifndef _SWAY_BACKGROUND_IMAGE_H 1#ifndef _SWAY_BACKGROUND_IMAGE_H
2#define _SWAY_BACKGROUND_IMAGE_H 2#define _SWAY_BACKGROUND_IMAGE_H
3#include "cairo.h" 3#include "cairo_util.h"
4 4
5enum background_mode { 5enum background_mode {
6 BACKGROUND_MODE_STRETCH, 6 BACKGROUND_MODE_STRETCH,
diff --git a/include/cairo.h b/include/cairo_util.h
index c1275db2..dc049c6d 100644
--- a/include/cairo.h
+++ b/include/cairo_util.h
@@ -1,8 +1,8 @@
1#ifndef _SWAY_CAIRO_H 1#ifndef _SWAY_CAIRO_UTIL_H
2#define _SWAY_CAIRO_H 2#define _SWAY_CAIRO_UTIL_H
3#include "config.h" 3#include "config.h"
4#include <stdint.h> 4#include <stdint.h>
5#include <cairo/cairo.h> 5#include <cairo.h>
6#include <wayland-client-protocol.h> 6#include <wayland-client-protocol.h>
7 7
8void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 8void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
diff --git a/include/gesture.h b/include/gesture.h
new file mode 100644
index 00000000..9c6b0f91
--- /dev/null
+++ b/include/gesture.h
@@ -0,0 +1,104 @@
1#ifndef _SWAY_GESTURE_H
2#define _SWAY_GESTURE_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7/**
8 * A gesture type used in binding.
9 */
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 6ab83c16..1db113c2 100644
--- a/include/pango.h
+++ b/include/pango.h
@@ -3,7 +3,7 @@
3#include <stdarg.h> 3#include <stdarg.h>
4#include <stdbool.h> 4#include <stdbool.h>
5#include <stdint.h> 5#include <stdint.h>
6#include <cairo/cairo.h> 6#include <cairo.h>
7#include <pango/pangocairo.h> 7#include <pango/pangocairo.h>
8 8
9/** 9/**
@@ -13,11 +13,12 @@
13 * escaped string to dest if provided. 13 * escaped string to dest if provided.
14 */ 14 */
15size_t escape_markup_text(const char *src, char *dest); 15size_t escape_markup_text(const char *src, char *dest);
16PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 16PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
17 const char *text, double scale, bool markup); 17 const char *text, double scale, bool markup);
18void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 18void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
19 int *baseline, double scale, bool markup, const char *fmt, ...); 19 int *baseline, double scale, bool markup, const char *fmt, ...);
20void pango_printf(cairo_t *cairo, const char *font, 20void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline);
21void render_text(cairo_t *cairo, PangoFontDescription *desc,
21 double scale, bool markup, const char *fmt, ...); 22 double scale, bool markup, const char *fmt, ...);
22 23
23#endif 24#endif
diff --git a/include/pool-buffer.h b/include/pool-buffer.h
index 54f5be06..b7a95afe 100644
--- a/include/pool-buffer.h
+++ b/include/pool-buffer.h
@@ -1,6 +1,6 @@
1#ifndef _SWAY_BUFFERS_H 1#ifndef _SWAY_BUFFERS_H
2#define _SWAY_BUFFERS_H 2#define _SWAY_BUFFERS_H
3#include <cairo/cairo.h> 3#include <cairo.h>
4#include <pango/pangocairo.h> 4#include <pango/pangocairo.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stdint.h> 6#include <stdint.h>
diff --git a/include/stringop.h b/include/stringop.h
index 8d7089e9..b29f59b2 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -2,6 +2,7 @@
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
7void strip_whitespace(char *str); 8void strip_whitespace(char *str);
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 964b3661..013a7b82 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -46,8 +46,8 @@ enum expected_args {
46struct cmd_results *checkarg(int argc, const char *name, 46struct cmd_results *checkarg(int argc, const char *name,
47 enum expected_args type, int val); 47 enum expected_args type, int val);
48 48
49struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, 49const struct cmd_handler *find_handler(char *line,
50 size_t handlers_size); 50 const struct cmd_handler *cmd_handlers, size_t handlers_size);
51 51
52/** 52/**
53 * Parse and executes a command. 53 * Parse and executes a command.
@@ -68,7 +68,7 @@ struct cmd_results *config_command(char *command, char **new_block);
68 * Parse and handle a sub command 68 * Parse and handle a sub command
69 */ 69 */
70struct cmd_results *config_subcommand(char **argv, int argc, 70struct cmd_results *config_subcommand(char **argv, int argc,
71 struct cmd_handler *handlers, size_t handlers_size); 71 const struct cmd_handler *handlers, size_t handlers_size);
72/* 72/*
73 * Parses a command policy rule. 73 * Parses a command policy rule.
74 */ 74 */
@@ -106,12 +106,14 @@ sway_cmd cmd_exec_process;
106sway_cmd cmd_assign; 106sway_cmd cmd_assign;
107sway_cmd cmd_bar; 107sway_cmd cmd_bar;
108sway_cmd cmd_bindcode; 108sway_cmd cmd_bindcode;
109sway_cmd cmd_bindgesture;
109sway_cmd cmd_bindswitch; 110sway_cmd cmd_bindswitch;
110sway_cmd cmd_bindsym; 111sway_cmd cmd_bindsym;
111sway_cmd cmd_border; 112sway_cmd cmd_border;
112sway_cmd cmd_client_noop; 113sway_cmd cmd_client_noop;
113sway_cmd cmd_client_focused; 114sway_cmd cmd_client_focused;
114sway_cmd cmd_client_focused_inactive; 115sway_cmd cmd_client_focused_inactive;
116sway_cmd cmd_client_focused_tab_title;
115sway_cmd cmd_client_unfocused; 117sway_cmd cmd_client_unfocused;
116sway_cmd cmd_client_urgent; 118sway_cmd cmd_client_urgent;
117sway_cmd cmd_client_placeholder; 119sway_cmd cmd_client_placeholder;
@@ -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;
@@ -282,7 +285,10 @@ sway_cmd output_cmd_dpms;
282sway_cmd output_cmd_enable; 285sway_cmd output_cmd_enable;
283sway_cmd output_cmd_max_render_time; 286sway_cmd output_cmd_max_render_time;
284sway_cmd output_cmd_mode; 287sway_cmd output_cmd_mode;
288sway_cmd output_cmd_modeline;
285sway_cmd output_cmd_position; 289sway_cmd output_cmd_position;
290sway_cmd output_cmd_power;
291sway_cmd output_cmd_render_bit_depth;
286sway_cmd output_cmd_scale; 292sway_cmd output_cmd_scale;
287sway_cmd output_cmd_scale_filter; 293sway_cmd output_cmd_scale_filter;
288sway_cmd output_cmd_subpixel; 294sway_cmd output_cmd_subpixel;
diff --git a/include/sway/config.h b/include/sway/config.h
index 59f22ae2..68c06846 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -5,16 +5,19 @@
5#include <string.h> 5#include <string.h>
6#include <time.h> 6#include <time.h>
7#include <wlr/interfaces/wlr_switch.h> 7#include <wlr/interfaces/wlr_switch.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_tablet_tool.h> 8#include <wlr/types/wlr_tablet_tool.h>
9#include <wlr/util/box.h>
10#include <xkbcommon/xkbcommon.h> 10#include <xkbcommon/xkbcommon.h>
11#include <xf86drmMode.h>
11#include "../include/config.h" 12#include "../include/config.h"
13#include "gesture.h"
12#include "list.h" 14#include "list.h"
13#include "swaynag.h" 15#include "swaynag.h"
14#include "tree/container.h" 16#include "tree/container.h"
15#include "sway/input/tablet.h" 17#include "sway/input/tablet.h"
16#include "sway/tree/root.h" 18#include "sway/tree/root.h"
17#include "wlr-layer-shell-unstable-v1-protocol.h" 19#include "wlr-layer-shell-unstable-v1-protocol.h"
20#include <pango/pangocairo.h>
18 21
19// TODO: Refactor this shit 22// TODO: Refactor this shit
20 23
@@ -31,7 +34,8 @@ enum binding_input_type {
31 BINDING_KEYSYM, 34 BINDING_KEYSYM,
32 BINDING_MOUSECODE, 35 BINDING_MOUSECODE,
33 BINDING_MOUSESYM, 36 BINDING_MOUSESYM,
34 BINDING_SWITCH 37 BINDING_SWITCH, // dummy, only used to call seat_execute_command
38 BINDING_GESTURE // dummy, only used to call seat_execute_command
35}; 39};
36 40
37enum binding_flags { 41enum binding_flags {
@@ -44,10 +48,11 @@ enum binding_flags {
44 BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload 48 BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload
45 BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor 49 BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
46 BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key 50 BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
51 BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
47}; 52};
48 53
49/** 54/**
50 * A key binding and an associated command. 55 * A key (or mouse) binding and an associated command.
51 */ 56 */
52struct sway_binding { 57struct sway_binding {
53 enum binding_input_type type; 58 enum binding_input_type type;
@@ -61,12 +66,10 @@ struct sway_binding {
61 char *command; 66 char *command;
62}; 67};
63 68
64/** 69enum sway_switch_trigger {
65 * A mouse binding and an associated command. 70 SWAY_SWITCH_TRIGGER_OFF,
66 */ 71 SWAY_SWITCH_TRIGGER_ON,
67struct sway_mouse_binding { 72 SWAY_SWITCH_TRIGGER_TOGGLE,
68 uint32_t button;
69 char *command;
70}; 73};
71 74
72/** 75/**
@@ -74,12 +77,22 @@ struct sway_mouse_binding {
74 */ 77 */
75struct sway_switch_binding { 78struct sway_switch_binding {
76 enum wlr_switch_type type; 79 enum wlr_switch_type type;
77 enum wlr_switch_state state; 80 enum sway_switch_trigger trigger;
78 uint32_t flags; 81 uint32_t flags;
79 char *command; 82 char *command;
80}; 83};
81 84
82/** 85/**
86 * A gesture binding and an associated command.
87 */
88struct sway_gesture_binding {
89 char *input;
90 uint32_t flags;
91 struct gesture gesture;
92 char *command;
93};
94
95/**
83 * Focus on window activation. 96 * Focus on window activation.
84 */ 97 */
85enum sway_fowa { 98enum sway_fowa {
@@ -98,6 +111,7 @@ struct sway_mode {
98 list_t *keycode_bindings; 111 list_t *keycode_bindings;
99 list_t *mouse_bindings; 112 list_t *mouse_bindings;
100 list_t *switch_bindings; 113 list_t *switch_bindings;
114 list_t *gesture_bindings;
101 bool pango; 115 bool pango;
102}; 116};
103 117
@@ -233,12 +247,6 @@ struct seat_config {
233 } xcursor_theme; 247 } xcursor_theme;
234}; 248};
235 249
236enum config_dpms {
237 DPMS_IGNORE,
238 DPMS_ON,
239 DPMS_OFF,
240};
241
242enum scale_filter_mode { 250enum scale_filter_mode {
243 SCALE_FILTER_DEFAULT, // the default is currently smart 251 SCALE_FILTER_DEFAULT, // the default is currently smart
244 SCALE_FILTER_LINEAR, 252 SCALE_FILTER_LINEAR,
@@ -246,6 +254,12 @@ enum scale_filter_mode {
246 SCALE_FILTER_SMART, 254 SCALE_FILTER_SMART,
247}; 255};
248 256
257enum render_bit_depth {
258 RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8
259 RENDER_BIT_DEPTH_8,
260 RENDER_BIT_DEPTH_10,
261};
262
249/** 263/**
250 * Size and position configuration for a particular output. 264 * Size and position configuration for a particular output.
251 * 265 *
@@ -254,9 +268,11 @@ enum scale_filter_mode {
254struct output_config { 268struct output_config {
255 char *name; 269 char *name;
256 int enabled; 270 int enabled;
271 int power;
257 int width, height; 272 int width, height;
258 float refresh_rate; 273 float refresh_rate;
259 int custom_mode; 274 int custom_mode;
275 drmModeModeInfo drm_mode;
260 int x, y; 276 int x, y;
261 float scale; 277 float scale;
262 enum scale_filter_mode scale_filter; 278 enum scale_filter_mode scale_filter;
@@ -264,11 +280,11 @@ struct output_config {
264 enum wl_output_subpixel subpixel; 280 enum wl_output_subpixel subpixel;
265 int max_render_time; // In milliseconds 281 int max_render_time; // In milliseconds
266 int adaptive_sync; 282 int adaptive_sync;
283 enum render_bit_depth render_bit_depth;
267 284
268 char *background; 285 char *background;
269 char *background_option; 286 char *background_option;
270 char *background_fallback; 287 char *background_fallback;
271 enum config_dpms dpms_state;
272}; 288};
273 289
274/** 290/**
@@ -281,6 +297,12 @@ struct side_gaps {
281 int left; 297 int left;
282}; 298};
283 299
300enum smart_gaps_mode {
301 SMART_GAPS_OFF,
302 SMART_GAPS_ON,
303 SMART_GAPS_INVERSE_OUTER,
304};
305
284/** 306/**
285 * Stores configuration for a workspace, regardless of whether the workspace 307 * Stores configuration for a workspace, regardless of whether the workspace
286 * exists. 308 * exists.
@@ -292,6 +314,12 @@ struct workspace_config {
292 struct side_gaps gaps_outer; 314 struct side_gaps gaps_outer;
293}; 315};
294 316
317enum pango_markup_config {
318 PANGO_MARKUP_DISABLED = false,
319 PANGO_MARKUP_ENABLED = true,
320 PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix)
321};
322
295struct bar_config { 323struct bar_config {
296 char *swaybar_command; 324 char *swaybar_command;
297 struct wl_client *client; 325 struct wl_client *client;
@@ -323,7 +351,7 @@ struct bar_config {
323 char *position; 351 char *position;
324 list_t *bindings; 352 list_t *bindings;
325 char *status_command; 353 char *status_command;
326 bool pango_markup; 354 enum pango_markup_config pango_markup;
327 char *font; 355 char *font;
328 int height; // -1 not defined 356 int height; // -1 not defined
329 bool workspace_buttons; 357 bool workspace_buttons;
@@ -410,14 +438,6 @@ enum sway_popup_during_fullscreen {
410 POPUP_LEAVE, 438 POPUP_LEAVE,
411}; 439};
412 440
413enum command_context {
414 CONTEXT_CONFIG = 1 << 0,
415 CONTEXT_BINDING = 1 << 1,
416 CONTEXT_IPC = 1 << 2,
417 CONTEXT_CRITERIA = 1 << 3,
418 CONTEXT_ALL = 0xFFFFFFFF,
419};
420
421enum focus_follows_mouse_mode { 441enum focus_follows_mouse_mode {
422 FOLLOWS_NO, 442 FOLLOWS_NO,
423 FOLLOWS_YES, 443 FOLLOWS_YES,
@@ -479,9 +499,10 @@ struct sway_config {
479 char *floating_scroll_right_cmd; 499 char *floating_scroll_right_cmd;
480 enum sway_container_layout default_orientation; 500 enum sway_container_layout default_orientation;
481 enum sway_container_layout default_layout; 501 enum sway_container_layout default_layout;
482 char *font; 502 char *font; // Used for IPC.
483 size_t font_height; 503 PangoFontDescription *font_description; // Used internally for rendering and validating.
484 size_t font_baseline; 504 int font_height;
505 int font_baseline;
485 bool pango_markup; 506 bool pango_markup;
486 int titlebar_border_thickness; 507 int titlebar_border_thickness;
487 int titlebar_h_padding; 508 int titlebar_h_padding;
@@ -512,7 +533,7 @@ struct sway_config {
512 bool tiling_drag; 533 bool tiling_drag;
513 int tiling_drag_threshold; 534 int tiling_drag_threshold;
514 535
515 bool smart_gaps; 536 enum smart_gaps_mode smart_gaps;
516 int gaps_inner; 537 int gaps_inner;
517 struct side_gaps gaps_outer; 538 struct side_gaps gaps_outer;
518 539
@@ -535,12 +556,15 @@ struct sway_config {
535 struct { 556 struct {
536 struct border_colors focused; 557 struct border_colors focused;
537 struct border_colors focused_inactive; 558 struct border_colors focused_inactive;
559 struct border_colors focused_tab_title;
538 struct border_colors unfocused; 560 struct border_colors unfocused;
539 struct border_colors urgent; 561 struct border_colors urgent;
540 struct border_colors placeholder; 562 struct border_colors placeholder;
541 float background[4]; 563 float background[4];
542 } border_colors; 564 } border_colors;
543 565
566 bool has_focused_tab_title;
567
544 // floating view 568 // floating view
545 int32_t floating_maximum_width; 569 int32_t floating_maximum_width;
546 int32_t floating_maximum_height; 570 int32_t floating_maximum_height;
@@ -559,7 +583,7 @@ struct sway_config {
559 struct sway_node *node; 583 struct sway_node *node;
560 struct sway_container *container; 584 struct sway_container *container;
561 struct sway_workspace *workspace; 585 struct sway_workspace *workspace;
562 bool using_criteria; 586 bool node_overridden; // True if the node is selected by means other than focus
563 struct { 587 struct {
564 int argc; 588 int argc;
565 char **argv; 589 char **argv;
@@ -675,6 +699,8 @@ void free_sway_binding(struct sway_binding *sb);
675 699
676void free_switch_binding(struct sway_switch_binding *binding); 700void free_switch_binding(struct sway_switch_binding *binding);
677 701
702void free_gesture_binding(struct sway_gesture_binding *binding);
703
678void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); 704void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
679 705
680void load_swaybar(struct bar_config *bar); 706void load_swaybar(struct bar_config *bar);
@@ -690,14 +716,13 @@ void free_bar_binding(struct bar_binding *binding);
690void free_workspace_config(struct workspace_config *wsc); 716void free_workspace_config(struct workspace_config *wsc);
691 717
692/** 718/**
693 * Updates the value of config->font_height based on the max title height 719 * Updates the value of config->font_height based on the metrics for title's
694 * reported by each container. If recalculate is true, the containers will 720 * font as reported by pango.
695 * recalculate their heights before reporting. 721 *
696 *
697 * If the height has changed, all containers will be rearranged to take on the 722 * If the height has changed, all containers will be rearranged to take on the
698 * new size. 723 * new size.
699 */ 724 */
700void config_update_font_height(bool recalculate); 725void config_update_font_height(void);
701 726
702/** 727/**
703 * Convert bindsym into bindcode using the first configured layout. 728 * Convert bindsym into bindcode using the first configured layout.
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index ad8610cd..59f57f94 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 {
diff --git a/include/sway/desktop.h b/include/sway/desktop.h
index c969a76b..7f2f5b3e 100644
--- a/include/sway/desktop.h
+++ b/include/sway/desktop.h
@@ -1,4 +1,4 @@
1#include <wlr/types/wlr_surface.h> 1#include <wlr/types/wlr_compositor.h>
2 2
3struct sway_container; 3struct sway_container;
4struct sway_view; 4struct sway_view;
diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h
index 0adafdb9..58d54c68 100644
--- a/include/sway/desktop/idle_inhibit_v1.h
+++ b/include/sway/desktop/idle_inhibit_v1.h
@@ -22,6 +22,7 @@ struct sway_idle_inhibit_manager_v1 {
22 22
23struct sway_idle_inhibitor_v1 { 23struct sway_idle_inhibitor_v1 {
24 struct sway_idle_inhibit_manager_v1 *manager; 24 struct sway_idle_inhibit_manager_v1 *manager;
25 struct wlr_idle_inhibitor_v1 *wlr_inhibitor;
25 struct sway_view *view; 26 struct sway_view *view;
26 enum sway_idle_inhibit_mode mode; 27 enum sway_idle_inhibit_mode mode;
27 28
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 175489c5..7dd58ba8 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -28,6 +28,12 @@ struct sway_view;
28 */ 28 */
29void transaction_commit_dirty(void); 29void transaction_commit_dirty(void);
30 30
31/*
32 * Same as transaction_commit_dirty, but signalling that this is a
33 * client-initiated change has already taken effect.
34 */
35void transaction_commit_dirty_client(void);
36
31/** 37/**
32 * Notify the transaction system that a view is ready for the new layout. 38 * Notify the transaction system that a view is ready for the new layout.
33 * 39 *
@@ -45,10 +51,4 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view,
45void transaction_notify_view_ready_by_geometry(struct sway_view *view, 51void transaction_notify_view_ready_by_geometry(struct sway_view *view,
46 double x, double y, int width, int height); 52 double x, double y, int width, int height);
47 53
48/**
49 * Unconditionally notify the transaction system that a view is ready for the
50 * new layout.
51 */
52void transaction_notify_view_ready_immediately(struct sway_view *view);
53
54#endif 54#endif
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 6a38190b..8a2898dd 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
@@ -36,6 +36,8 @@ struct sway_cursor {
36 bool active_confine_requires_warp; 36 bool active_confine_requires_warp;
37 37
38 struct wlr_pointer_gestures_v1 *pointer_gestures; 38 struct wlr_pointer_gestures_v1 *pointer_gestures;
39 struct wl_listener hold_begin;
40 struct wl_listener hold_end;
39 struct wl_listener pinch_begin; 41 struct wl_listener pinch_begin;
40 struct wl_listener pinch_update; 42 struct wl_listener pinch_update;
41 struct wl_listener pinch_end; 43 struct wl_listener pinch_end;
@@ -52,7 +54,9 @@ struct sway_cursor {
52 struct wl_listener touch_down; 54 struct wl_listener touch_down;
53 struct wl_listener touch_up; 55 struct wl_listener touch_up;
54 struct wl_listener touch_motion; 56 struct wl_listener touch_motion;
57 struct wl_listener touch_frame;
55 bool simulating_pointer_from_touch; 58 bool simulating_pointer_from_touch;
59 bool pointer_touch_up;
56 int32_t pointer_touch_id; 60 int32_t pointer_touch_id;
57 61
58 struct wl_listener tool_axis; 62 struct wl_listener tool_axis;
@@ -108,7 +112,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
108 enum wlr_button_state state); 112 enum wlr_button_state state);
109 113
110void dispatch_cursor_axis(struct sway_cursor *cursor, 114void dispatch_cursor_axis(struct sway_cursor *cursor,
111 struct wlr_event_pointer_axis *event); 115 struct wlr_pointer_axis_event *event);
112 116
113void cursor_set_image(struct sway_cursor *cursor, const char *image, 117void cursor_set_image(struct sway_cursor *cursor, const char *image,
114 struct wl_client *client); 118 struct wl_client *client);
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 de019976..890d632e 100644
--- a/include/sway/input/libinput.h
+++ b/include/sway/input/libinput.h
@@ -6,4 +6,6 @@ void sway_input_configure_libinput_device(struct sway_input_device *device);
6 6
7void sway_input_reset_libinput_device(struct sway_input_device *device); 7void sway_input_reset_libinput_device(struct sway_input_device *device);
8 8
9bool sway_libinput_device_is_builtin(struct sway_input_device *device);
10
9#endif 11#endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 4118df66..c2041742 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -18,7 +18,23 @@ struct sway_seatop_impl {
18 enum wlr_button_state state); 18 enum wlr_button_state state);
19 void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); 19 void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec);
20 void (*pointer_axis)(struct sway_seat *seat, 20 void (*pointer_axis)(struct sway_seat *seat,
21 struct wlr_event_pointer_axis *event); 21 struct wlr_pointer_axis_event *event);
22 void (*hold_begin)(struct sway_seat *seat,
23 struct wlr_pointer_hold_begin_event *event);
24 void (*hold_end)(struct sway_seat *seat,
25 struct wlr_pointer_hold_end_event *event);
26 void (*pinch_begin)(struct sway_seat *seat,
27 struct wlr_pointer_pinch_begin_event *event);
28 void (*pinch_update)(struct sway_seat *seat,
29 struct wlr_pointer_pinch_update_event *event);
30 void (*pinch_end)(struct sway_seat *seat,
31 struct wlr_pointer_pinch_end_event *event);
32 void (*swipe_begin)(struct sway_seat *seat,
33 struct wlr_pointer_swipe_begin_event *event);
34 void (*swipe_update)(struct sway_seat *seat,
35 struct wlr_pointer_swipe_update_event *event);
36 void (*swipe_end)(struct sway_seat *seat,
37 struct wlr_pointer_swipe_end_event *event);
22 void (*rebase)(struct sway_seat *seat, uint32_t time_msec); 38 void (*rebase)(struct sway_seat *seat, uint32_t time_msec);
23 void (*tablet_tool_motion)(struct sway_seat *seat, 39 void (*tablet_tool_motion)(struct sway_seat *seat,
24 struct sway_tablet_tool *tool, uint32_t time_msec); 40 struct sway_tablet_tool *tool, uint32_t time_msec);
@@ -185,6 +201,10 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat);
185 201
186struct sway_container *seat_get_focused_container(struct sway_seat *seat); 202struct sway_container *seat_get_focused_container(struct sway_seat *seat);
187 203
204// Force focus to a particular surface that is not part of the workspace
205// hierarchy (used for lockscreen)
206void sway_force_focus(struct wlr_surface *surface);
207
188/** 208/**
189 * Return the last container to be focused for the seat (or the most recently 209 * Return the last container to be focused for the seat (or the most recently
190 * opened if no container has received focused) that is a child of the given 210 * opened if no container has received focused) that is a child of the given
@@ -239,7 +259,10 @@ enum wlr_edges find_resize_edge(struct sway_container *cont,
239void seatop_begin_default(struct sway_seat *seat); 259void seatop_begin_default(struct sway_seat *seat);
240 260
241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 261void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
242 uint32_t time_msec, int sx, int sy); 262 uint32_t time_msec, double sx, double sy);
263
264void seatop_begin_down_on_surface(struct sway_seat *seat,
265 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy);
243 266
244void seatop_begin_move_floating(struct sway_seat *seat, 267void seatop_begin_move_floating(struct sway_seat *seat,
245 struct sway_container *con); 268 struct sway_container *con);
@@ -271,7 +294,7 @@ void seatop_button(struct sway_seat *seat, uint32_t time_msec,
271void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); 294void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
272 295
273void seatop_pointer_axis(struct sway_seat *seat, 296void seatop_pointer_axis(struct sway_seat *seat,
274 struct wlr_event_pointer_axis *event); 297 struct wlr_pointer_axis_event *event);
275 298
276void seatop_tablet_tool_tip(struct sway_seat *seat, 299void seatop_tablet_tool_tip(struct sway_seat *seat,
277 struct sway_tablet_tool *tool, uint32_t time_msec, 300 struct sway_tablet_tool *tool, uint32_t time_msec,
@@ -280,6 +303,25 @@ void seatop_tablet_tool_tip(struct sway_seat *seat,
280void seatop_tablet_tool_motion(struct sway_seat *seat, 303void seatop_tablet_tool_motion(struct sway_seat *seat,
281 struct sway_tablet_tool *tool, uint32_t time_msec); 304 struct sway_tablet_tool *tool, uint32_t time_msec);
282 305
306void seatop_hold_begin(struct sway_seat *seat,
307 struct wlr_pointer_hold_begin_event *event);
308void seatop_hold_end(struct sway_seat *seat,
309 struct wlr_pointer_hold_end_event *event);
310
311void seatop_pinch_begin(struct sway_seat *seat,
312 struct wlr_pointer_pinch_begin_event *event);
313void seatop_pinch_update(struct sway_seat *seat,
314 struct wlr_pointer_pinch_update_event *event);
315void seatop_pinch_end(struct sway_seat *seat,
316 struct wlr_pointer_pinch_end_event *event);
317
318void seatop_swipe_begin(struct sway_seat *seat,
319 struct wlr_pointer_swipe_begin_event *event);
320void seatop_swipe_update(struct sway_seat *seat,
321 struct wlr_pointer_swipe_update_event *event);
322void seatop_swipe_end(struct sway_seat *seat,
323 struct wlr_pointer_swipe_end_event *event);
324
283void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); 325void seatop_rebase(struct sway_seat *seat, uint32_t time_msec);
284 326
285/** 327/**
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..c0a5aff7 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;
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
index 6cf9bdb3..c70fd935 100644
--- a/include/sway/input/text_input.h
+++ b/include/sway/input/text_input.h
@@ -3,7 +3,7 @@
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" 7#include "sway/input/seat.h"
8 8
9/** 9/**
@@ -28,7 +28,10 @@ struct sway_input_method_relay {
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_grab_keyboard;
31 struct wl_listener input_method_destroy; 32 struct wl_listener input_method_destroy;
33
34 struct wl_listener input_method_keyboard_grab_destroy;
32}; 35};
33 36
34struct sway_text_input { 37struct sway_text_input {
diff --git a/include/sway/layers.h b/include/sway/layers.h
index 457634c2..f8508493 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -1,8 +1,7 @@
1#ifndef _SWAY_LAYERS_H 1#ifndef _SWAY_LAYERS_H
2#define _SWAY_LAYERS_H 2#define _SWAY_LAYERS_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/types/wlr_box.h> 4#include <wlr/types/wlr_compositor.h>
5#include <wlr/types/wlr_surface.h>
6#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
7 6
8enum layer_parent { 7enum layer_parent {
@@ -23,7 +22,11 @@ struct sway_layer_surface {
23 struct wl_listener new_subsurface; 22 struct wl_listener new_subsurface;
24 23
25 struct wlr_box geo; 24 struct wlr_box geo;
25 bool mapped;
26 struct wlr_box extent;
26 enum zwlr_layer_shell_v1_layer layer; 27 enum zwlr_layer_shell_v1_layer layer;
28
29 struct wl_list subsurfaces;
27}; 30};
28 31
29struct sway_layer_popup { 32struct sway_layer_popup {
@@ -43,6 +46,7 @@ struct sway_layer_popup {
43struct sway_layer_subsurface { 46struct sway_layer_subsurface {
44 struct wlr_subsurface *wlr_subsurface; 47 struct wlr_subsurface *wlr_subsurface;
45 struct sway_layer_surface *layer_surface; 48 struct sway_layer_surface *layer_surface;
49 struct wl_list link;
46 50
47 struct wl_listener map; 51 struct wl_listener map;
48 struct wl_listener unmap; 52 struct wl_listener unmap;
diff --git a/include/sway/output.h b/include/sway/output.h
index 96986700..6d8319bf 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -3,7 +3,6 @@
3#include <time.h> 3#include <time.h>
4#include <unistd.h> 4#include <unistd.h>
5#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_box.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
8#include "config.h" 7#include "config.h"
9#include "sway/tree/node.h" 8#include "sway/tree/node.h"
@@ -33,7 +32,7 @@ struct sway_output {
33 int width, height; // transformed buffer size 32 int width, height; // transformed buffer size
34 enum wl_output_subpixel detected_subpixel; 33 enum wl_output_subpixel detected_subpixel;
35 enum scale_filter_mode scale_filter; 34 enum scale_filter_mode scale_filter;
36 // last applied mode when the output is DPMS'ed 35 // last applied mode when the output is powered off
37 struct wlr_output_mode *current_mode; 36 struct wlr_output_mode *current_mode;
38 37
39 bool enabling, enabled; 38 bool enabling, enabled;
@@ -49,7 +48,7 @@ struct sway_output {
49 struct wl_listener damage_frame; 48 struct wl_listener damage_frame;
50 49
51 struct { 50 struct {
52 struct wl_signal destroy; 51 struct wl_signal disable;
53 } events; 52 } events;
54 53
55 struct timespec last_presentation; 54 struct timespec last_presentation;
@@ -72,8 +71,8 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
72void output_add_workspace(struct sway_output *output, 71void output_add_workspace(struct sway_output *output,
73 struct sway_workspace *workspace); 72 struct sway_workspace *workspace);
74 73
75typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, 74typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
76 struct wlr_surface *surface, struct wlr_box *box, float rotation, 75 struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box,
77 void *user_data); 76 void *user_data);
78 77
79void output_damage_whole(struct sway_output *output); 78void output_damage_whole(struct sway_output *output);
diff --git a/include/sway/server.h b/include/sway/server.h
index 0f5e3ab2..4cce17cc 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -4,16 +4,19 @@
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/backend.h> 5#include <wlr/backend.h>
6#include <wlr/backend/session.h> 6#include <wlr/backend/session.h>
7#include <wlr/render/allocator.h>
7#include <wlr/render/wlr_renderer.h> 8#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_compositor.h> 9#include <wlr/types/wlr_compositor.h>
9#include <wlr/types/wlr_data_device.h> 10#include <wlr/types/wlr_data_device.h>
10#include <wlr/types/wlr_input_method_v2.h> 11#include <wlr/types/wlr_input_method_v2.h>
11#include <wlr/types/wlr_foreign_toplevel_management_v1.h> 12#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
13#include <wlr/types/wlr_drm_lease_v1.h>
12#include <wlr/types/wlr_layer_shell_v1.h> 14#include <wlr/types/wlr_layer_shell_v1.h>
13#include <wlr/types/wlr_output_management_v1.h> 15#include <wlr/types/wlr_output_management_v1.h>
14#include <wlr/types/wlr_output_power_management_v1.h> 16#include <wlr/types/wlr_output_power_management_v1.h>
15#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
16#include <wlr/types/wlr_relative_pointer_v1.h> 18#include <wlr/types/wlr_relative_pointer_v1.h>
19#include <wlr/types/wlr_session_lock_v1.h>
17#include <wlr/types/wlr_server_decoration.h> 20#include <wlr/types/wlr_server_decoration.h>
18#include <wlr/types/wlr_text_input_v3.h> 21#include <wlr/types/wlr_text_input_v3.h>
19#include <wlr/types/wlr_xdg_shell.h> 22#include <wlr/types/wlr_xdg_shell.h>
@@ -23,19 +26,24 @@
23#include "sway/xwayland.h" 26#include "sway/xwayland.h"
24#endif 27#endif
25 28
29struct sway_transaction;
30
26struct sway_server { 31struct sway_server {
27 struct wl_display *wl_display; 32 struct wl_display *wl_display;
28 struct wl_event_loop *wl_event_loop; 33 struct wl_event_loop *wl_event_loop;
29 const char *socket; 34 const char *socket;
30 35
31 struct wlr_backend *backend; 36 struct wlr_backend *backend;
32 struct wlr_backend *noop_backend;
33 // secondary headless backend used for creating virtual outputs on-the-fly 37 // secondary headless backend used for creating virtual outputs on-the-fly
34 struct wlr_backend *headless_backend; 38 struct wlr_backend *headless_backend;
39 struct wlr_renderer *renderer;
40 struct wlr_allocator *allocator;
35 41
36 struct wlr_compositor *compositor; 42 struct wlr_compositor *compositor;
37 struct wl_listener compositor_new_surface; 43 struct wl_listener compositor_new_surface;
38 44
45 struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
46
39 struct wlr_data_device_manager *data_device_manager; 47 struct wlr_data_device_manager *data_device_manager;
40 48
41 struct sway_input_manager *input; 49 struct sway_input_manager *input;
@@ -70,6 +78,9 @@ struct sway_server {
70 struct wl_listener xdg_decoration; 78 struct wl_listener xdg_decoration;
71 struct wl_list xdg_decorations; // sway_xdg_decoration::link 79 struct wl_list xdg_decorations; // sway_xdg_decoration::link
72 80
81 struct wlr_drm_lease_v1_manager *drm_lease_manager;
82 struct wl_listener drm_lease_request;
83
73 struct wlr_presentation *presentation; 84 struct wlr_presentation *presentation;
74 85
75 struct wlr_pointer_constraints_v1 *pointer_constraints; 86 struct wlr_pointer_constraints_v1 *pointer_constraints;
@@ -79,14 +90,44 @@ struct sway_server {
79 struct wl_listener output_manager_apply; 90 struct wl_listener output_manager_apply;
80 struct wl_listener output_manager_test; 91 struct wl_listener output_manager_test;
81 92
93 struct {
94 bool locked;
95 struct wlr_session_lock_manager_v1 *manager;
96
97 struct wlr_session_lock_v1 *lock;
98 struct wl_listener lock_new_surface;
99 struct wl_listener lock_unlock;
100 struct wl_listener lock_destroy;
101
102 struct wl_listener new_lock;
103 struct wl_listener manager_destroy;
104 } session_lock;
105
82 struct wlr_output_power_manager_v1 *output_power_manager_v1; 106 struct wlr_output_power_manager_v1 *output_power_manager_v1;
83 struct wl_listener output_power_manager_set_mode; 107 struct wl_listener output_power_manager_set_mode;
84 struct wlr_input_method_manager_v2 *input_method; 108 struct wlr_input_method_manager_v2 *input_method;
85 struct wlr_text_input_manager_v3 *text_input; 109 struct wlr_text_input_manager_v3 *text_input;
86 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 110 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
87 111
112 struct wlr_xdg_activation_v1 *xdg_activation_v1;
113 struct wl_listener xdg_activation_v1_request_activate;
114
115 // The timeout for transactions, after which a transaction is applied
116 // regardless of readiness.
88 size_t txn_timeout_ms; 117 size_t txn_timeout_ms;
89 list_t *transactions; 118
119 // Stores a transaction after it has been committed, but is waiting for
120 // views to ack the new dimensions before being applied. A queued
121 // transaction is frozen and must not have new instructions added to it.
122 struct sway_transaction *queued_transaction;
123
124 // Stores a pending transaction that will be committed once the existing
125 // queued transaction is applied and freed. The pending transaction can be
126 // updated with new instructions as needed.
127 struct sway_transaction *pending_transaction;
128
129 // Stores the nodes that have been marked as "dirty" and will be put into
130 // the pending transaction.
90 list_t *dirty_nodes; 131 list_t *dirty_nodes;
91}; 132};
92 133
@@ -96,6 +137,7 @@ struct sway_debug {
96 bool noatomic; // Ignore atomic layout updates 137 bool noatomic; // Ignore atomic layout updates
97 bool txn_timings; // Log verbose messages about transactions 138 bool txn_timings; // Log verbose messages about transactions
98 bool txn_wait; // Always wait for the timeout before applying 139 bool txn_wait; // Always wait for the timeout before applying
140 bool noscanout; // Disable direct scan-out
99 141
100 enum { 142 enum {
101 DAMAGE_DEFAULT, // Default behaviour 143 DAMAGE_DEFAULT, // Default behaviour
@@ -106,18 +148,19 @@ struct sway_debug {
106 148
107extern struct sway_debug debug; 149extern struct sway_debug debug;
108 150
109/* Prepares an unprivileged server_init by performing all privileged operations in advance */
110bool server_privileged_prepare(struct sway_server *server);
111bool server_init(struct sway_server *server); 151bool server_init(struct sway_server *server);
112void server_fini(struct sway_server *server); 152void server_fini(struct sway_server *server);
113bool server_start(struct sway_server *server); 153bool server_start(struct sway_server *server);
114void server_run(struct sway_server *server); 154void server_run(struct sway_server *server);
115 155
156void restore_nofile_limit(void);
157
116void handle_compositor_new_surface(struct wl_listener *listener, void *data); 158void handle_compositor_new_surface(struct wl_listener *listener, void *data);
117void handle_new_output(struct wl_listener *listener, void *data); 159void handle_new_output(struct wl_listener *listener, void *data);
118 160
119void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); 161void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
120void handle_layer_shell_surface(struct wl_listener *listener, void *data); 162void handle_layer_shell_surface(struct wl_listener *listener, void *data);
163void sway_session_lock_init(void);
121void handle_xdg_shell_surface(struct wl_listener *listener, void *data); 164void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
122#if HAVE_XWAYLAND 165#if HAVE_XWAYLAND
123void handle_xwayland_surface(struct wl_listener *listener, void *data); 166void handle_xwayland_surface(struct wl_listener *listener, void *data);
@@ -125,5 +168,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data);
125void handle_server_decoration(struct wl_listener *listener, void *data); 168void handle_server_decoration(struct wl_listener *listener, void *data);
126void handle_xdg_decoration(struct wl_listener *listener, void *data); 169void handle_xdg_decoration(struct wl_listener *listener, void *data);
127void handle_pointer_constraint(struct wl_listener *listener, void *data); 170void handle_pointer_constraint(struct wl_listener *listener, void *data);
171void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
172 void *data);
173
174void set_rr_scheduling(void);
128 175
129#endif 176#endif
diff --git a/include/sway/surface.h b/include/sway/surface.h
index 4da96c02..fb1cd775 100644
--- a/include/sway/surface.h
+++ b/include/sway/surface.h
@@ -1,6 +1,6 @@
1#ifndef _SWAY_SURFACE_H 1#ifndef _SWAY_SURFACE_H
2#define _SWAY_SURFACE_H 2#define _SWAY_SURFACE_H
3#include <wlr/types/wlr_surface.h> 3#include <wlr/types/wlr_compositor.h>
4 4
5struct sway_surface { 5struct sway_surface {
6 struct wlr_surface *wlr_surface; 6 struct wlr_surface *wlr_surface;
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 7e9df59f..751612e2 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -2,8 +2,7 @@
2#define _SWAY_CONTAINER_H 2#define _SWAY_CONTAINER_H
3#include <stdint.h> 3#include <stdint.h>
4#include <sys/types.h> 4#include <sys/types.h>
5#include <wlr/types/wlr_box.h> 5#include <wlr/types/wlr_compositor.h>
6#include <wlr/types/wlr_surface.h>
7#include "list.h" 6#include "list.h"
8#include "sway/tree/node.h" 7#include "sway/tree/node.h"
9 8
@@ -46,9 +45,9 @@ struct sway_container_state {
46 45
47 enum sway_fullscreen_mode fullscreen_mode; 46 enum sway_fullscreen_mode fullscreen_mode;
48 47
49 struct sway_workspace *workspace; 48 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
50 struct sway_container *parent; 49 struct sway_container *parent; // NULL if container in root of workspace
51 list_t *children; 50 list_t *children; // struct sway_container
52 51
53 struct sway_container *focused_inactive_child; 52 struct sway_container *focused_inactive_child;
54 bool focused; 53 bool focused;
@@ -60,6 +59,7 @@ struct sway_container_state {
60 bool border_left; 59 bool border_left;
61 bool border_right; 60 bool border_right;
62 61
62 // These are in layout coordinates.
63 double content_x, content_y; 63 double content_x, content_y;
64 double content_width, content_height; 64 double content_width, content_height;
65}; 65};
@@ -68,14 +68,12 @@ struct sway_container {
68 struct sway_node node; 68 struct sway_node node;
69 struct sway_view *view; 69 struct sway_view *view;
70 70
71 // The pending state is the main container properties, and the current state is in the below struct.
72 // This means most places of the code can refer to the main variables (pending state) and it'll just work.
73 struct sway_container_state current; 71 struct sway_container_state current;
72 struct sway_container_state pending;
74 73
75 char *title; // The view's title (unformatted) 74 char *title; // The view's title (unformatted)
76 char *formatted_title; // The title displayed in the title bar 75 char *formatted_title; // The title displayed in the title bar
77 76
78 enum sway_container_layout layout;
79 enum sway_container_layout prev_split_layout; 77 enum sway_container_layout prev_split_layout;
80 78
81 // Whether stickiness has been enabled on this container. Use 79 // Whether stickiness has been enabled on this container. Use
@@ -86,11 +84,13 @@ struct sway_container {
86 // For C_ROOT, this has no meaning 84 // For C_ROOT, this has no meaning
87 // For other types, this is the position in layout coordinates 85 // For other types, this is the position in layout coordinates
88 // Includes borders 86 // Includes borders
89 double x, y;
90 double width, height;
91 double saved_x, saved_y; 87 double saved_x, saved_y;
92 double saved_width, saved_height; 88 double saved_width, saved_height;
93 89
90 // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD
91 // border which we use to restore when the view returns to SSD.
92 enum sway_container_border saved_border;
93
94 // The share of the space of parent container this container occupies 94 // The share of the space of parent container this container occupies
95 double width_fraction; 95 double width_fraction;
96 double height_fraction; 96 double height_fraction;
@@ -100,33 +100,11 @@ struct sway_container {
100 double child_total_width; 100 double child_total_width;
101 double child_total_height; 101 double child_total_height;
102 102
103 // These are in layout coordinates.
104 double content_x, content_y;
105 int content_width, content_height;
106
107 // In most cases this is the same as the content x and y, but if the view 103 // In most cases this is the same as the content x and y, but if the view
108 // refuses to resize to the content dimensions then it can be smaller. 104 // refuses to resize to the content dimensions then it can be smaller.
109 // These are in layout coordinates. 105 // These are in layout coordinates.
110 double surface_x, surface_y; 106 double surface_x, surface_y;
111 107
112 enum sway_fullscreen_mode fullscreen_mode;
113
114 enum sway_container_border border;
115
116 // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD
117 // border which we use to restore when the view returns to SSD.
118 enum sway_container_border saved_border;
119
120 int border_thickness;
121 bool border_top;
122 bool border_bottom;
123 bool border_left;
124 bool border_right;
125
126 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
127 struct sway_container *parent; // NULL if container in root of workspace
128 list_t *children; // struct sway_container
129
130 // Outputs currently being intersected 108 // Outputs currently being intersected
131 list_t *outputs; // struct sway_output 109 list_t *outputs; // struct sway_output
132 110
@@ -139,14 +117,14 @@ struct sway_container {
139 117
140 struct wlr_texture *title_focused; 118 struct wlr_texture *title_focused;
141 struct wlr_texture *title_focused_inactive; 119 struct wlr_texture *title_focused_inactive;
120 struct wlr_texture *title_focused_tab_title;
142 struct wlr_texture *title_unfocused; 121 struct wlr_texture *title_unfocused;
143 struct wlr_texture *title_urgent; 122 struct wlr_texture *title_urgent;
144 size_t title_height;
145 size_t title_baseline;
146 123
147 list_t *marks; // char * 124 list_t *marks; // char *
148 struct wlr_texture *marks_focused; 125 struct wlr_texture *marks_focused;
149 struct wlr_texture *marks_focused_inactive; 126 struct wlr_texture *marks_focused_inactive;
127 struct wlr_texture *marks_focused_tab_title;
150 struct wlr_texture *marks_unfocused; 128 struct wlr_texture *marks_unfocused;
151 struct wlr_texture *marks_urgent; 129 struct wlr_texture *marks_urgent;
152 130
@@ -185,6 +163,11 @@ void container_for_each_child(struct sway_container *container,
185 void (*f)(struct sway_container *container, void *data), void *data); 163 void (*f)(struct sway_container *container, void *data), void *data);
186 164
187/** 165/**
166 * Returns the fullscreen container obstructing this container if it exists.
167 */
168struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container);
169
170/**
188 * Returns true if the given container is an ancestor of this container. 171 * Returns true if the given container is an ancestor of this container.
189 */ 172 */
190bool container_has_ancestor(struct sway_container *container, 173bool container_has_ancestor(struct sway_container *container,
@@ -200,11 +183,6 @@ struct sway_container *container_flatten(struct sway_container *container);
200 183
201void container_update_title_textures(struct sway_container *container); 184void container_update_title_textures(struct sway_container *container);
202 185
203/**
204 * Calculate the container's title_height property.
205 */
206void container_calculate_title_height(struct sway_container *container);
207
208size_t container_build_representation(enum sway_container_layout layout, 186size_t container_build_representation(enum sway_container_layout layout,
209 list_t *children, char *buffer); 187 list_t *children, char *buffer);
210 188
@@ -231,10 +209,17 @@ void container_set_geometry_from_content(struct sway_container *con);
231/** 209/**
232 * Determine if the given container is itself floating. 210 * Determine if the given container is itself floating.
233 * This will return false for any descendants of a floating container. 211 * This will return false for any descendants of a floating container.
212 *
213 * Uses pending container state.
234 */ 214 */
235bool container_is_floating(struct sway_container *container); 215bool container_is_floating(struct sway_container *container);
236 216
237/** 217/**
218 * Same as above, but for current container state.
219 */
220bool container_is_current_floating(struct sway_container *container);
221
222/**
238 * Get a container's box in layout coordinates. 223 * Get a container's box in layout coordinates.
239 */ 224 */
240void container_get_box(struct sway_container *container, struct wlr_box *box); 225void container_get_box(struct sway_container *container, struct wlr_box *box);
@@ -299,6 +284,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container);
299/** 284/**
300 * Return the output which will be used for scale purposes. 285 * Return the output which will be used for scale purposes.
301 * This is the most recently entered output. 286 * This is the most recently entered output.
287 * If the container is not on any output, return NULL.
302 */ 288 */
303struct sway_output *container_get_effective_output(struct sway_container *con); 289struct sway_output *container_get_effective_output(struct sway_container *con);
304 290
@@ -378,7 +364,7 @@ bool container_is_sticky_or_child(struct sway_container *con);
378 * This will destroy pairs of redundant H/V splits 364 * This will destroy pairs of redundant H/V splits
379 * e.g. H[V[H[app app]] app] -> H[app app app] 365 * e.g. H[V[H[app app]] app] -> H[app app app]
380 * The middle "V[H[" are eliminated by a call to container_squash 366 * The middle "V[H[" are eliminated by a call to container_squash
381 * on the V[ con. It's grandchildren are added to it's parent. 367 * on the V[ con. It's grandchildren are added to its parent.
382 * 368 *
383 * This function is roughly equivalent to i3's tree_flatten here: 369 * This function is roughly equivalent to i3's tree_flatten here:
384 * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 370 * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index e8f4d573..5d4a2f2d 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -31,7 +31,7 @@ struct sway_root {
31 list_t *scratchpad; // struct sway_container 31 list_t *scratchpad; // struct sway_container
32 32
33 // For when there's no connected outputs 33 // For when there's no connected outputs
34 struct sway_output *noop_output; 34 struct sway_output *fallback_output;
35 35
36 struct sway_container *fullscreen_global; 36 struct sway_container *fullscreen_global;
37 37
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index e071e6c9..0dcbf1aa 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -1,7 +1,7 @@
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 "config.h"
6#if HAVE_XWAYLAND 6#if HAVE_XWAYLAND
7#include <wlr/xwayland.h> 7#include <wlr/xwayland.h>
@@ -108,11 +108,10 @@ struct sway_view {
108 list_t *executed_criteria; // struct criteria * 108 list_t *executed_criteria; // struct criteria *
109 109
110 union { 110 union {
111 struct wlr_xdg_surface *wlr_xdg_surface; 111 struct wlr_xdg_toplevel *wlr_xdg_toplevel;
112#if HAVE_XWAYLAND 112#if HAVE_XWAYLAND
113 struct wlr_xwayland_surface *wlr_xwayland_surface; 113 struct wlr_xwayland_surface *wlr_xwayland_surface;
114#endif 114#endif
115 struct wlr_wl_shell_surface *wlr_wl_shell_surface;
116 }; 115 };
117 116
118 struct { 117 struct {
@@ -171,6 +170,7 @@ struct sway_xwayland_unmanaged {
171 170
172 int lx, ly; 171 int lx, ly;
173 172
173 struct wl_listener request_activate;
174 struct wl_listener request_configure; 174 struct wl_listener request_configure;
175 struct wl_listener request_fullscreen; 175 struct wl_listener request_fullscreen;
176 struct wl_listener commit; 176 struct wl_listener commit;
@@ -184,7 +184,7 @@ struct sway_xwayland_unmanaged {
184struct sway_view_child; 184struct sway_view_child;
185 185
186struct sway_view_child_impl { 186struct sway_view_child_impl {
187 void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy); 187 void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy);
188 void (*destroy)(struct sway_view_child *child); 188 void (*destroy)(struct sway_view_child *child);
189}; 189};
190 190
@@ -218,7 +218,7 @@ struct sway_subsurface {
218struct sway_xdg_popup { 218struct sway_xdg_popup {
219 struct sway_view_child child; 219 struct sway_view_child child;
220 220
221 struct wlr_xdg_surface *wlr_xdg_surface; 221 struct wlr_xdg_popup *wlr_xdg_popup;
222 222
223 struct wl_listener new_popup; 223 struct wl_listener new_popup;
224 struct wl_listener destroy; 224 struct wl_listener destroy;
@@ -311,12 +311,22 @@ void view_destroy(struct sway_view *view);
311 311
312void view_begin_destroy(struct sway_view *view); 312void view_begin_destroy(struct sway_view *view);
313 313
314/**
315 * Map a view, ie. make it visible in the tree.
316 *
317 * `fullscreen` should be set to true (and optionally `fullscreen_output`
318 * should be populated) if the view should be made fullscreen immediately.
319 *
320 * `decoration` should be set to true if the client prefers CSD. The client's
321 * preference may be ignored.
322 */
314void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, 323void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
315 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); 324 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
316 325
317void view_unmap(struct sway_view *view); 326void view_unmap(struct sway_view *view);
318 327
319void view_update_size(struct sway_view *view, int width, int height); 328void view_update_size(struct sway_view *view);
329void view_center_surface(struct sway_view *view);
320 330
321void view_child_init(struct sway_view_child *child, 331void view_child_init(struct sway_view_child *child,
322 const struct sway_view_child_impl *impl, struct sway_view *view, 332 const struct sway_view_child_impl *impl, struct sway_view *view,
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index fdd92f64..b3d93a81 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -60,20 +60,20 @@ void workspace_consider_destroy(struct sway_workspace *ws);
60 60
61char *workspace_next_name(const char *output_name); 61char *workspace_next_name(const char *output_name);
62 62
63bool workspace_switch(struct sway_workspace *workspace, 63struct sway_workspace *workspace_auto_back_and_forth(
64 bool no_auto_back_and_forth); 64 struct sway_workspace *workspace);
65
66bool workspace_switch(struct sway_workspace *workspace);
65 67
66struct sway_workspace *workspace_by_number(const char* name); 68struct sway_workspace *workspace_by_number(const char* name);
67 69
68struct sway_workspace *workspace_by_name(const char*); 70struct sway_workspace *workspace_by_name(const char*);
69 71
70struct sway_workspace *workspace_output_next( 72struct sway_workspace *workspace_output_next(struct sway_workspace *current);
71 struct sway_workspace *current, bool create);
72 73
73struct sway_workspace *workspace_next(struct sway_workspace *current); 74struct sway_workspace *workspace_next(struct sway_workspace *current);
74 75
75struct sway_workspace *workspace_output_prev( 76struct sway_workspace *workspace_output_prev(struct sway_workspace *current);
76 struct sway_workspace *current, bool create);
77 77
78struct sway_workspace *workspace_prev(struct sway_workspace *current); 78struct sway_workspace *workspace_prev(struct sway_workspace *current);
79 79
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 545a66a8..3ad0bdf3 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -58,7 +58,6 @@ struct swaybar_output {
58 struct zxdg_output_v1 *xdg_output; 58 struct zxdg_output_v1 *xdg_output;
59 struct wl_surface *surface; 59 struct wl_surface *surface;
60 struct zwlr_layer_surface_v1 *layer_surface; 60 struct zwlr_layer_surface_v1 *layer_surface;
61 struct wl_region *input_region;
62 uint32_t wl_name; 61 uint32_t wl_name;
63 62
64 struct wl_list workspaces; // swaybar_workspace::link 63 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..1aec6d6c 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;
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h
index 9e39e716..2d68b6c9 100644
--- a/include/swaynag/swaynag.h
+++ b/include/swaynag/swaynag.h
@@ -5,7 +5,6 @@
5#include "list.h" 5#include "list.h"
6#include "pool-buffer.h" 6#include "pool-buffer.h"
7#include "swaynag/types.h" 7#include "swaynag/types.h"
8#include "xdg-output-unstable-v1-client-protocol.h"
9 8
10#define SWAYNAG_MAX_HEIGHT 500 9#define SWAYNAG_MAX_HEIGHT 500
11 10
@@ -68,20 +67,18 @@ struct swaynag_details {
68 int offset; 67 int offset;
69 int visible_lines; 68 int visible_lines;
70 int total_lines; 69 int total_lines;
71 struct swaynag_button *button_details; 70 struct swaynag_button button_details;
72 struct swaynag_button button_up; 71 struct swaynag_button button_up;
73 struct swaynag_button button_down; 72 struct swaynag_button button_down;
74}; 73};
75 74
76struct swaynag { 75struct swaynag {
77 bool run_display; 76 bool run_display;
78 int querying_outputs;
79 77
80 struct wl_display *display; 78 struct wl_display *display;
81 struct wl_compositor *compositor; 79 struct wl_compositor *compositor;
82 struct wl_seat *seat; 80 struct wl_seat *seat;
83 struct wl_shm *shm; 81 struct wl_shm *shm;
84 struct zxdg_output_manager_v1 *xdg_output_manager;
85 struct wl_list outputs; // swaynag_output::link 82 struct wl_list outputs; // swaynag_output::link
86 struct wl_list seats; // swaynag_seat::link 83 struct wl_list seats; // swaynag_seat::link
87 struct swaynag_output *output; 84 struct swaynag_output *output;
diff --git a/include/swaynag/types.h b/include/swaynag/types.h
index 24da9418..18f218e0 100644
--- a/include/swaynag/types.h
+++ b/include/swaynag/types.h
@@ -4,9 +4,11 @@
4struct swaynag_type { 4struct swaynag_type {
5 char *name; 5 char *name;
6 6
7 char *font; 7 char *font; // Used for debugging.
8 PangoFontDescription *font_description;
8 char *output; 9 char *output;
9 uint32_t anchors; 10 uint32_t anchors;
11 int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset
10 12
11 // Colors 13 // Colors
12 uint32_t button_text; 14 uint32_t button_text;
diff --git a/include/util.h b/include/util.h
index c80da1cb..f887d489 100644
--- a/include/util.h
+++ b/include/util.h
@@ -30,12 +30,6 @@ int parse_movement_amount(int argc, char **argv,
30 struct movement_amount *amount); 30 struct movement_amount *amount);
31 31
32/** 32/**
33 * Get the current time, in milliseconds.
34 */
35
36uint32_t get_current_time_msec(void);
37
38/**
39 * Wrap i into the range [0, max] 33 * Wrap i into the range [0, max]
40 */ 34 */
41int wrap(int i, int max); 35int wrap(int i, int max);
diff --git a/meson.build b/meson.build
index 38a55678..d23300be 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,9 @@
1project( 1project(
2 'sway', 2 'sway',
3 'c', 3 'c',
4 version: '1.5', #release_version 4 version: '1.8-dev',
5 license: 'MIT', 5 license: 'MIT',
6 meson_version: '>=0.53.0', 6 meson_version: '>=0.60.0',
7 default_options: [ 7 default_options: [
8 'c_std=c11', 8 'c_std=c11',
9 'warning_level=2', 9 'warning_level=2',
@@ -35,56 +35,51 @@ if is_freebsd
35 add_project_arguments('-D_C11_SOURCE', language: 'c') 35 add_project_arguments('-D_C11_SOURCE', language: 'c')
36endif 36endif
37 37
38jsonc = dependency('json-c', version: '>=0.13') 38# Execute the wlroots subproject, if any
39pcre = dependency('libpcre') 39wlroots_version = ['>=0.16.0', '<0.17.0']
40wayland_server = dependency('wayland-server') 40subproject(
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'))
55bash_comp = dependency('bash-completion', required: false)
56fish_comp = dependency('fish', required: false)
57math = cc.find_library('m')
58rt = cc.find_library('rt')
59
60# Try first to find wlroots as a subproject, then as a system dependency
61wlroots_version = ['>=0.12.0', '<0.13.0']
62wlroots_proj = subproject(
63 'wlroots', 41 'wlroots',
64 default_options: ['examples=false'], 42 default_options: ['examples=false'],
65 required: false, 43 required: false,
66 version: wlroots_version, 44 version: wlroots_version,
67) 45)
46
47jsonc = dependency('json-c', version: '>=0.13')
48pcre2 = dependency('libpcre2-8')
49wayland_server = dependency('wayland-server', version: '>=1.20.0')
50wayland_client = dependency('wayland-client')
51wayland_cursor = dependency('wayland-cursor')
52wayland_egl = dependency('wayland-egl')
53wayland_protos = dependency('wayland-protocols', version: '>=1.24')
54wlroots = dependency('wlroots', version: wlroots_version)
55xkbcommon = dependency('xkbcommon')
56cairo = dependency('cairo')
57pango = dependency('pango')
58pangocairo = dependency('pangocairo')
59gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
60pixman = dependency('pixman-1')
61glesv2 = dependency('glesv2')
62libevdev = dependency('libevdev')
63libinput = dependency('libinput', version: '>=1.6.0')
64xcb = dependency('xcb', required: get_option('xwayland'))
65drm_full = dependency('libdrm') # only needed for drm_fourcc.h
66drm = drm_full.partial_dependency(compile_args: true, includes: true)
67libudev = dependency('libudev')
68bash_comp = dependency('bash-completion', required: false)
69fish_comp = dependency('fish', required: false)
70math = cc.find_library('m')
71rt = cc.find_library('rt')
72xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
73threads = dependency('threads') # for pthread_setschedparam
74
68wlroots_features = { 75wlroots_features = {
69 'xwayland': false, 76 'xwayland': false,
70 'systemd': false,
71 'elogind': false,
72 'libseat': false,
73} 77}
74if wlroots_proj.found() 78foreach name, _ : wlroots_features
75 wlroots = wlroots_proj.get_variable('wlroots') 79 var_name = 'have_' + name.underscorify()
76 wlroots_conf = wlroots_proj.get_variable('conf_data') 80 have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
77 foreach name, _ : wlroots_features 81 wlroots_features += { name: have }
78 has = wlroots_conf.get('WLR_HAS_' + name.to_upper()) == 1 82endforeach
79 wlroots_features += { name: has }
80 endforeach
81else
82 wlroots = dependency('wlroots', version: wlroots_version)
83 foreach name, _ : wlroots_features
84 has = cc.get_define('WLR_HAS_' + name.to_upper(), prefix: '#include <wlr/config.h>', dependencies: wlroots) == '1'
85 wlroots_features += { name: has }
86 endforeach
87endif
88 83
89if get_option('xwayland').enabled() and not wlroots_features['xwayland'] 84if get_option('xwayland').enabled() and not wlroots_features['xwayland']
90 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') 85 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
@@ -95,19 +90,11 @@ if get_option('sd-bus-provider') == 'auto'
95 if not get_option('tray').disabled() 90 if not get_option('tray').disabled()
96 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') 91 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto')
97 endif 92 endif
98 sdbus = dependency('libsystemd', 93 sdbus = dependency(['libsystemd', 'libelogind'],
99 required: false, 94 required: false,
100 version: '>=239', 95 version: '>=239',
101 not_found_message: 'libsystemd not found, trying libelogind',
102 ) 96 )
103 if not sdbus.found() 97 if not sdbus.found()
104 sdbus = dependency('libelogind',
105 required: false,
106 version: '>=239',
107 not_found_message: 'libelogind not found, trying basu',
108 )
109 endif
110 if not sdbus.found()
111 sdbus = dependency('basu', required: false) 98 sdbus = dependency('basu', required: false)
112 endif 99 endif
113else 100else
@@ -131,8 +118,7 @@ conf_data.set10('HAVE_TRAY', have_tray)
131 118
132scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) 119scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
133if scdoc.found() 120if scdoc.found()
134 scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) 121 scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true)
135 sh = find_program('sh', native: true)
136 mandir = get_option('mandir') 122 mandir = get_option('mandir')
137 man_files = [ 123 man_files = [
138 'sway/sway.1.scd', 124 'sway/sway.1.scd',
@@ -143,9 +129,15 @@ if scdoc.found()
143 'sway/sway-output.5.scd', 129 'sway/sway-output.5.scd',
144 'swaybar/swaybar-protocol.7.scd', 130 'swaybar/swaybar-protocol.7.scd',
145 'swaymsg/swaymsg.1.scd', 131 'swaymsg/swaymsg.1.scd',
146 'swaynag/swaynag.1.scd',
147 'swaynag/swaynag.5.scd',
148 ] 132 ]
133
134 if get_option('swaynag')
135 man_files += [
136 'swaynag/swaynag.1.scd',
137 'swaynag/swaynag.5.scd',
138 ]
139 endif
140
149 foreach filename : man_files 141 foreach filename : man_files
150 topic = filename.split('.')[-3].split('/')[-1] 142 topic = filename.split('.')[-3].split('/')[-1]
151 section = filename.split('.')[-2] 143 section = filename.split('.')[-2]
@@ -155,10 +147,10 @@ if scdoc.found()
155 output, 147 output,
156 input: filename, 148 input: filename,
157 output: output, 149 output: output,
158 command: [ 150 command: scdoc_prog,
159 sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
160 ],
161 install: true, 151 install: true,
152 feed: true,
153 capture: true,
162 install_dir: '@0@/man@1@'.format(mandir, section) 154 install_dir: '@0@/man@1@'.format(mandir, section)
163 ) 155 )
164 endforeach 156 endforeach
@@ -169,8 +161,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir
169version = '"@0@"'.format(meson.project_version()) 161version = '"@0@"'.format(meson.project_version())
170git = find_program('git', native: true, required: false) 162git = find_program('git', native: true, required: false)
171if git.found() 163if git.found()
172 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) 164 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
173 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) 165 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
174 if git_commit.returncode() == 0 and git_branch.returncode() == 0 166 if git_commit.returncode() == 0 and git_branch.returncode() == 0
175 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( 167 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format(
176 meson.project_version(), 168 meson.project_version(),
@@ -183,7 +175,7 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
183 175
184# Compute the relative path used by compiler invocations. 176# Compute the relative path used by compiler invocations.
185source_root = meson.current_source_dir().split('/') 177source_root = meson.current_source_dir().split('/')
186build_root = meson.build_root().split('/') 178build_root = meson.global_build_root().split('/')
187relative_dir_parts = [] 179relative_dir_parts = []
188i = 0 180i = 0
189in_prefix = true 181in_prefix = true
@@ -227,9 +219,15 @@ subdir('common')
227subdir('sway') 219subdir('sway')
228subdir('swaymsg') 220subdir('swaymsg')
229 221
230subdir('client') 222if get_option('swaybar') or get_option('swaynag')
231subdir('swaybar') 223 subdir('client')
232subdir('swaynag') 224endif
225if get_option('swaybar')
226 subdir('swaybar')
227endif
228if get_option('swaynag')
229 subdir('swaynag')
230endif
233 231
234config = configuration_data() 232config = configuration_data()
235config.set('datadir', join_paths(prefix, datadir)) 233config.set('datadir', join_paths(prefix, datadir))
@@ -277,13 +275,17 @@ endif
277if get_option('bash-completions') 275if get_option('bash-completions')
278 bash_files = files( 276 bash_files = files(
279 'completions/bash/sway', 277 'completions/bash/sway',
280 'completions/bash/swaybar',
281 'completions/bash/swaymsg', 278 'completions/bash/swaymsg',
282 ) 279 )
280
281 if get_option('swaybar')
282 bash_files += files('completions/bash/swaybar')
283 endif
284
283 if bash_comp.found() 285 if bash_comp.found()
284 bash_install_dir = bash_comp.get_pkgconfig_variable( 286 bash_install_dir = bash_comp.get_variable(
285 'completionsdir', 287 pkgconfig: 'completionsdir',
286 define_variable: ['datadir', datadir] 288 pkgconfig_define: ['datadir', datadir]
287 ) 289 )
288 else 290 else
289 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') 291 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
@@ -296,12 +298,16 @@ if get_option('fish-completions')
296 fish_files = files( 298 fish_files = files(
297 'completions/fish/sway.fish', 299 'completions/fish/sway.fish',
298 'completions/fish/swaymsg.fish', 300 'completions/fish/swaymsg.fish',
299 'completions/fish/swaynag.fish',
300 ) 301 )
302
303 if get_option('swaynag')
304 fish_files += files('completions/fish/swaynag.fish')
305 endif
306
301 if fish_comp.found() 307 if fish_comp.found()
302 fish_install_dir = fish_comp.get_pkgconfig_variable( 308 fish_install_dir = fish_comp.get_variable(
303 'completionsdir', 309 pkgconfig: 'completionsdir',
304 define_variable: ['datadir', datadir] 310 pkgconfig_define: ['datadir', datadir]
305 ) 311 )
306 else 312 else
307 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') 313 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
@@ -313,12 +319,7 @@ endif
313summary({ 319summary({
314 'xwayland': have_xwayland, 320 'xwayland': have_xwayland,
315 'gdk-pixbuf': gdk_pixbuf.found(), 321 'gdk-pixbuf': gdk_pixbuf.found(),
316 'sd-bus': sdbus.found(),
317 'tray': have_tray, 322 'tray': have_tray,
318 'man-pages': scdoc.found(), 323 'man-pages': scdoc.found(),
319}, bool_yn: true) 324}, bool_yn: true)
320 325
321if not wlroots_features['systemd'] and not wlroots_features['elogind'] and not wlroots_features['libseat']
322 warning('The sway binary must be setuid when compiled without (e)logind or libseat')
323 warning('You must do this manually post-install: chmod a+s /path/to/sway')
324endif
diff --git a/meson_options.txt b/meson_options.txt
index e36900b6..6ba67554 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,6 +2,8 @@ option('default-wallpaper', type: 'boolean', value: true, description: 'Install
2option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') 2option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
3option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') 3option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')
4option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') 4option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.')
5option('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar')
6option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag')
5option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') 7option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
6option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') 8option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray')
7option('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 swaybg')
diff --git a/protocols/meson.build b/protocols/meson.build
index 124e9777..df24a4e5 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -1,9 +1,9 @@
1wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') 1wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
2 2
3wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) 3wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true)
4if wayland_scanner_dep.found() 4if wayland_scanner_dep.found()
5 wayland_scanner = find_program( 5 wayland_scanner = find_program(
6 wayland_scanner_dep.get_pkgconfig_variable('wayland_scanner'), 6 wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
7 native: true, 7 native: true,
8 ) 8 )
9else 9else
@@ -15,6 +15,7 @@ protocols = [
15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], 16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
17 [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], 17 [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'],
18 [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
18 ['wlr-layer-shell-unstable-v1.xml'], 19 ['wlr-layer-shell-unstable-v1.xml'],
19 ['idle.xml'], 20 ['idle.xml'],
20 ['wlr-input-inhibitor-unstable-v1.xml'], 21 ['wlr-input-inhibitor-unstable-v1.xml'],
diff --git a/sway/commands.c b/sway/commands.c
index fe1e98b5..2160a970 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -42,15 +42,17 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
42} 42}
43 43
44/* Keep alphabetized */ 44/* Keep alphabetized */
45static struct cmd_handler handlers[] = { 45static const struct cmd_handler handlers[] = {
46 { "assign", cmd_assign }, 46 { "assign", cmd_assign },
47 { "bar", cmd_bar }, 47 { "bar", cmd_bar },
48 { "bindcode", cmd_bindcode }, 48 { "bindcode", cmd_bindcode },
49 { "bindgesture", cmd_bindgesture },
49 { "bindswitch", cmd_bindswitch }, 50 { "bindswitch", cmd_bindswitch },
50 { "bindsym", cmd_bindsym }, 51 { "bindsym", cmd_bindsym },
51 { "client.background", cmd_client_noop }, 52 { "client.background", cmd_client_noop },
52 { "client.focused", cmd_client_focused }, 53 { "client.focused", cmd_client_focused },
53 { "client.focused_inactive", cmd_client_focused_inactive }, 54 { "client.focused_inactive", cmd_client_focused_inactive },
55 { "client.focused_tab_title", cmd_client_focused_tab_title },
54 { "client.placeholder", cmd_client_noop }, 56 { "client.placeholder", cmd_client_noop },
55 { "client.unfocused", cmd_client_unfocused }, 57 { "client.unfocused", cmd_client_unfocused },
56 { "client.urgent", cmd_client_urgent }, 58 { "client.urgent", cmd_client_urgent },
@@ -91,6 +93,7 @@ static struct cmd_handler handlers[] = {
91 { "titlebar_border_thickness", cmd_titlebar_border_thickness }, 93 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
92 { "titlebar_padding", cmd_titlebar_padding }, 94 { "titlebar_padding", cmd_titlebar_padding },
93 { "unbindcode", cmd_unbindcode }, 95 { "unbindcode", cmd_unbindcode },
96 { "unbindgesture", cmd_unbindgesture },
94 { "unbindswitch", cmd_unbindswitch }, 97 { "unbindswitch", cmd_unbindswitch },
95 { "unbindsym", cmd_unbindsym }, 98 { "unbindsym", cmd_unbindsym },
96 { "workspace", cmd_workspace }, 99 { "workspace", cmd_workspace },
@@ -98,7 +101,7 @@ static struct cmd_handler handlers[] = {
98}; 101};
99 102
100/* Config-time only commands. Keep alphabetized */ 103/* Config-time only commands. Keep alphabetized */
101static struct cmd_handler config_handlers[] = { 104static const struct cmd_handler config_handlers[] = {
102 { "default_orientation", cmd_default_orientation }, 105 { "default_orientation", cmd_default_orientation },
103 { "include", cmd_include }, 106 { "include", cmd_include },
104 { "swaybg_command", cmd_swaybg_command }, 107 { "swaybg_command", cmd_swaybg_command },
@@ -108,7 +111,7 @@ static struct cmd_handler config_handlers[] = {
108}; 111};
109 112
110/* Runtime-only commands. Keep alphabetized */ 113/* Runtime-only commands. Keep alphabetized */
111static struct cmd_handler command_handlers[] = { 114static const struct cmd_handler command_handlers[] = {
112 { "border", cmd_border }, 115 { "border", cmd_border },
113 { "create_output", cmd_create_output }, 116 { "create_output", cmd_create_output },
114 { "exit", cmd_exit }, 117 { "exit", cmd_exit },
@@ -144,22 +147,22 @@ static int handler_compare(const void *_a, const void *_b) {
144 return strcasecmp(a->command, b->command); 147 return strcasecmp(a->command, b->command);
145} 148}
146 149
147struct cmd_handler *find_handler(char *line, struct cmd_handler *handlers, 150const struct cmd_handler *find_handler(char *line,
148 size_t handlers_size) { 151 const struct cmd_handler *handlers, size_t handlers_size) {
149 if (!handlers || !handlers_size) { 152 if (!handlers || !handlers_size) {
150 return NULL; 153 return NULL;
151 } 154 }
152 struct cmd_handler query = { .command = line }; 155 const struct cmd_handler query = { .command = line };
153 return bsearch(&query, handlers, 156 return bsearch(&query, handlers,
154 handlers_size / sizeof(struct cmd_handler), 157 handlers_size / sizeof(struct cmd_handler),
155 sizeof(struct cmd_handler), handler_compare); 158 sizeof(struct cmd_handler), handler_compare);
156} 159}
157 160
158static struct cmd_handler *find_handler_ex(char *line, 161static const struct cmd_handler *find_handler_ex(char *line,
159 struct cmd_handler *config_handlers, size_t config_handlers_size, 162 const struct cmd_handler *config_handlers, size_t config_handlers_size,
160 struct cmd_handler *command_handlers, size_t command_handlers_size, 163 const struct cmd_handler *command_handlers, size_t command_handlers_size,
161 struct cmd_handler *handlers, size_t handlers_size) { 164 const struct cmd_handler *handlers, size_t handlers_size) {
162 struct cmd_handler *handler = NULL; 165 const struct cmd_handler *handler = NULL;
163 if (config->reading) { 166 if (config->reading) {
164 handler = find_handler(line, config_handlers, config_handlers_size); 167 handler = find_handler(line, config_handlers, config_handlers_size);
165 } else if (config->active) { 168 } else if (config->active) {
@@ -168,16 +171,17 @@ static struct cmd_handler *find_handler_ex(char *line,
168 return handler ? handler : find_handler(line, handlers, handlers_size); 171 return handler ? handler : find_handler(line, handlers, handlers_size);
169} 172}
170 173
171static struct cmd_handler *find_core_handler(char *line) { 174static const struct cmd_handler *find_core_handler(char *line) {
172 return find_handler_ex(line, config_handlers, sizeof(config_handlers), 175 return find_handler_ex(line, config_handlers, sizeof(config_handlers),
173 command_handlers, sizeof(command_handlers), 176 command_handlers, sizeof(command_handlers),
174 handlers, sizeof(handlers)); 177 handlers, sizeof(handlers));
175} 178}
176 179
177static void set_config_node(struct sway_node *node) { 180static void set_config_node(struct sway_node *node, bool node_overridden) {
178 config->handler_context.node = node; 181 config->handler_context.node = node;
179 config->handler_context.container = NULL; 182 config->handler_context.container = NULL;
180 config->handler_context.workspace = NULL; 183 config->handler_context.workspace = NULL;
184 config->handler_context.node_overridden = node_overridden;
181 185
182 if (node == NULL) { 186 if (node == NULL) {
183 return; 187 return;
@@ -186,7 +190,7 @@ static void set_config_node(struct sway_node *node) {
186 switch (node->type) { 190 switch (node->type) {
187 case N_CONTAINER: 191 case N_CONTAINER:
188 config->handler_context.container = node->sway_container; 192 config->handler_context.container = node->sway_container;
189 config->handler_context.workspace = node->sway_container->workspace; 193 config->handler_context.workspace = node->sway_container->pending.workspace;
190 break; 194 break;
191 case N_WORKSPACE: 195 case N_WORKSPACE:
192 config->handler_context.workspace = node->sway_workspace; 196 config->handler_context.workspace = node->sway_workspace;
@@ -202,6 +206,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
202 char *cmd; 206 char *cmd;
203 char matched_delim = ';'; 207 char matched_delim = ';';
204 list_t *containers = NULL; 208 list_t *containers = NULL;
209 bool using_criteria = false;
205 210
206 if (seat == NULL) { 211 if (seat == NULL) {
207 // passing a NULL seat means we just pick the default seat 212 // passing a NULL seat means we just pick the default seat
@@ -225,7 +230,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
225 for (; isspace(*head); ++head) {} 230 for (; isspace(*head); ++head) {}
226 // Extract criteria (valid for this command list only). 231 // Extract criteria (valid for this command list only).
227 if (matched_delim == ';') { 232 if (matched_delim == ';') {
228 config->handler_context.using_criteria = false; 233 using_criteria = false;
229 if (*head == '[') { 234 if (*head == '[') {
230 char *error = NULL; 235 char *error = NULL;
231 struct criteria *criteria = criteria_parse(head, &error); 236 struct criteria *criteria = criteria_parse(head, &error);
@@ -239,7 +244,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
239 containers = criteria_get_containers(criteria); 244 containers = criteria_get_containers(criteria);
240 head += strlen(criteria->raw); 245 head += strlen(criteria->raw);
241 criteria_destroy(criteria); 246 criteria_destroy(criteria);
242 config->handler_context.using_criteria = true; 247 using_criteria = true;
243 // Skip leading whitespace 248 // Skip leading whitespace
244 for (; isspace(*head); ++head) {} 249 for (; isspace(*head); ++head) {}
245 } 250 }
@@ -265,7 +270,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
265 } 270 }
266 } 271 }
267 } 272 }
268 struct cmd_handler *handler = find_core_handler(argv[0]); 273 const struct cmd_handler *handler = find_core_handler(argv[0]);
269 if (!handler) { 274 if (!handler) {
270 list_add(res_list, cmd_results_new(CMD_INVALID, 275 list_add(res_list, cmd_results_new(CMD_INVALID,
271 "Unknown/invalid command '%s'", argv[0])); 276 "Unknown/invalid command '%s'", argv[0]));
@@ -278,11 +283,14 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
278 argv[i] = do_var_replacement(argv[i]); 283 argv[i] = do_var_replacement(argv[i]);
279 } 284 }
280 285
281 if (!config->handler_context.using_criteria) { 286
282 // The container or workspace which this command will run on. 287 if (!using_criteria) {
283 struct sway_node *node = con ? &con->node : 288 if (con) {
284 seat_get_focus_inactive(seat, &root->node); 289 set_config_node(&con->node, true);
285 set_config_node(node); 290 } else {
291 set_config_node(seat_get_focus_inactive(seat, &root->node),
292 false);
293 }
286 struct cmd_results *res = handler->handle(argc-1, argv+1); 294 struct cmd_results *res = handler->handle(argc-1, argv+1);
287 list_add(res_list, res); 295 list_add(res_list, res);
288 if (res->status == CMD_INVALID) { 296 if (res->status == CMD_INVALID) {
@@ -296,7 +304,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
296 struct cmd_results *fail_res = NULL; 304 struct cmd_results *fail_res = NULL;
297 for (int i = 0; i < containers->length; ++i) { 305 for (int i = 0; i < containers->length; ++i) {
298 struct sway_container *container = containers->items[i]; 306 struct sway_container *container = containers->items[i];
299 set_config_node(&container->node); 307 set_config_node(&container->node, true);
300 struct cmd_results *res = handler->handle(argc-1, argv+1); 308 struct cmd_results *res = handler->handle(argc-1, argv+1);
301 if (res->status == CMD_SUCCESS) { 309 if (res->status == CMD_SUCCESS) {
302 free_cmd_results(res); 310 free_cmd_results(res);
@@ -370,7 +378,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
370 378
371 // Determine the command handler 379 // Determine the command handler
372 sway_log(SWAY_INFO, "Config command: %s", exec); 380 sway_log(SWAY_INFO, "Config command: %s", exec);
373 struct cmd_handler *handler = find_core_handler(argv[0]); 381 const struct cmd_handler *handler = find_core_handler(argv[0]);
374 if (!handler || !handler->handle) { 382 if (!handler || !handler->handle) {
375 const char *error = handler 383 const char *error = handler
376 ? "Command '%s' is shimmed, but unimplemented" 384 ? "Command '%s' is shimmed, but unimplemented"
@@ -401,6 +409,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
401 && handler->handle != cmd_bindsym 409 && handler->handle != cmd_bindsym
402 && handler->handle != cmd_bindcode 410 && handler->handle != cmd_bindcode
403 && handler->handle != cmd_bindswitch 411 && handler->handle != cmd_bindswitch
412 && handler->handle != cmd_bindgesture
404 && handler->handle != cmd_set 413 && handler->handle != cmd_set
405 && handler->handle != cmd_for_window 414 && handler->handle != cmd_for_window
406 && (*argv[i] == '\"' || *argv[i] == '\'')) { 415 && (*argv[i] == '\"' || *argv[i] == '\'')) {
@@ -418,12 +427,12 @@ cleanup:
418} 427}
419 428
420struct cmd_results *config_subcommand(char **argv, int argc, 429struct cmd_results *config_subcommand(char **argv, int argc,
421 struct cmd_handler *handlers, size_t handlers_size) { 430 const struct cmd_handler *handlers, size_t handlers_size) {
422 char *command = join_args(argv, argc); 431 char *command = join_args(argv, argc);
423 sway_log(SWAY_DEBUG, "Subcommand: %s", command); 432 sway_log(SWAY_DEBUG, "Subcommand: %s", command);
424 free(command); 433 free(command);
425 434
426 struct cmd_handler *handler = find_handler(argv[0], handlers, 435 const struct cmd_handler *handler = find_handler(argv[0], handlers,
427 handlers_size); 436 handlers_size);
428 if (!handler) { 437 if (!handler) {
429 return cmd_results_new(CMD_INVALID, 438 return cmd_results_new(CMD_INVALID,
@@ -453,41 +462,13 @@ struct cmd_results *config_commands_command(char *exec) {
453 goto cleanup; 462 goto cleanup;
454 } 463 }
455 464
456 struct cmd_handler *handler = find_handler(cmd, NULL, 0); 465 const struct cmd_handler *handler = find_handler(cmd, NULL, 0);
457 if (!handler && strcmp(cmd, "*") != 0) { 466 if (!handler && strcmp(cmd, "*") != 0) {
458 results = cmd_results_new(CMD_INVALID, 467 results = cmd_results_new(CMD_INVALID,
459 "Unknown/invalid command '%s'", cmd); 468 "Unknown/invalid command '%s'", cmd);
460 goto cleanup; 469 goto cleanup;
461 } 470 }
462 471
463 enum command_context context = 0;
464
465 struct {
466 char *name;
467 enum command_context context;
468 } context_names[] = {
469 { "config", CONTEXT_CONFIG },
470 { "binding", CONTEXT_BINDING },
471 { "ipc", CONTEXT_IPC },
472 { "criteria", CONTEXT_CRITERIA },
473 { "all", CONTEXT_ALL },
474 };
475
476 for (int i = 1; i < argc; ++i) {
477 size_t j;
478 for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
479 if (strcmp(context_names[j].name, argv[i]) == 0) {
480 break;
481 }
482 }
483 if (j == sizeof(context_names) / sizeof(context_names[0])) {
484 results = cmd_results_new(CMD_INVALID,
485 "Invalid command context %s", argv[i]);
486 goto cleanup;
487 }
488 context |= context_names[j].context;
489 }
490
491 results = cmd_results_new(CMD_SUCCESS, NULL); 472 results = cmd_results_new(CMD_SUCCESS, NULL);
492 473
493cleanup: 474cleanup:
@@ -504,13 +485,19 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
504 } 485 }
505 results->status = status; 486 results->status = status;
506 if (format) { 487 if (format) {
507 char *error = malloc(256); 488 char *error = NULL;
508 va_list args; 489 va_list args;
509 va_start(args, format); 490 va_start(args, format);
510 if (error) { 491 int slen = vsnprintf(NULL, 0, format, args);
511 vsnprintf(error, 256, format, args);
512 }
513 va_end(args); 492 va_end(args);
493 if (slen > 0) {
494 error = malloc(slen + 1);
495 if (error != NULL) {
496 va_start(args, format);
497 vsnprintf(error, slen + 1, format, args);
498 va_end(args);
499 }
500 }
514 results->error = error; 501 results->error = error;
515 } else { 502 } else {
516 results->error = NULL; 503 results->error = NULL;
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index d42b7fc2..8571d282 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -8,7 +8,7 @@
8#include "log.h" 8#include "log.h"
9 9
10// Must be in alphabetical order for bsearch 10// Must be in alphabetical order for bsearch
11static struct cmd_handler bar_handlers[] = { 11static const struct cmd_handler bar_handlers[] = {
12 { "bindcode", bar_cmd_bindcode }, 12 { "bindcode", bar_cmd_bindcode },
13 { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, 13 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
14 { "bindsym", bar_cmd_bindsym }, 14 { "bindsym", bar_cmd_bindsym },
@@ -41,7 +41,7 @@ static struct cmd_handler bar_handlers[] = {
41}; 41};
42 42
43// Must be in alphabetical order for bsearch 43// Must be in alphabetical order for bsearch
44static struct cmd_handler bar_config_handlers[] = { 44static const struct cmd_handler bar_config_handlers[] = {
45 { "id", bar_cmd_id }, 45 { "id", bar_cmd_id },
46 { "swaybar_command", bar_cmd_swaybar_command }, 46 { "swaybar_command", bar_cmd_swaybar_command },
47}; 47};
@@ -116,6 +116,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
116 if (res && res->status != CMD_SUCCESS) { 116 if (res && res->status != CMD_SUCCESS) {
117 if (id) { 117 if (id) {
118 free_bar_config(config->current_bar); 118 free_bar_config(config->current_bar);
119 config->current_bar = NULL;
119 id = NULL; 120 id = NULL;
120 } 121 }
121 return res; 122 return res;
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 2d5b22bf..275fa3c6 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -4,7 +4,7 @@
4#include "util.h" 4#include "util.h"
5 5
6// Must be in alphabetical order for bsearch 6// Must be in alphabetical order for bsearch
7static struct cmd_handler bar_colors_handlers[] = { 7static const struct cmd_handler bar_colors_handlers[] = {
8 { "active_workspace", bar_colors_cmd_active_workspace }, 8 { "active_workspace", bar_colors_cmd_active_workspace },
9 { "background", bar_colors_cmd_background }, 9 { "background", bar_colors_cmd_background },
10 { "binding_mode", bar_colors_cmd_binding_mode }, 10 { "binding_mode", bar_colors_cmd_binding_mode },
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 62987f3e..891c87af 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -11,7 +11,20 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
11 } 11 }
12 char *font = join_args(argv, argc); 12 char *font = join_args(argv, argc);
13 free(config->current_bar->font); 13 free(config->current_bar->font);
14 config->current_bar->font = font; 14
15 if (strncmp(font, "pango:", 6) == 0) {
16 if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
17 config->current_bar->pango_markup = true;
18 }
19 config->current_bar->font = strdup(font + 6);
20 } else {
21 if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
22 config->current_bar->pango_markup = false;
23 }
24 config->current_bar->font = strdup(font);
25 }
26
27 free(font);
15 sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", 28 sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s",
16 config->current_bar->font, config->current_bar->id); 29 config->current_bar->font, config->current_bar->id);
17 return cmd_results_new(CMD_SUCCESS, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 1f08a5d2..8b661e3a 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -54,7 +54,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
54 } 54 }
55 55
56 const char *state = argv[0]; 56 const char *state = argv[0];
57 if (config->reading) { 57 if (config->current_bar) {
58 error = bar_set_hidden_state(config->current_bar, state); 58 error = bar_set_hidden_state(config->current_bar, state);
59 } else { 59 } else {
60 const char *id = argc == 2 ? argv[1] : NULL; 60 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 8b3fb275..7c2f423b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -58,7 +58,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
58 } 58 }
59 59
60 const char *mode = argv[0]; 60 const char *mode = argv[0];
61 if (config->reading) { 61 if (config->current_bar) {
62 error = bar_set_mode(config->current_bar, mode); 62 error = bar_set_mode(config->current_bar, mode);
63 } else { 63 } else {
64 const char *id = argc == 2 ? argv[1] : NULL; 64 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index f6e58d99..c0b383db 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -8,6 +8,7 @@
8#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
9#include "sway/commands.h" 9#include "sway/commands.h"
10#include "sway/config.h" 10#include "sway/config.h"
11#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 12#include "sway/input/cursor.h"
12#include "sway/input/keyboard.h" 13#include "sway/input/keyboard.h"
13#include "sway/ipc-server.h" 14#include "sway/ipc-server.h"
@@ -46,7 +47,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a,
46 if (binding_a->type != binding_b->type) { 47 if (binding_a->type != binding_b->type) {
47 return false; 48 return false;
48 } 49 }
49 if (binding_a->state != binding_b->state) { 50 if (binding_a->trigger != binding_b->trigger) {
50 return false; 51 return false;
51 } 52 }
52 if ((binding_a->flags & BINDING_LOCKED) != 53 if ((binding_a->flags & BINDING_LOCKED) !=
@@ -371,6 +372,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
371 strlen("--input-device=")) == 0) { 372 strlen("--input-device=")) == 0) {
372 free(binding->input); 373 free(binding->input);
373 binding->input = strdup(argv[0] + strlen("--input-device=")); 374 binding->input = strdup(argv[0] + strlen("--input-device="));
375 strip_quotes(binding->input);
374 } else if (strcmp("--no-warn", argv[0]) == 0) { 376 } else if (strcmp("--no-warn", argv[0]) == 0) {
375 warn = false; 377 warn = false;
376 } else if (strcmp("--no-repeat", argv[0]) == 0) { 378 } else if (strcmp("--no-repeat", argv[0]) == 0) {
@@ -550,17 +552,17 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
550 "unknown switch %s)", bindtype, split->items[0]); 552 "unknown switch %s)", bindtype, split->items[0]);
551 } 553 }
552 if (strcmp(split->items[1], "on") == 0) { 554 if (strcmp(split->items[1], "on") == 0) {
553 binding->state = WLR_SWITCH_STATE_ON; 555 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
554 } else if (strcmp(split->items[1], "off") == 0) { 556 } else if (strcmp(split->items[1], "off") == 0) {
555 binding->state = WLR_SWITCH_STATE_OFF; 557 binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
556 } else if (strcmp(split->items[1], "toggle") == 0) { 558 } else if (strcmp(split->items[1], "toggle") == 0) {
557 binding->state = WLR_SWITCH_STATE_TOGGLE; 559 binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
558 } else { 560 } else {
559 free_switch_binding(binding); 561 free_switch_binding(binding);
560 return cmd_results_new(CMD_FAILURE, 562 return cmd_results_new(CMD_FAILURE,
561 "Invalid %s command " 563 "Invalid %s command "
562 "(expected switch state: unknown state %d)", 564 "(expected switch state: unknown state %s)",
563 bindtype, split->items[0]); 565 bindtype, split->items[1]);
564 } 566 }
565 list_free_items_and_destroy(split); 567 list_free_items_and_destroy(split);
566 568
@@ -642,6 +644,8 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
642 if (success) { 644 if (success) {
643 ipc_event_binding(binding); 645 ipc_event_binding(binding);
644 } 646 }
647
648 transaction_commit_dirty();
645} 649}
646 650
647/** 651/**
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 647663ac..7818fc96 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -19,11 +19,11 @@ static void set_border(struct sway_container *con,
19 view_set_csd_from_server(con->view, false); 19 view_set_csd_from_server(con->view, false);
20 } else if (!con->view->using_csd && new_border == B_CSD) { 20 } else if (!con->view->using_csd && new_border == B_CSD) {
21 view_set_csd_from_server(con->view, true); 21 view_set_csd_from_server(con->view, true);
22 con->saved_border = con->border; 22 con->saved_border = con->pending.border;
23 } 23 }
24 } 24 }
25 if (new_border != B_CSD || container_is_floating(con)) { 25 if (new_border != B_CSD || container_is_floating(con)) {
26 con->border = new_border; 26 con->pending.border = new_border;
27 } 27 }
28 if (con->view) { 28 if (con->view) {
29 con->view->using_csd = new_border == B_CSD; 29 con->view->using_csd = new_border == B_CSD;
@@ -35,7 +35,7 @@ static void border_toggle(struct sway_container *con) {
35 set_border(con, B_NONE); 35 set_border(con, B_NONE);
36 return; 36 return;
37 } 37 }
38 switch (con->border) { 38 switch (con->pending.border) {
39 case B_NONE: 39 case B_NONE:
40 set_border(con, B_PIXEL); 40 set_border(con, B_PIXEL);
41 break; 41 break;
@@ -88,7 +88,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
88 "or 'border pixel <px>'"); 88 "or 'border pixel <px>'");
89 } 89 }
90 if (argc == 2) { 90 if (argc == 2) {
91 container->border_thickness = atoi(argv[1]); 91 container->pending.border_thickness = atoi(argv[1]);
92 } 92 }
93 93
94 if (container_is_floating(container)) { 94 if (container_is_floating(container)) {
diff --git a/sway/commands/client.c b/sway/commands/client.c
index dd0694df..77263145 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -18,6 +18,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
18 return error; 18 return error;
19 } 19 }
20 20
21 if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) {
22 sway_log(SWAY_ERROR,
23 "Warning: indicator and child_border colors have no effect for %s",
24 cmd_name);
25 }
26
21 struct border_colors colors = {0}; 27 struct border_colors colors = {0};
22 const char *ind_hex = argc > 3 ? argv[3] : default_indicator; 28 const char *ind_hex = argc > 3 ? argv[3] : default_indicator;
23 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background 29 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background
@@ -80,3 +86,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) {
80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); 86 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]);
81 return cmd_results_new(CMD_SUCCESS, NULL); 87 return cmd_results_new(CMD_SUCCESS, NULL);
82} 88}
89
90struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {
91 struct cmd_results *result = handle_command(argc, argv,
92 "client.focused_tab_title",
93 &config->border_colors.focused_tab_title, "#2e9ef4ff");
94 if (result && result->status == CMD_SUCCESS) {
95 config->has_focused_tab_title = true;
96 }
97 return result;
98}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 39e48a44..b35065c1 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -7,6 +7,7 @@
7#include <signal.h> 7#include <signal.h>
8#include "sway/commands.h" 8#include "sway/commands.h"
9#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/server.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"
@@ -26,7 +27,7 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
26 27
27struct cmd_results *cmd_exec_process(int argc, char **argv) { 28struct cmd_results *cmd_exec_process(int argc, char **argv) {
28 struct cmd_results *error = NULL; 29 struct cmd_results *error = NULL;
29 char *tmp = NULL; 30 char *cmd = NULL;
30 if (strcmp(argv[0], "--no-startup-id") == 0) { 31 if (strcmp(argv[0], "--no-startup-id") == 0) {
31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 32 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored.");
32 --argc; ++argv; 33 --argc; ++argv;
@@ -36,17 +37,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
36 } 37 }
37 38
38 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { 39 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) {
39 tmp = strdup(argv[0]); 40 cmd = strdup(argv[0]);
40 strip_quotes(tmp); 41 strip_quotes(cmd);
41 } else { 42 } else {
42 tmp = join_args(argv, argc); 43 cmd = join_args(argv, argc);
43 } 44 }
44 45
45 // Put argument into cmd array
46 char cmd[4096];
47 strncpy(cmd, tmp, sizeof(cmd) - 1);
48 cmd[sizeof(cmd) - 1] = 0;
49 free(tmp);
50 sway_log(SWAY_DEBUG, "Executing %s", cmd); 46 sway_log(SWAY_DEBUG, "Executing %s", cmd);
51 47
52 int fd[2]; 48 int fd[2];
@@ -58,15 +54,18 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
58 // Fork process 54 // Fork process
59 if ((pid = fork()) == 0) { 55 if ((pid = fork()) == 0) {
60 // Fork child process again 56 // Fork child process again
57 restore_nofile_limit();
61 setsid(); 58 setsid();
62 sigset_t set; 59 sigset_t set;
63 sigemptyset(&set); 60 sigemptyset(&set);
64 sigprocmask(SIG_SETMASK, &set, NULL); 61 sigprocmask(SIG_SETMASK, &set, NULL);
62 signal(SIGPIPE, SIG_DFL);
65 close(fd[0]); 63 close(fd[0]);
66 if ((child = fork()) == 0) { 64 if ((child = fork()) == 0) {
67 close(fd[1]); 65 close(fd[1]);
68 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); 66 execlp("sh", "sh", "-c", cmd, (void *)NULL);
69 _exit(0); 67 sway_log_errno(SWAY_ERROR, "execlp failed");
68 _exit(1);
70 } 69 }
71 ssize_t s = 0; 70 ssize_t s = 0;
72 while ((size_t)s < sizeof(pid_t)) { 71 while ((size_t)s < sizeof(pid_t)) {
@@ -75,10 +74,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
75 close(fd[1]); 74 close(fd[1]);
76 _exit(0); // Close child process 75 _exit(0); // Close child process
77 } else if (pid < 0) { 76 } else if (pid < 0) {
77 free(cmd);
78 close(fd[0]); 78 close(fd[0]);
79 close(fd[1]); 79 close(fd[1]);
80 return cmd_results_new(CMD_FAILURE, "fork() failed"); 80 return cmd_results_new(CMD_FAILURE, "fork() failed");
81 } 81 }
82 free(cmd);
82 close(fd[1]); // close write 83 close(fd[1]); // close write
83 ssize_t s = 0; 84 ssize_t s = 0;
84 while ((size_t)s < sizeof(pid_t)) { 85 while ((size_t)s < sizeof(pid_t)) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index ce123345..74f6522c 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -40,8 +40,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
40 // If the container is in a floating split container, 40 // If the container is in a floating split container,
41 // operate on the split container instead of the child. 41 // operate on the split container instead of the child.
42 if (container_is_floating_or_child(container)) { 42 if (container_is_floating_or_child(container)) {
43 while (container->parent) { 43 while (container->pending.parent) {
44 container = container->parent; 44 container = container->pending.parent;
45 } 45 }
46 } 46 }
47 47
@@ -51,8 +51,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
51 container_set_floating(container, wants_floating); 51 container_set_floating(container, wants_floating);
52 52
53 // Floating containers in the scratchpad should be ignored 53 // Floating containers in the scratchpad should be ignored
54 if (container->workspace) { 54 if (container->pending.workspace) {
55 arrange_workspace(container->workspace); 55 arrange_workspace(container->pending.workspace);
56 } 56 }
57 57
58 return cmd_results_new(CMD_SUCCESS, NULL); 58 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 79b7aed5..facd82de 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container,
54 } else { 54 } else {
55 return false; 55 return false;
56 } 56 }
57 57
58 return true; 58 return true;
59} 59}
60 60
@@ -141,9 +141,9 @@ static struct sway_node *node_get_in_direction_tiling(
141 struct sway_container *wrap_candidate = NULL; 141 struct sway_container *wrap_candidate = NULL;
142 struct sway_container *current = container; 142 struct sway_container *current = container;
143 while (current) { 143 while (current) {
144 if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) { 144 if (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
145 // Fullscreen container with a direction - go straight to outputs 145 // Fullscreen container with a direction - go straight to outputs
146 struct sway_output *output = current->workspace->output; 146 struct sway_output *output = current->pending.workspace->output;
147 struct sway_output *new_output = 147 struct sway_output *new_output =
148 output_get_in_direction(output, dir); 148 output_get_in_direction(output, dir);
149 if (!new_output) { 149 if (!new_output) {
@@ -151,7 +151,7 @@ static struct sway_node *node_get_in_direction_tiling(
151 } 151 }
152 return get_node_in_output_direction(new_output, dir); 152 return get_node_in_output_direction(new_output, dir);
153 } 153 }
154 if (current->fullscreen_mode == FULLSCREEN_GLOBAL) { 154 if (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
155 return NULL; 155 return NULL;
156 } 156 }
157 157
@@ -202,11 +202,11 @@ static struct sway_node *node_get_in_direction_tiling(
202 } 202 }
203 } 203 }
204 204
205 current = current->parent; 205 current = current->pending.parent;
206 } 206 }
207 207
208 // Check a different output 208 // Check a different output
209 struct sway_output *output = container->workspace->output; 209 struct sway_output *output = container->pending.workspace->output;
210 struct sway_output *new_output = output_get_in_direction(output, dir); 210 struct sway_output *new_output = output_get_in_direction(output, dir);
211 if ((config->focus_wrapping != WRAP_WORKSPACE || 211 if ((config->focus_wrapping != WRAP_WORKSPACE ||
212 container->node.type == N_WORKSPACE) && new_output) { 212 container->node.type == N_WORKSPACE) && new_output) {
@@ -226,23 +226,23 @@ static struct sway_node *node_get_in_direction_tiling(
226static struct sway_node *node_get_in_direction_floating( 226static struct sway_node *node_get_in_direction_floating(
227 struct sway_container *con, struct sway_seat *seat, 227 struct sway_container *con, struct sway_seat *seat,
228 enum wlr_direction dir) { 228 enum wlr_direction dir) {
229 double ref_lx = con->x + con->width / 2; 229 double ref_lx = con->pending.x + con->pending.width / 2;
230 double ref_ly = con->y + con->height / 2; 230 double ref_ly = con->pending.y + con->pending.height / 2;
231 double closest_distance = DBL_MAX; 231 double closest_distance = DBL_MAX;
232 struct sway_container *closest_con = NULL; 232 struct sway_container *closest_con = NULL;
233 233
234 if (!con->workspace) { 234 if (!con->pending.workspace) {
235 return NULL; 235 return NULL;
236 } 236 }
237 237
238 for (int i = 0; i < con->workspace->floating->length; i++) { 238 for (int i = 0; i < con->pending.workspace->floating->length; i++) {
239 struct sway_container *floater = con->workspace->floating->items[i]; 239 struct sway_container *floater = con->pending.workspace->floating->items[i];
240 if (floater == con) { 240 if (floater == con) {
241 continue; 241 continue;
242 } 242 }
243 float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT 243 float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT
244 ? (floater->x + floater->width / 2) - ref_lx 244 ? (floater->pending.x + floater->pending.width / 2) - ref_lx
245 : (floater->y + floater->height / 2) - ref_ly; 245 : (floater->pending.y + floater->pending.height / 2) - ref_ly;
246 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { 246 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) {
247 distance = -distance; 247 distance = -distance;
248 } 248 }
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
267 new_focus = seat_get_focus_inactive_tiling(seat, ws); 267 new_focus = seat_get_focus_inactive_tiling(seat, ws);
268 } 268 }
269 if (new_focus) { 269 if (new_focus) {
270 struct sway_container *new_focus_view =
271 seat_get_focus_inactive_view(seat, &new_focus->node);
272 if (new_focus_view) {
273 new_focus = new_focus_view;
274 }
270 seat_set_focus_container(seat, new_focus); 275 seat_set_focus_container(seat, new_focus);
271 276
272 // If we're on the floating layer and the floating container area 277 // If we're on the floating layer and the floating container area
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
280 } 285 }
281 } else { 286 } else {
282 return cmd_results_new(CMD_FAILURE, 287 return cmd_results_new(CMD_FAILURE,
283 "Failed to find a %s container in workspace", 288 "Failed to find a %s container in workspace.",
284 floating ? "floating" : "tiling"); 289 floating ? "floating" : "tiling");
285 } 290 }
286 return cmd_results_new(CMD_SUCCESS, NULL); 291 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
290 int argc, char **argv) { 295 int argc, char **argv) {
291 if (!argc) { 296 if (!argc) {
292 return cmd_results_new(CMD_INVALID, 297 return cmd_results_new(CMD_INVALID,
293 "Expected 'focus output <direction|name>'"); 298 "Expected 'focus output <direction|name>'.");
294 } 299 }
295 char *identifier = join_args(argv, argc); 300 char *identifier = join_args(argv, argc);
296 struct sway_output *output = output_by_name_or_id(identifier); 301 struct sway_output *output = output_by_name_or_id(identifier);
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
300 if (!parse_direction(identifier, &direction)) { 305 if (!parse_direction(identifier, &direction)) {
301 free(identifier); 306 free(identifier);
302 return cmd_results_new(CMD_INVALID, 307 return cmd_results_new(CMD_INVALID,
303 "There is no output with that name"); 308 "There is no output with that name.");
304 } 309 }
305 struct sway_workspace *ws = seat_get_focused_workspace(seat); 310 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) { 311 if (!ws) {
307 free(identifier); 312 free(identifier);
308 return cmd_results_new(CMD_FAILURE, 313 return cmd_results_new(CMD_FAILURE,
309 "No focused workspace to base directions off of"); 314 "No focused workspace to base directions off of.");
310 } 315 }
311 output = output_get_in_direction(ws->output, direction); 316 output = output_get_in_direction(ws->output, direction);
312 317
@@ -334,7 +339,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
334static struct cmd_results *focus_parent(void) { 339static struct cmd_results *focus_parent(void) {
335 struct sway_seat *seat = config->handler_context.seat; 340 struct sway_seat *seat = config->handler_context.seat;
336 struct sway_container *con = config->handler_context.container; 341 struct sway_container *con = config->handler_context.container;
337 if (!con || con->fullscreen_mode) { 342 if (!con || con->pending.fullscreen_mode) {
338 return cmd_results_new(CMD_SUCCESS, NULL); 343 return cmd_results_new(CMD_SUCCESS, NULL);
339 } 344 }
340 struct sway_node *parent = node_get_parent(&con->node); 345 struct sway_node *parent = node_get_parent(&con->node);
@@ -370,13 +375,24 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
370 struct sway_seat *seat = config->handler_context.seat; 375 struct sway_seat *seat = config->handler_context.seat;
371 if (node->type < N_WORKSPACE) { 376 if (node->type < N_WORKSPACE) {
372 return cmd_results_new(CMD_FAILURE, 377 return cmd_results_new(CMD_FAILURE,
373 "Command 'focus' cannot be used above the workspace level"); 378 "Command 'focus' cannot be used above the workspace level.");
374 } 379 }
375 380
376 if (argc == 0 && container) { 381 if (argc == 0) {
382 if (!container) {
383 return cmd_results_new(CMD_FAILURE, "No container to focus was specified.");
384 }
385
377 if (container_is_scratchpad_hidden_or_child(container)) { 386 if (container_is_scratchpad_hidden_or_child(container)) {
378 root_scratchpad_show(container); 387 root_scratchpad_show(container);
379 } 388 }
389 // if we are switching to a container under a fullscreen window, we first
390 // need to exit fullscreen so that the newly focused container becomes visible
391 struct sway_container *obstructing = container_obstructing_fullscreen_container(container);
392 if (obstructing) {
393 container_fullscreen_disable(obstructing);
394 arrange_root();
395 }
380 seat_set_focus_container(seat, container); 396 seat_set_focus_container(seat, container);
381 seat_consider_warp_to_focus(seat); 397 seat_consider_warp_to_focus(seat);
382 container_raise_floating(container); 398 container_raise_floating(container);
@@ -439,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
439 return cmd_results_new(CMD_FAILURE, ""); 455 return cmd_results_new(CMD_FAILURE, "");
440 } 456 }
441 struct sway_node *next_focus = NULL; 457 struct sway_node *next_focus = NULL;
442 if (container_is_floating(container)) { 458 if (container_is_floating(container) &&
459 container->pending.fullscreen_mode == FULLSCREEN_NONE) {
443 next_focus = node_get_in_direction_floating(container, seat, direction); 460 next_focus = node_get_in_direction_floating(container, seat, direction);
444 } else { 461 } else {
445 next_focus = node_get_in_direction_tiling(container, seat, direction, descend); 462 next_focus = node_get_in_direction_tiling(container, seat, direction, descend);
diff --git a/sway/commands/font.c b/sway/commands/font.c
index c54365b5..74bb6b9f 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -4,6 +4,7 @@
4#include "sway/config.h" 4#include "sway/config.h"
5#include "log.h" 5#include "log.h"
6#include "stringop.h" 6#include "stringop.h"
7#include <pango/pangocairo.h>
7 8
8struct cmd_results *cmd_font(int argc, char **argv) { 9struct cmd_results *cmd_font(int argc, char **argv) {
9 struct cmd_results *error = NULL; 10 struct cmd_results *error = NULL;
@@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) {
16 if (strncmp(font, "pango:", 6) == 0) { 17 if (strncmp(font, "pango:", 6) == 0) {
17 config->pango_markup = true; 18 config->pango_markup = true;
18 config->font = strdup(font + 6); 19 config->font = strdup(font + 6);
20 free(font);
19 } else { 21 } else {
20 config->pango_markup = false; 22 config->pango_markup = false;
21 config->font = strdup(font); 23 config->font = font;
22 } 24 }
23 25
24 free(font); 26 // Parse the font early so we can reject it if it's not valid for pango.
25 config_update_font_height(true); 27 // Also avoids re-parsing each time we render text.
28 PangoFontDescription *font_description = pango_font_description_from_string(config->font);
29
30 const char *family = pango_font_description_get_family(font_description);
31 if (family == NULL) {
32 pango_font_description_free(font_description);
33 return cmd_results_new(CMD_FAILURE, "Invalid font family.");
34 }
35
36 const gint size = pango_font_description_get_size(font_description);
37 if (size == 0) {
38 pango_font_description_free(font_description);
39 return cmd_results_new(CMD_FAILURE, "Invalid font size.");
40 }
41
42 if (config->font_description != NULL) {
43 pango_font_description_free(config->font_description);
44 }
45
46 config->font_description = font_description;
47 config_update_font_height();
48
26 return cmd_results_new(CMD_SUCCESS, NULL); 49 return cmd_results_new(CMD_SUCCESS, NULL);
27} 50}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 3392a7f7..21c1e9a0 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -18,30 +18,19 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
18 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
19 "Can't run this command while there's no outputs connected."); 19 "Can't run this command while there's no outputs connected.");
20 } 20 }
21 struct sway_node *node = config->handler_context.node;
22 struct sway_container *container = config->handler_context.container; 21 struct sway_container *container = config->handler_context.container;
23 struct sway_workspace *workspace = config->handler_context.workspace;
24 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
25 return cmd_results_new(CMD_FAILURE,
26 "Can't fullscreen an empty workspace");
27 }
28 22
29 // If in the scratchpad, operate on the highest container 23 if (!container) {
30 if (container && !container->workspace) { 24 // If the focus is not a container, do nothing successfully
31 while (container->parent) { 25 return cmd_results_new(CMD_SUCCESS, NULL);
32 container = container->parent; 26 } else if (!container->pending.workspace) {
33 } 27 // If in the scratchpad, operate on the highest container
34 } 28 while (container->pending.parent) {
35 29 container = container->pending.parent;
36 bool is_fullscreen = false;
37 for (struct sway_container *curr = container; curr; curr = curr->parent) {
38 if (curr->fullscreen_mode != FULLSCREEN_NONE) {
39 container = curr;
40 is_fullscreen = true;
41 break;
42 } 30 }
43 } 31 }
44 32
33 bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE;
45 bool global = false; 34 bool global = false;
46 bool enable = !is_fullscreen; 35 bool enable = !is_fullscreen;
47 36
@@ -57,13 +46,6 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
57 global = strcasecmp(argv[1], "global") == 0; 46 global = strcasecmp(argv[1], "global") == 0;
58 } 47 }
59 48
60 if (enable && node->type == N_WORKSPACE) {
61 // Wrap the workspace's children in a container so we can fullscreen it
62 container = workspace_wrap_children(workspace);
63 workspace->layout = L_HORIZ;
64 seat_set_focus_container(config->handler_context.seat, container);
65 }
66
67 enum sway_fullscreen_mode mode = FULLSCREEN_NONE; 49 enum sway_fullscreen_mode mode = FULLSCREEN_NONE;
68 if (enable) { 50 if (enable) {
69 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; 51 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE;
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 021df843..1deeb56e 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -11,7 +11,8 @@
11enum gaps_op { 11enum gaps_op {
12 GAPS_OP_SET, 12 GAPS_OP_SET,
13 GAPS_OP_ADD, 13 GAPS_OP_ADD,
14 GAPS_OP_SUBTRACT 14 GAPS_OP_SUBTRACT,
15 GAPS_OP_TOGGLE
15}; 16};
16 17
17struct gaps_data { 18struct gaps_data {
@@ -102,6 +103,9 @@ static void apply_gaps_op(int *prop, enum gaps_op op, int amount) {
102 case GAPS_OP_SUBTRACT: 103 case GAPS_OP_SUBTRACT:
103 *prop -= amount; 104 *prop -= amount;
104 break; 105 break;
106 case GAPS_OP_TOGGLE:
107 *prop = *prop ? 0 : amount;
108 break;
105 } 109 }
106} 110}
107 111
@@ -133,9 +137,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) {
133} 137}
134 138
135// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all 139// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all
136// set|plus|minus <px> 140// set|plus|minus|toggle <px>
137static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" 141static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|"
138 "top|right|bottom|left current|all set|plus|minus <px>'"; 142 "top|right|bottom|left current|all set|plus|minus|toggle <px>'";
139static struct cmd_results *gaps_set_runtime(int argc, char **argv) { 143static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
140 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 144 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
141 if (error) { 145 if (error) {
@@ -180,6 +184,8 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
180 data.operation = GAPS_OP_ADD; 184 data.operation = GAPS_OP_ADD;
181 } else if (strcasecmp(argv[2], "minus") == 0) { 185 } else if (strcasecmp(argv[2], "minus") == 0) {
182 data.operation = GAPS_OP_SUBTRACT; 186 data.operation = GAPS_OP_SUBTRACT;
187 } else if (strcasecmp(argv[2], "toggle") == 0) {
188 data.operation = GAPS_OP_TOGGLE;
183 } else { 189 } else {
184 return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); 190 return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime);
185 } 191 }
@@ -200,7 +206,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
200} 206}
201 207
202// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces 208// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces
203// gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only 209// gaps inner|outer|<dir>|<side> current|all set|plus|minus|toggle <px> - runtime only
204// <dir> = horizontal|vertical 210// <dir> = horizontal|vertical
205// <side> = top|right|bottom|left 211// <side> = top|right|bottom|left
206struct cmd_results *cmd_gaps(int argc, char **argv) { 212struct cmd_results *cmd_gaps(int argc, char **argv) {
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
new file mode 100644
index 00000000..d4442cc3
--- /dev/null
+++ b/sway/commands/gesture.c
@@ -0,0 +1,166 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h"
3
4#include "gesture.h"
5#include "log.h"
6#include "stringop.h"
7#include "sway/commands.h"
8
9void free_gesture_binding(struct sway_gesture_binding *binding) {
10 if (!binding) {
11 return;
12 }
13 free(binding->input);
14 free(binding->command);
15 free(binding);
16}
17
18/**
19 * Returns true if the bindings have the same gesture type, direction, etc
20 */
21static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
22 struct sway_gesture_binding *binding_b) {
23 if (strcmp(binding_a->input, binding_b->input) != 0) {
24 return false;
25 }
26
27 if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
28 return false;
29 }
30
31 if ((binding_a->flags & BINDING_EXACT) !=
32 (binding_b->flags & BINDING_EXACT)) {
33 return false;
34 }
35 return true;
36}
37
38/**
39 * Add gesture binding to config
40 */
41static struct cmd_results *gesture_binding_add(
42 struct sway_gesture_binding *binding,
43 const char *gesturecombo, bool warn) {
44 list_t *mode_bindings = config->current_mode->gesture_bindings;
45 // overwrite the binding if it already exists
46 bool overwritten = false;
47 for (int i = 0; i < mode_bindings->length; ++i) {
48 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
49 if (binding_gesture_equal(binding, config_binding)) {
50 sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
51 gesturecombo, binding->command, config_binding->command);
52 if (warn) {
53 config_add_swaynag_warning("Overwriting binding"
54 "'%s' to `%s` from `%s`",
55 gesturecombo, binding->command,
56 config_binding->command);
57 }
58 free_gesture_binding(config_binding);
59 mode_bindings->items[i] = binding;
60 overwritten = true;
61 }
62 }
63
64 if (!overwritten) {
65 list_add(mode_bindings, binding);
66 sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
67 gesturecombo, binding->command);
68 }
69
70 return cmd_results_new(CMD_SUCCESS, NULL);
71}
72
73/**
74 * Remove gesture binding from config
75 */
76static struct cmd_results *gesture_binding_remove(
77 struct sway_gesture_binding *binding, const char *gesturecombo) {
78 list_t *mode_bindings = config->current_mode->gesture_bindings;
79 for (int i = 0; i < mode_bindings->length; ++i) {
80 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
81 if (binding_gesture_equal(binding, config_binding)) {
82 free_gesture_binding(config_binding);
83 free_gesture_binding(binding);
84 list_del(mode_bindings, i);
85 sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
86 gesturecombo);
87 return cmd_results_new(CMD_SUCCESS, NULL);
88 }
89 }
90
91 free_gesture_binding(binding);
92 return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
93 gesturecombo);
94}
95
96/**
97 * Parse and execute bindgesture or unbindgesture command.
98 */
99static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
100 int minargs = 2;
101 char *bindtype = "bindgesture";
102 if (unbind) {
103 minargs--;
104 bindtype = "unbindgesture";
105 }
106
107 struct cmd_results *error = NULL;
108 if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
109 return error;
110 }
111 struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
112 if (!binding) {
113 return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
114 }
115 binding->input = strdup("*");
116
117 bool warn = true;
118
119 // Handle flags
120 while (argc > 0) {
121 if (strcmp("--exact", argv[0]) == 0) {
122 binding->flags |= BINDING_EXACT;
123 } else if (strcmp("--no-warn", argv[0]) == 0) {
124 warn = false;
125 } else if (strncmp("--input-device=", argv[0],
126 strlen("--input-device=")) == 0) {
127 free(binding->input);
128 binding->input = strdup(argv[0] + strlen("--input-device="));
129 } else {
130 break;
131 }
132 argv++;
133 argc--;
134 }
135
136 if (argc < minargs) {
137 free(binding);
138 return cmd_results_new(CMD_FAILURE,
139 "Invalid %s command (expected at least %d "
140 "non-option arguments, got %d)", bindtype, minargs, argc);
141 }
142
143 char* errmsg = NULL;
144 if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
145 free(binding);
146 struct cmd_results *final = cmd_results_new(CMD_FAILURE,
147 "Invalid %s command (%s)",
148 bindtype, errmsg);
149 free(errmsg);
150 return final;
151 }
152
153 if (unbind) {
154 return gesture_binding_remove(binding, argv[0]);
155 }
156 binding->command = join_args(argv + 1, argc - 1);
157 return gesture_binding_add(binding, argv[0], warn);
158}
159
160struct cmd_results *cmd_bindgesture(int argc, char **argv) {
161 return cmd_bind_or_unbind_gesture(argc, argv, false);
162}
163
164struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
165 return cmd_bind_or_unbind_gesture(argc, argv, true);
166}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index c9bb8e06..77acb671 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -7,7 +7,7 @@
7#include "stringop.h" 7#include "stringop.h"
8 8
9// must be in order for the bsearch 9// must be in order for the bsearch
10static struct cmd_handler input_handlers[] = { 10static const struct cmd_handler input_handlers[] = {
11 { "accel_profile", input_cmd_accel_profile }, 11 { "accel_profile", input_cmd_accel_profile },
12 { "calibration_matrix", input_cmd_calibration_matrix }, 12 { "calibration_matrix", input_cmd_calibration_matrix },
13 { "click_method", input_cmd_click_method }, 13 { "click_method", input_cmd_click_method },
@@ -40,7 +40,7 @@ static struct cmd_handler input_handlers[] = {
40}; 40};
41 41
42// must be in order for the bsearch 42// must be in order for the bsearch
43static struct cmd_handler input_config_handlers[] = { 43static const struct cmd_handler input_config_handlers[] = {
44 { "xkb_capslock", input_cmd_xkb_capslock }, 44 { "xkb_capslock", input_cmd_xkb_capslock },
45 { "xkb_numlock", input_cmd_xkb_numlock }, 45 { "xkb_numlock", input_cmd_xkb_numlock },
46}; 46};
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c
index e85495e5..284b57d0 100644
--- a/sway/commands/input/map_to_region.c
+++ b/sway/commands/input/map_to_region.c
@@ -1,7 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wlr/types/wlr_box.h>
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/config.h" 5#include "sway/config.h"
7 6
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c
index d6548a68..3cce4ec8 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,10 +1,16 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <wlr/interfaces/wlr_keyboard.h>
3#include "sway/config.h" 4#include "sway/config.h"
4#include "sway/commands.h" 5#include "sway/commands.h"
5#include "sway/input/input-manager.h" 6#include "sway/input/input-manager.h"
6#include "log.h" 7#include "log.h"
7 8
9struct xkb_switch_layout_action {
10 struct wlr_keyboard *keyboard;
11 xkb_layout_index_t layout;
12};
13
8static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { 14static 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); 15 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
10 if (idx >= num_layouts) { 16 if (idx >= num_layouts) {
@@ -28,10 +34,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {
28 return layout_idx; 34 return layout_idx;
29} 35}
30 36
31static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { 37static 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); 38 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
33 xkb_layout_index_t idx = get_current_layout_index(kbd); 39 xkb_layout_index_t idx = get_current_layout_index(kbd);
34 switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); 40 return (idx + num_layouts + dir) % num_layouts;
35} 41}
36 42
37struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { 43struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
@@ -66,6 +72,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
66 relative = 0; 72 relative = 0;
67 } 73 }
68 74
75 struct xkb_switch_layout_action *actions = calloc(
76 wl_list_length(&server.input->devices),
77 sizeof(struct xkb_switch_layout_action));
78 size_t actions_len = 0;
79
80 if (!actions) {
81 return cmd_results_new(CMD_FAILURE, "Unable to allocate actions");
82 }
83
84 /* Calculate new indexes first because switching a layout in one
85 keyboard may result in a change on other keyboards as well because
86 of keyboard groups. */
69 struct sway_input_device *dev; 87 struct sway_input_device *dev;
70 wl_list_for_each(dev, &server.input->devices, link) { 88 wl_list_for_each(dev, &server.input->devices, link) {
71 if (strcmp(ic->identifier, "*") != 0 && 89 if (strcmp(ic->identifier, "*") != 0 &&
@@ -76,12 +94,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
76 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 94 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
77 continue; 95 continue;
78 } 96 }
97
98 struct xkb_switch_layout_action *action =
99 &actions[actions_len++];
100
101 action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
79 if (relative) { 102 if (relative) {
80 switch_layout_relative(dev->wlr_device->keyboard, relative); 103 action->layout = get_layout_relative(action->keyboard, relative);
81 } else { 104 } else {
82 switch_layout(dev->wlr_device->keyboard, layout); 105 action->layout = layout;
83 } 106 }
84 } 107 }
85 108
109 for (size_t i = 0; i < actions_len; i++) {
110 switch_layout(actions[i].keyboard, actions[i].layout);
111 }
112 free(actions);
113
86 return cmd_results_new(CMD_SUCCESS, NULL); 114 return cmd_results_new(CMD_SUCCESS, NULL);
87} 115}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index f2af183b..2ba61b38 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -133,7 +133,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
133 133
134 // Operate on parent container, like i3. 134 // Operate on parent container, like i3.
135 if (container) { 135 if (container) {
136 container = container->parent; 136 container = container->pending.parent;
137 } 137 }
138 138
139 // We could be working with a container OR a workspace. These are different 139 // We could be working with a container OR a workspace. These are different
@@ -142,10 +142,10 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
142 enum sway_container_layout new_layout = L_NONE; 142 enum sway_container_layout new_layout = L_NONE;
143 enum sway_container_layout old_layout = L_NONE; 143 enum sway_container_layout old_layout = L_NONE;
144 if (container) { 144 if (container) {
145 old_layout = container->layout; 145 old_layout = container->pending.layout;
146 new_layout = get_layout(argc, argv, 146 new_layout = get_layout(argc, argv,
147 container->layout, container->prev_split_layout, 147 container->pending.layout, container->prev_split_layout,
148 container->workspace->output); 148 container->pending.workspace->output);
149 } else { 149 } else {
150 old_layout = workspace->layout; 150 old_layout = workspace->layout;
151 new_layout = get_layout(argc, argv, 151 new_layout = get_layout(argc, argv,
@@ -160,13 +160,13 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
160 if (old_layout != L_TABBED && old_layout != L_STACKED) { 160 if (old_layout != L_TABBED && old_layout != L_STACKED) {
161 container->prev_split_layout = old_layout; 161 container->prev_split_layout = old_layout;
162 } 162 }
163 container->layout = new_layout; 163 container->pending.layout = new_layout;
164 container_update_representation(container); 164 container_update_representation(container);
165 } else if (config->handler_context.container) { 165 } else if (config->handler_context.container) {
166 // i3 avoids changing workspace layouts with a new container 166 // i3 avoids changing workspace layouts with a new container
167 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 167 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817
168 container = workspace_wrap_children(workspace); 168 container = workspace_wrap_children(workspace);
169 container->layout = new_layout; 169 container->pending.layout = new_layout;
170 container_update_representation(container); 170 container_update_representation(container);
171 } else { 171 } else {
172 if (old_layout != L_TABBED && old_layout != L_STACKED) { 172 if (old_layout != L_TABBED && old_layout != L_STACKED) {
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index a5871dab..7263efcb 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -9,12 +9,14 @@
9#include "stringop.h" 9#include "stringop.h"
10 10
11// Must be in order for the bsearch 11// Must be in order for the bsearch
12static struct cmd_handler mode_handlers[] = { 12static const struct cmd_handler mode_handlers[] = {
13 { "bindcode", cmd_bindcode }, 13 { "bindcode", cmd_bindcode },
14 { "bindgesture", cmd_bindgesture },
14 { "bindswitch", cmd_bindswitch }, 15 { "bindswitch", cmd_bindswitch },
15 { "bindsym", cmd_bindsym }, 16 { "bindsym", cmd_bindsym },
16 { "set", cmd_set }, 17 { "set", cmd_set },
17 { "unbindcode", cmd_unbindcode }, 18 { "unbindcode", cmd_unbindcode },
19 { "unbindgesture", cmd_unbindgesture },
18 { "unbindswitch", cmd_unbindswitch }, 20 { "unbindswitch", cmd_unbindswitch },
19 { "unbindsym", cmd_unbindsym }, 21 { "unbindsym", cmd_unbindsym },
20}; 22};
@@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
59 mode->keycode_bindings = create_list(); 61 mode->keycode_bindings = create_list();
60 mode->mouse_bindings = create_list(); 62 mode->mouse_bindings = create_list();
61 mode->switch_bindings = create_list(); 63 mode->switch_bindings = create_list();
64 mode->gesture_bindings = create_list();
62 mode->pango = pango; 65 mode->pango = pango;
63 list_add(config->modes, mode); 66 list_add(config->modes, mode);
64 } 67 }
diff --git a/sway/commands/move.c b/sway/commands/move.c
index f8f89f18..0d0d9727 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -113,8 +113,8 @@ static void container_move_to_container_from_direction(
113 struct sway_container *container, struct sway_container *destination, 113 struct sway_container *container, struct sway_container *destination,
114 enum wlr_direction move_dir) { 114 enum wlr_direction move_dir) {
115 if (destination->view) { 115 if (destination->view) {
116 if (destination->parent == container->parent && 116 if (destination->pending.parent == container->pending.parent &&
117 destination->workspace == container->workspace) { 117 destination->pending.workspace == container->pending.workspace) {
118 sway_log(SWAY_DEBUG, "Swapping siblings"); 118 sway_log(SWAY_DEBUG, "Swapping siblings");
119 list_t *siblings = container_get_siblings(container); 119 list_t *siblings = container_get_siblings(container);
120 int container_index = list_find(siblings, container); 120 int container_index = list_find(siblings, container);
@@ -126,28 +126,28 @@ static void container_move_to_container_from_direction(
126 int offset = 126 int offset =
127 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; 127 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP;
128 int index = container_sibling_index(destination) + offset; 128 int index = container_sibling_index(destination) + offset;
129 if (destination->parent) { 129 if (destination->pending.parent) {
130 container_insert_child(destination->parent, container, index); 130 container_insert_child(destination->pending.parent, container, index);
131 } else { 131 } else {
132 workspace_insert_tiling(destination->workspace, 132 workspace_insert_tiling(destination->pending.workspace,
133 container, index); 133 container, index);
134 } 134 }
135 container->width = container->height = 0; 135 container->pending.width = container->pending.height = 0;
136 container->width_fraction = container->height_fraction = 0; 136 container->width_fraction = container->height_fraction = 0;
137 workspace_squash(destination->workspace); 137 workspace_squash(destination->pending.workspace);
138 } 138 }
139 return; 139 return;
140 } 140 }
141 141
142 if (is_parallel(destination->layout, move_dir)) { 142 if (is_parallel(destination->pending.layout, move_dir)) {
143 sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); 143 sway_log(SWAY_DEBUG, "Reparenting container (parallel)");
144 int index = 144 int index =
145 move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? 145 move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ?
146 0 : destination->children->length; 146 0 : destination->pending.children->length;
147 container_insert_child(destination, container, index); 147 container_insert_child(destination, container, index);
148 container->width = container->height = 0; 148 container->pending.width = container->pending.height = 0;
149 container->width_fraction = container->height_fraction = 0; 149 container->width_fraction = container->height_fraction = 0;
150 workspace_squash(destination->workspace); 150 workspace_squash(destination->pending.workspace);
151 return; 151 return;
152 } 152 }
153 153
@@ -168,7 +168,7 @@ static void container_move_to_container_from_direction(
168static void container_move_to_workspace_from_direction( 168static void container_move_to_workspace_from_direction(
169 struct sway_container *container, struct sway_workspace *workspace, 169 struct sway_container *container, struct sway_workspace *workspace,
170 enum wlr_direction move_dir) { 170 enum wlr_direction move_dir) {
171 container->width = container->height = 0; 171 container->pending.width = container->pending.height = 0;
172 container->width_fraction = container->height_fraction = 0; 172 container->width_fraction = container->height_fraction = 0;
173 173
174 if (is_parallel(workspace->layout, move_dir)) { 174 if (is_parallel(workspace->layout, move_dir)) {
@@ -188,8 +188,8 @@ static void container_move_to_workspace_from_direction(
188 workspace_add_tiling(workspace, container); 188 workspace_add_tiling(workspace, container);
189 return; 189 return;
190 } 190 }
191 while (focus_inactive->parent) { 191 while (focus_inactive->pending.parent) {
192 focus_inactive = focus_inactive->parent; 192 focus_inactive = focus_inactive->pending.parent;
193 } 193 }
194 container_move_to_container_from_direction(container, focus_inactive, 194 container_move_to_container_from_direction(container, focus_inactive,
195 move_dir); 195 move_dir);
@@ -197,25 +197,25 @@ static void container_move_to_workspace_from_direction(
197 197
198static void container_move_to_workspace(struct sway_container *container, 198static void container_move_to_workspace(struct sway_container *container,
199 struct sway_workspace *workspace) { 199 struct sway_workspace *workspace) {
200 if (container->workspace == workspace) { 200 if (container->pending.workspace == workspace) {
201 return; 201 return;
202 } 202 }
203 struct sway_workspace *old_workspace = container->workspace; 203 struct sway_workspace *old_workspace = container->pending.workspace;
204 if (container_is_floating(container)) { 204 if (container_is_floating(container)) {
205 struct sway_output *old_output = container->workspace->output; 205 struct sway_output *old_output = container->pending.workspace->output;
206 container_detach(container); 206 container_detach(container);
207 workspace_add_floating(workspace, container); 207 workspace_add_floating(workspace, container);
208 container_handle_fullscreen_reparent(container); 208 container_handle_fullscreen_reparent(container);
209 // If changing output, center it within the workspace 209 // If changing output, center it within the workspace
210 if (old_output != workspace->output && !container->fullscreen_mode) { 210 if (old_output != workspace->output && !container->pending.fullscreen_mode) {
211 container_floating_move_to_center(container); 211 container_floating_move_to_center(container);
212 } 212 }
213 } else { 213 } else {
214 container_detach(container); 214 container_detach(container);
215 if (workspace_is_empty(workspace) && container->children) { 215 if (workspace_is_empty(workspace) && container->pending.children) {
216 workspace_unwrap_children(workspace, container); 216 workspace_unwrap_children(workspace, container);
217 } else { 217 } else {
218 container->width = container->height = 0; 218 container->pending.width = container->pending.height = 0;
219 container->width_fraction = container->height_fraction = 0; 219 container->width_fraction = container->height_fraction = 0;
220 workspace_add_tiling(workspace, container); 220 workspace_add_tiling(workspace, container);
221 } 221 }
@@ -237,13 +237,13 @@ static void container_move_to_container(struct sway_container *container,
237 return; 237 return;
238 } 238 }
239 if (container_is_floating(container)) { 239 if (container_is_floating(container)) {
240 container_move_to_workspace(container, destination->workspace); 240 container_move_to_workspace(container, destination->pending.workspace);
241 return; 241 return;
242 } 242 }
243 struct sway_workspace *old_workspace = container->workspace; 243 struct sway_workspace *old_workspace = container->pending.workspace;
244 244
245 container_detach(container); 245 container_detach(container);
246 container->width = container->height = 0; 246 container->pending.width = container->pending.height = 0;
247 container->width_fraction = container->height_fraction = 0; 247 container->width_fraction = container->height_fraction = 0;
248 248
249 if (destination->view) { 249 if (destination->view) {
@@ -256,12 +256,12 @@ static void container_move_to_container(struct sway_container *container,
256 ipc_event_window(container, "move"); 256 ipc_event_window(container, "move");
257 } 257 }
258 258
259 if (destination->workspace) { 259 if (destination->pending.workspace) {
260 workspace_focus_fullscreen(destination->workspace); 260 workspace_focus_fullscreen(destination->pending.workspace);
261 workspace_detect_urgent(destination->workspace); 261 workspace_detect_urgent(destination->pending.workspace);
262 } 262 }
263 263
264 if (old_workspace && old_workspace != destination->workspace) { 264 if (old_workspace && old_workspace != destination->pending.workspace) {
265 workspace_detect_urgent(old_workspace); 265 workspace_detect_urgent(old_workspace);
266 } 266 }
267} 267}
@@ -275,7 +275,7 @@ static bool container_move_to_next_output(struct sway_container *container,
275 if (!sway_assert(ws, "Expected output to have a workspace")) { 275 if (!sway_assert(ws, "Expected output to have a workspace")) {
276 return false; 276 return false;
277 } 277 }
278 switch (container->fullscreen_mode) { 278 switch (container->pending.fullscreen_mode) {
279 case FULLSCREEN_NONE: 279 case FULLSCREEN_NONE:
280 container_move_to_workspace_from_direction(container, ws, move_dir); 280 container_move_to_workspace_from_direction(container, ws, move_dir);
281 return true; 281 return true;
@@ -293,12 +293,12 @@ static bool container_move_to_next_output(struct sway_container *container,
293static bool container_move_in_direction(struct sway_container *container, 293static bool container_move_in_direction(struct sway_container *container,
294 enum wlr_direction move_dir) { 294 enum wlr_direction move_dir) {
295 // If moving a fullscreen view, only consider outputs 295 // If moving a fullscreen view, only consider outputs
296 switch (container->fullscreen_mode) { 296 switch (container->pending.fullscreen_mode) {
297 case FULLSCREEN_NONE: 297 case FULLSCREEN_NONE:
298 break; 298 break;
299 case FULLSCREEN_WORKSPACE: 299 case FULLSCREEN_WORKSPACE:
300 return container_move_to_next_output(container, 300 return container_move_to_next_output(container,
301 container->workspace->output, move_dir); 301 container->pending.workspace->output, move_dir);
302 case FULLSCREEN_GLOBAL: 302 case FULLSCREEN_GLOBAL:
303 return false; 303 return false;
304 } 304 }
@@ -317,26 +317,26 @@ static bool container_move_in_direction(struct sway_container *container,
317 while (!ancestor) { 317 while (!ancestor) {
318 // Don't allow containers to move out of their 318 // Don't allow containers to move out of their
319 // fullscreen or floating parent 319 // fullscreen or floating parent
320 if (current->fullscreen_mode || container_is_floating(current)) { 320 if (current->pending.fullscreen_mode || container_is_floating(current)) {
321 return false; 321 return false;
322 } 322 }
323 323
324 enum sway_container_layout parent_layout = container_parent_layout(current); 324 enum sway_container_layout parent_layout = container_parent_layout(current);
325 if (!is_parallel(parent_layout, move_dir)) { 325 if (!is_parallel(parent_layout, move_dir)) {
326 if (!current->parent) { 326 if (!current->pending.parent) {
327 // No parallel parent, so we reorient the workspace 327 // No parallel parent, so we reorient the workspace
328 current = workspace_wrap_children(current->workspace); 328 current = workspace_wrap_children(current->pending.workspace);
329 current->workspace->layout = 329 current->pending.workspace->layout =
330 move_dir == WLR_DIRECTION_LEFT || 330 move_dir == WLR_DIRECTION_LEFT ||
331 move_dir == WLR_DIRECTION_RIGHT ? 331 move_dir == WLR_DIRECTION_RIGHT ?
332 L_HORIZ : L_VERT; 332 L_HORIZ : L_VERT;
333 container->height = container->width = 0; 333 container->pending.height = container->pending.width = 0;
334 container->height_fraction = container->width_fraction = 0; 334 container->height_fraction = container->width_fraction = 0;
335 workspace_update_representation(current->workspace); 335 workspace_update_representation(current->pending.workspace);
336 wrapped = true; 336 wrapped = true;
337 } else { 337 } else {
338 // Keep looking for a parallel parent 338 // Keep looking for a parallel parent
339 current = current->parent; 339 current = current->pending.parent;
340 } 340 }
341 continue; 341 continue;
342 } 342 }
@@ -356,14 +356,14 @@ static bool container_move_in_direction(struct sway_container *container,
356 container_move_to_container_from_direction(container, 356 container_move_to_container_from_direction(container,
357 target, move_dir); 357 target, move_dir);
358 return true; 358 return true;
359 } else if (!container->parent) { 359 } else if (!container->pending.parent) {
360 // Container is at workspace level so we move it to the 360 // Container is at workspace level so we move it to the
361 // next workspace if possible 361 // next workspace if possible
362 return container_move_to_next_output(container, 362 return container_move_to_next_output(container,
363 current->workspace->output, move_dir); 363 current->pending.workspace->output, move_dir);
364 } else { 364 } else {
365 // Container has escaped its immediate parallel parent 365 // Container has escaped its immediate parallel parent
366 current = current->parent; 366 current = current->pending.parent;
367 continue; 367 continue;
368 } 368 }
369 } 369 }
@@ -377,31 +377,31 @@ static bool container_move_in_direction(struct sway_container *container,
377 container_move_to_container_from_direction(container, 377 container_move_to_container_from_direction(container,
378 target, move_dir); 378 target, move_dir);
379 return true; 379 return true;
380 } else if (!wrapped && !container->parent->parent && 380 } else if (!wrapped && !container->pending.parent->pending.parent &&
381 container->parent->children->length == 1) { 381 container->pending.parent->pending.children->length == 1) {
382 // Treat singleton children as if they are at workspace level like i3 382 // Treat singleton children as if they are at workspace level like i3
383 // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 383 // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367
384 return container_move_to_next_output(container, 384 return container_move_to_next_output(container,
385 ancestor->workspace->output, move_dir); 385 ancestor->pending.workspace->output, move_dir);
386 } else { 386 } else {
387 // Container will be promoted 387 // Container will be promoted
388 struct sway_container *old_parent = container->parent; 388 struct sway_container *old_parent = container->pending.parent;
389 if (ancestor->parent) { 389 if (ancestor->pending.parent) {
390 // Container will move in with its parent 390 // Container will move in with its parent
391 container_insert_child(ancestor->parent, container, 391 container_insert_child(ancestor->pending.parent, container,
392 index + (offs < 0 ? 0 : 1)); 392 index + (offs < 0 ? 0 : 1));
393 } else { 393 } else {
394 // Container will move to workspace level, 394 // Container will move to workspace level,
395 // may be re-split by workspace_layout 395 // may be re-split by workspace_layout
396 workspace_insert_tiling(ancestor->workspace, container, 396 workspace_insert_tiling(ancestor->pending.workspace, container,
397 index + (offs < 0 ? 0 : 1)); 397 index + (offs < 0 ? 0 : 1));
398 } 398 }
399 ancestor->height = ancestor->width = 0; 399 ancestor->pending.height = ancestor->pending.width = 0;
400 ancestor->height_fraction = ancestor->width_fraction = 0; 400 ancestor->height_fraction = ancestor->width_fraction = 0;
401 if (old_parent) { 401 if (old_parent) {
402 container_reap_empty(old_parent); 402 container_reap_empty(old_parent);
403 } 403 }
404 workspace_squash(container->workspace); 404 workspace_squash(container->pending.workspace);
405 return true; 405 return true;
406 } 406 }
407} 407}
@@ -427,14 +427,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
427 container = workspace_wrap_children(workspace); 427 container = workspace_wrap_children(workspace);
428 } 428 }
429 429
430 if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { 430 if (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
431 return cmd_results_new(CMD_FAILURE, 431 return cmd_results_new(CMD_FAILURE,
432 "Can't move fullscreen global container"); 432 "Can't move fullscreen global container");
433 } 433 }
434 434
435 struct sway_seat *seat = config->handler_context.seat; 435 struct sway_seat *seat = config->handler_context.seat;
436 struct sway_container *old_parent = container->parent; 436 struct sway_container *old_parent = container->pending.parent;
437 struct sway_workspace *old_ws = container->workspace; 437 struct sway_workspace *old_ws = container->pending.workspace;
438 struct sway_output *old_output = old_ws ? old_ws->output : NULL; 438 struct sway_output *old_output = old_ws ? old_ws->output : NULL;
439 struct sway_node *destination = NULL; 439 struct sway_node *destination = NULL;
440 440
@@ -508,7 +508,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
508 destination = dst ? &dst->node : &ws->node; 508 destination = dst ? &dst->node : &ws->node;
509 } else if (strcasecmp(argv[0], "output") == 0) { 509 } else if (strcasecmp(argv[0], "output") == 0) {
510 struct sway_output *new_output = output_in_direction(argv[1], 510 struct sway_output *new_output = output_in_direction(argv[1],
511 old_output, container->x, container->y); 511 old_output, container->pending.x, container->pending.y);
512 if (!new_output) { 512 if (!new_output) {
513 return cmd_results_new(CMD_FAILURE, 513 return cmd_results_new(CMD_FAILURE,
514 "Can't find output with name/direction '%s'", argv[1]); 514 "Can't find output with name/direction '%s'", argv[1]);
@@ -706,12 +706,12 @@ static struct cmd_results *cmd_move_in_direction(
706 "Cannot move workspaces in a direction"); 706 "Cannot move workspaces in a direction");
707 } 707 }
708 if (container_is_floating(container)) { 708 if (container_is_floating(container)) {
709 if (container->fullscreen_mode) { 709 if (container->pending.fullscreen_mode) {
710 return cmd_results_new(CMD_FAILURE, 710 return cmd_results_new(CMD_FAILURE,
711 "Cannot move fullscreen floating container"); 711 "Cannot move fullscreen floating container");
712 } 712 }
713 double lx = container->x; 713 double lx = container->pending.x;
714 double ly = container->y; 714 double ly = container->pending.y;
715 switch (direction) { 715 switch (direction) {
716 case WLR_DIRECTION_LEFT: 716 case WLR_DIRECTION_LEFT:
717 lx -= move_amt; 717 lx -= move_amt;
@@ -729,8 +729,8 @@ static struct cmd_results *cmd_move_in_direction(
729 container_floating_move_to(container, lx, ly); 729 container_floating_move_to(container, lx, ly);
730 return cmd_results_new(CMD_SUCCESS, NULL); 730 return cmd_results_new(CMD_SUCCESS, NULL);
731 } 731 }
732 struct sway_workspace *old_ws = container->workspace; 732 struct sway_workspace *old_ws = container->pending.workspace;
733 struct sway_container *old_parent = container->parent; 733 struct sway_container *old_parent = container->pending.parent;
734 734
735 if (!container_move_in_direction(container, direction)) { 735 if (!container_move_in_direction(container, direction)) {
736 // Container didn't move 736 // Container didn't move
@@ -744,7 +744,7 @@ static struct cmd_results *cmd_move_in_direction(
744 workspace_consider_destroy(old_ws); 744 workspace_consider_destroy(old_ws);
745 } 745 }
746 746
747 struct sway_workspace *new_ws = container->workspace; 747 struct sway_workspace *new_ws = container->pending.workspace;
748 748
749 if (root->fullscreen_global) { 749 if (root->fullscreen_global) {
750 arrange_root(); 750 arrange_root();
@@ -781,22 +781,22 @@ static struct cmd_results *cmd_move_to_position_pointer(
781 } 781 }
782 struct wlr_cursor *cursor = seat->cursor->cursor; 782 struct wlr_cursor *cursor = seat->cursor->cursor;
783 /* Determine where to put the window. */ 783 /* Determine where to put the window. */
784 double lx = cursor->x - container->width / 2; 784 double lx = cursor->x - container->pending.width / 2;
785 double ly = cursor->y - container->height / 2; 785 double ly = cursor->y - container->pending.height / 2;
786 786
787 /* Correct target coordinates to be in bounds (on screen). */ 787 /* Correct target coordinates to be in bounds (on screen). */
788 struct wlr_output *output = wlr_output_layout_output_at( 788 struct wlr_output *output = wlr_output_layout_output_at(
789 root->output_layout, cursor->x, cursor->y); 789 root->output_layout, cursor->x, cursor->y);
790 if (output) { 790 if (output) {
791 struct wlr_box *box = 791 struct wlr_box box;
792 wlr_output_layout_get_box(root->output_layout, output); 792 wlr_output_layout_get_box(root->output_layout, output, &box);
793 lx = fmax(lx, box->x); 793 lx = fmax(lx, box.x);
794 ly = fmax(ly, box->y); 794 ly = fmax(ly, box.y);
795 if (lx + container->width > box->x + box->width) { 795 if (lx + container->pending.width > box.x + box.width) {
796 lx = box->x + box->width - container->width; 796 lx = box.x + box.width - container->pending.width;
797 } 797 }
798 if (ly + container->height > box->y + box->height) { 798 if (ly + container->pending.height > box.y + box.height) {
799 ly = box->y + box->height - container->height; 799 ly = box.y + box.height - container->pending.height;
800 } 800 }
801 } 801 }
802 802
@@ -846,16 +846,16 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
846 } else if (strcmp(argv[0], "center") == 0) { 846 } else if (strcmp(argv[0], "center") == 0) {
847 double lx, ly; 847 double lx, ly;
848 if (absolute) { 848 if (absolute) {
849 lx = root->x + (root->width - container->width) / 2; 849 lx = root->x + (root->width - container->pending.width) / 2;
850 ly = root->y + (root->height - container->height) / 2; 850 ly = root->y + (root->height - container->pending.height) / 2;
851 } else { 851 } else {
852 struct sway_workspace *ws = container->workspace; 852 struct sway_workspace *ws = container->pending.workspace;
853 if (!ws) { 853 if (!ws) {
854 struct sway_seat *seat = config->handler_context.seat; 854 struct sway_seat *seat = config->handler_context.seat;
855 ws = seat_get_focused_workspace(seat); 855 ws = seat_get_focused_workspace(seat);
856 } 856 }
857 lx = ws->x + (ws->width - container->width) / 2; 857 lx = ws->x + (ws->width - container->pending.width) / 2;
858 ly = ws->y + (ws->height - container->height) / 2; 858 ly = ws->y + (ws->height - container->pending.height) / 2;
859 } 859 }
860 container_floating_move_to(container, lx, ly); 860 container_floating_move_to(container, lx, ly);
861 return cmd_results_new(CMD_SUCCESS, NULL); 861 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -874,6 +874,10 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
874 return cmd_results_new(CMD_INVALID, "Invalid x position specified"); 874 return cmd_results_new(CMD_INVALID, "Invalid x position specified");
875 } 875 }
876 876
877 if (argc < 1) {
878 return cmd_results_new(CMD_FAILURE, expected_position_syntax);
879 }
880
877 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 881 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
878 // Y direction 882 // Y direction
879 num_consumed_args = parse_movement_amount(argc, argv, &ly); 883 num_consumed_args = parse_movement_amount(argc, argv, &ly);
@@ -886,7 +890,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
886 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 890 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
887 } 891 }
888 892
889 struct sway_workspace *ws = container->workspace; 893 struct sway_workspace *ws = container->pending.workspace;
890 if (!ws) { 894 if (!ws) {
891 struct sway_seat *seat = config->handler_context.seat; 895 struct sway_seat *seat = config->handler_context.seat;
892 ws = seat_get_focused_workspace(seat); 896 ws = seat_get_focused_workspace(seat);
@@ -960,14 +964,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) {
960 // If the container is in a floating split container, 964 // If the container is in a floating split container,
961 // operate on the split container instead of the child. 965 // operate on the split container instead of the child.
962 if (container_is_floating_or_child(con)) { 966 if (container_is_floating_or_child(con)) {
963 while (con->parent) { 967 while (con->pending.parent) {
964 con = con->parent; 968 con = con->pending.parent;
965 } 969 }
966 } 970 }
967 971
968 if (!con->scratchpad) { 972 if (!con->scratchpad) {
969 root_scratchpad_add_container(con, NULL); 973 root_scratchpad_add_container(con, NULL);
970 } else if (con->workspace) { 974 } else if (con->pending.workspace) {
971 root_scratchpad_hide(con); 975 root_scratchpad_hide(con);
972 } 976 }
973 return cmd_results_new(CMD_SUCCESS, NULL); 977 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 5186a2ba..c102344d 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -6,7 +6,7 @@
6#include "log.h" 6#include "log.h"
7 7
8// must be in order for the bsearch 8// must be in order for the bsearch
9static struct cmd_handler output_handlers[] = { 9static const struct cmd_handler output_handlers[] = {
10 { "adaptive_sync", output_cmd_adaptive_sync }, 10 { "adaptive_sync", output_cmd_adaptive_sync },
11 { "background", output_cmd_background }, 11 { "background", output_cmd_background },
12 { "bg", output_cmd_background }, 12 { "bg", output_cmd_background },
@@ -15,8 +15,11 @@ static struct cmd_handler output_handlers[] = {
15 { "enable", output_cmd_enable }, 15 { "enable", output_cmd_enable },
16 { "max_render_time", output_cmd_max_render_time }, 16 { "max_render_time", output_cmd_max_render_time },
17 { "mode", output_cmd_mode }, 17 { "mode", output_cmd_mode },
18 { "modeline", output_cmd_modeline },
18 { "pos", output_cmd_position }, 19 { "pos", output_cmd_position },
19 { "position", output_cmd_position }, 20 { "position", output_cmd_position },
21 { "power", output_cmd_power },
22 { "render_bit_depth", output_cmd_render_bit_depth },
20 { "res", output_cmd_mode }, 23 { "res", output_cmd_mode },
21 { "resolution", output_cmd_mode }, 24 { "resolution", output_cmd_mode },
22 { "scale", output_cmd_scale }, 25 { "scale", output_cmd_scale },
@@ -32,9 +35,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
32 return error; 35 return error;
33 } 36 }
34 37
35 // The NOOP-1 output is a dummy output used when there's no outputs 38 // The HEADLESS-1 output is a dummy output used when there's no outputs
36 // connected. It should never be configured. 39 // connected. It should never be configured.
37 if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { 40 if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
38 return cmd_results_new(CMD_FAILURE, 41 return cmd_results_new(CMD_FAILURE,
39 "Refusing to configure the no op output"); 42 "Refusing to configure the no op output");
40 } 43 }
@@ -51,7 +54,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
51 if (!sway_output) { 54 if (!sway_output) {
52 return cmd_results_new(CMD_FAILURE, "Unknown output"); 55 return cmd_results_new(CMD_FAILURE, "Unknown output");
53 } 56 }
54 if (sway_output == root->noop_output) { 57 if (sway_output == root->fallback_output) {
55 return cmd_results_new(CMD_FAILURE, 58 return cmd_results_new(CMD_FAILURE,
56 "Refusing to configure the no op output"); 59 "Refusing to configure the no op output");
57 } 60 }
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 68ee9fe1..67f212ff 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -102,19 +102,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
102 } 102 }
103 103
104 char *conf_path = dirname(conf); 104 char *conf_path = dirname(conf);
105 char *rel_path = src; 105 char *real_src = malloc(strlen(conf_path) + strlen(src) + 2);
106 src = malloc(strlen(conf_path) + strlen(src) + 2); 106 if (!real_src) {
107 if (!src) { 107 free(src);
108 free(rel_path);
109 free(conf); 108 free(conf);
110 sway_log(SWAY_ERROR, "Unable to allocate memory"); 109 sway_log(SWAY_ERROR, "Unable to allocate memory");
111 return cmd_results_new(CMD_FAILURE, 110 return cmd_results_new(CMD_FAILURE,
112 "Unable to allocate resources"); 111 "Unable to allocate resources");
113 } 112 }
114 113
115 sprintf(src, "%s/%s", conf_path, rel_path); 114 snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src);
116 free(rel_path); 115 free(src);
117 free(conf); 116 free(conf);
117 src = real_src;
118 } 118 }
119 119
120 bool can_access = access(src, F_OK) != -1; 120 bool can_access = access(src, F_OK) != -1;
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 9d75a80e..c7adbd58 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,22 +1,8 @@
1#include "log.h"
1#include "sway/commands.h" 2#include "sway/commands.h"
2#include "sway/config.h"
3#include "util.h"
4 3
5struct cmd_results *output_cmd_dpms(int argc, char **argv) { 4struct cmd_results *output_cmd_dpms(int argc, char **argv) {
6 if (!config->handler_context.output_config) { 5 sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, "
7 return cmd_results_new(CMD_FAILURE, "Missing output config"); 6 "use \"output power\" instead");
8 } 7 return output_cmd_power(argc, argv);
9 if (!argc) {
10 return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
11 }
12
13 if (parse_boolean(argv[0], true)) {
14 config->handler_context.output_config->dpms_state = DPMS_ON;
15 } else {
16 config->handler_context.output_config->dpms_state = DPMS_OFF;
17 }
18
19 config->handler_context.leftovers.argc = argc - 1;
20 config->handler_context.leftovers.argv = argv + 1;
21 return NULL;
22} 8}
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c
index 5b710713..019d625a 100644
--- a/sway/commands/output/mode.c
+++ b/sway/commands/output/mode.c
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
20 output->custom_mode = 0; 20 output->custom_mode = 0;
21 } 21 }
22 22
23 // Reset custom modeline, if any
24 output->drm_mode.type = 0;
25
23 char *end; 26 char *end;
24 output->width = strtol(*argv, &end, 10); 27 output->width = strtol(*argv, &end, 10);
25 if (*end) { 28 if (*end) {
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
58 return NULL; 61 return NULL;
59} 62}
60 63
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/reload.c b/sway/commands/reload.c
index 3c994d54..76f14bba 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -48,7 +48,6 @@ static void do_reload(void *data) {
48 } 48 }
49 list_free_items_and_destroy(bar_ids); 49 list_free_items_and_destroy(bar_ids);
50 50
51 config_update_font_height(true);
52 root_for_each_container(rebuild_textures_iterator, NULL); 51 root_for_each_container(rebuild_textures_iterator, NULL);
53 52
54 arrange_root(); 53 arrange_root();
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ca36e858..425069de 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -57,7 +57,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
57 (allow_last || index < siblings->length - 1)) { 57 (allow_last || index < siblings->length - 1)) {
58 return con; 58 return con;
59 } 59 }
60 con = con->parent; 60 con = con->pending.parent;
61 } 61 }
62 62
63 return NULL; 63 return NULL;
@@ -115,13 +115,13 @@ void container_resize_tiled(struct sway_container *con,
115 int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; 115 int sibling_amount = prev ? ceil((double)amount / 2.0) : amount;
116 116
117 if (is_horizontal(axis)) { 117 if (is_horizontal(axis)) {
118 if (con->width + amount < MIN_SANE_W) { 118 if (con->pending.width + amount < MIN_SANE_W) {
119 return; 119 return;
120 } 120 }
121 if (next->width - sibling_amount < MIN_SANE_W) { 121 if (next->pending.width - sibling_amount < MIN_SANE_W) {
122 return; 122 return;
123 } 123 }
124 if (prev && prev->width - sibling_amount < MIN_SANE_W) { 124 if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) {
125 return; 125 return;
126 } 126 }
127 if (con->child_total_width <= 0) { 127 if (con->child_total_width <= 0) {
@@ -133,7 +133,7 @@ void container_resize_tiled(struct sway_container *con,
133 list_t *siblings = container_get_siblings(con); 133 list_t *siblings = container_get_siblings(con);
134 for (int i = 0; i < siblings->length; ++i) { 134 for (int i = 0; i < siblings->length; ++i) {
135 struct sway_container *con = siblings->items[i]; 135 struct sway_container *con = siblings->items[i];
136 con->width_fraction = con->width / con->child_total_width; 136 con->width_fraction = con->pending.width / con->child_total_width;
137 } 137 }
138 138
139 double amount_fraction = (double)amount / con->child_total_width; 139 double amount_fraction = (double)amount / con->child_total_width;
@@ -146,13 +146,13 @@ void container_resize_tiled(struct sway_container *con,
146 prev->width_fraction -= sibling_amount_fraction; 146 prev->width_fraction -= sibling_amount_fraction;
147 } 147 }
148 } else { 148 } else {
149 if (con->height + amount < MIN_SANE_H) { 149 if (con->pending.height + amount < MIN_SANE_H) {
150 return; 150 return;
151 } 151 }
152 if (next->height - sibling_amount < MIN_SANE_H) { 152 if (next->pending.height - sibling_amount < MIN_SANE_H) {
153 return; 153 return;
154 } 154 }
155 if (prev && prev->height - sibling_amount < MIN_SANE_H) { 155 if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) {
156 return; 156 return;
157 } 157 }
158 if (con->child_total_height <= 0) { 158 if (con->child_total_height <= 0) {
@@ -164,7 +164,7 @@ void container_resize_tiled(struct sway_container *con,
164 list_t *siblings = container_get_siblings(con); 164 list_t *siblings = container_get_siblings(con);
165 for (int i = 0; i < siblings->length; ++i) { 165 for (int i = 0; i < siblings->length; ++i) {
166 struct sway_container *con = siblings->items[i]; 166 struct sway_container *con = siblings->items[i];
167 con->height_fraction = con->height / con->child_total_height; 167 con->height_fraction = con->pending.height / con->child_total_height;
168 } 168 }
169 169
170 double amount_fraction = (double)amount / con->child_total_height; 170 double amount_fraction = (double)amount / con->child_total_height;
@@ -178,10 +178,10 @@ void container_resize_tiled(struct sway_container *con,
178 } 178 }
179 } 179 }
180 180
181 if (con->parent) { 181 if (con->pending.parent) {
182 arrange_container(con->parent); 182 arrange_container(con->pending.parent);
183 } else { 183 } else {
184 arrange_workspace(con->workspace); 184 arrange_workspace(con->pending.workspace);
185 } 185 }
186} 186}
187 187
@@ -203,15 +203,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis,
203 int min_width, max_width, min_height, max_height; 203 int min_width, max_width, min_height, max_height;
204 floating_calculate_constraints(&min_width, &max_width, 204 floating_calculate_constraints(&min_width, &max_width,
205 &min_height, &max_height); 205 &min_height, &max_height);
206 if (con->width + grow_width < min_width) { 206 if (con->pending.width + grow_width < min_width) {
207 grow_width = min_width - con->width; 207 grow_width = min_width - con->pending.width;
208 } else if (con->width + grow_width > max_width) { 208 } else if (con->pending.width + grow_width > max_width) {
209 grow_width = max_width - con->width; 209 grow_width = max_width - con->pending.width;
210 } 210 }
211 if (con->height + grow_height < min_height) { 211 if (con->pending.height + grow_height < min_height) {
212 grow_height = min_height - con->height; 212 grow_height = min_height - con->pending.height;
213 } else if (con->height + grow_height > max_height) { 213 } else if (con->pending.height + grow_height > max_height) {
214 grow_height = max_height - con->height; 214 grow_height = max_height - con->pending.height;
215 } 215 }
216 int grow_x = 0, grow_y = 0; 216 int grow_x = 0, grow_y = 0;
217 217
@@ -227,15 +227,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis,
227 if (grow_width == 0 && grow_height == 0) { 227 if (grow_width == 0 && grow_height == 0) {
228 return cmd_results_new(CMD_INVALID, "Cannot resize any further"); 228 return cmd_results_new(CMD_INVALID, "Cannot resize any further");
229 } 229 }
230 con->x += grow_x; 230 con->pending.x += grow_x;
231 con->y += grow_y; 231 con->pending.y += grow_y;
232 con->width += grow_width; 232 con->pending.width += grow_width;
233 con->height += grow_height; 233 con->pending.height += grow_height;
234 234
235 con->content_x += grow_x; 235 con->pending.content_x += grow_x;
236 con->content_y += grow_y; 236 con->pending.content_y += grow_y;
237 con->content_width += grow_width; 237 con->pending.content_width += grow_width;
238 con->content_height += grow_height; 238 con->pending.content_height += grow_height;
239 239
240 arrange_container(con); 240 arrange_container(con);
241 241
@@ -256,9 +256,9 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
256 float pct = amount->amount / 100.0f; 256 float pct = amount->amount / 100.0f;
257 257
258 if (is_horizontal(axis)) { 258 if (is_horizontal(axis)) {
259 amount->amount = (float)current->width * pct; 259 amount->amount = (float)current->pending.width * pct;
260 } else { 260 } else {
261 amount->amount = (float)current->height * pct; 261 amount->amount = (float)current->pending.height * pct;
262 } 262 }
263 } 263 }
264 264
@@ -281,20 +281,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
281 if (width->unit == MOVEMENT_UNIT_PPT || 281 if (width->unit == MOVEMENT_UNIT_PPT ||
282 width->unit == MOVEMENT_UNIT_DEFAULT) { 282 width->unit == MOVEMENT_UNIT_DEFAULT) {
283 // Convert to px 283 // Convert to px
284 struct sway_container *parent = con->parent; 284 struct sway_container *parent = con->pending.parent;
285 while (parent && parent->layout != L_HORIZ) { 285 while (parent && parent->pending.layout != L_HORIZ) {
286 parent = parent->parent; 286 parent = parent->pending.parent;
287 } 287 }
288 if (parent) { 288 if (parent) {
289 width->amount = parent->width * width->amount / 100; 289 width->amount = parent->pending.width * width->amount / 100;
290 } else { 290 } else {
291 width->amount = con->workspace->width * width->amount / 100; 291 width->amount = con->pending.workspace->width * width->amount / 100;
292 } 292 }
293 width->unit = MOVEMENT_UNIT_PX; 293 width->unit = MOVEMENT_UNIT_PX;
294 } 294 }
295 if (width->unit == MOVEMENT_UNIT_PX) { 295 if (width->unit == MOVEMENT_UNIT_PX) {
296 container_resize_tiled(con, AXIS_HORIZONTAL, 296 container_resize_tiled(con, AXIS_HORIZONTAL,
297 width->amount - con->width); 297 width->amount - con->pending.width);
298 } 298 }
299 } 299 }
300 300
@@ -302,20 +302,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
302 if (height->unit == MOVEMENT_UNIT_PPT || 302 if (height->unit == MOVEMENT_UNIT_PPT ||
303 height->unit == MOVEMENT_UNIT_DEFAULT) { 303 height->unit == MOVEMENT_UNIT_DEFAULT) {
304 // Convert to px 304 // Convert to px
305 struct sway_container *parent = con->parent; 305 struct sway_container *parent = con->pending.parent;
306 while (parent && parent->layout != L_VERT) { 306 while (parent && parent->pending.layout != L_VERT) {
307 parent = parent->parent; 307 parent = parent->pending.parent;
308 } 308 }
309 if (parent) { 309 if (parent) {
310 height->amount = parent->height * height->amount / 100; 310 height->amount = parent->pending.height * height->amount / 100;
311 } else { 311 } else {
312 height->amount = con->workspace->height * height->amount / 100; 312 height->amount = con->pending.workspace->height * height->amount / 100;
313 } 313 }
314 height->unit = MOVEMENT_UNIT_PX; 314 height->unit = MOVEMENT_UNIT_PX;
315 } 315 }
316 if (height->unit == MOVEMENT_UNIT_PX) { 316 if (height->unit == MOVEMENT_UNIT_PX) {
317 container_resize_tiled(con, AXIS_VERTICAL, 317 container_resize_tiled(con, AXIS_VERTICAL,
318 height->amount - con->height); 318 height->amount - con->pending.height);
319 } 319 }
320 } 320 }
321 321
@@ -339,15 +339,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
339 "Cannot resize a hidden scratchpad container by ppt"); 339 "Cannot resize a hidden scratchpad container by ppt");
340 } 340 }
341 // Convert to px 341 // Convert to px
342 width->amount = con->workspace->width * width->amount / 100; 342 width->amount = con->pending.workspace->width * width->amount / 100;
343 width->unit = MOVEMENT_UNIT_PX; 343 width->unit = MOVEMENT_UNIT_PX;
344 // Falls through 344 // Falls through
345 case MOVEMENT_UNIT_PX: 345 case MOVEMENT_UNIT_PX:
346 case MOVEMENT_UNIT_DEFAULT: 346 case MOVEMENT_UNIT_DEFAULT:
347 width->amount = fmax(min_width, fmin(width->amount, max_width)); 347 width->amount = fmax(min_width, fmin(width->amount, max_width));
348 grow_width = width->amount - con->width; 348 grow_width = width->amount - con->pending.width;
349 con->x -= grow_width / 2; 349 con->pending.x -= grow_width / 2;
350 con->width = width->amount; 350 con->pending.width = width->amount;
351 break; 351 break;
352 case MOVEMENT_UNIT_INVALID: 352 case MOVEMENT_UNIT_INVALID:
353 sway_assert(false, "invalid width unit"); 353 sway_assert(false, "invalid width unit");
@@ -363,15 +363,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
363 "Cannot resize a hidden scratchpad container by ppt"); 363 "Cannot resize a hidden scratchpad container by ppt");
364 } 364 }
365 // Convert to px 365 // Convert to px
366 height->amount = con->workspace->height * height->amount / 100; 366 height->amount = con->pending.workspace->height * height->amount / 100;
367 height->unit = MOVEMENT_UNIT_PX; 367 height->unit = MOVEMENT_UNIT_PX;
368 // Falls through 368 // Falls through
369 case MOVEMENT_UNIT_PX: 369 case MOVEMENT_UNIT_PX:
370 case MOVEMENT_UNIT_DEFAULT: 370 case MOVEMENT_UNIT_DEFAULT:
371 height->amount = fmax(min_height, fmin(height->amount, max_height)); 371 height->amount = fmax(min_height, fmin(height->amount, max_height));
372 grow_height = height->amount - con->height; 372 grow_height = height->amount - con->pending.height;
373 con->y -= grow_height / 2; 373 con->pending.y -= grow_height / 2;
374 con->height = height->amount; 374 con->pending.height = height->amount;
375 break; 375 break;
376 case MOVEMENT_UNIT_INVALID: 376 case MOVEMENT_UNIT_INVALID:
377 sway_assert(false, "invalid height unit"); 377 sway_assert(false, "invalid height unit");
@@ -379,10 +379,10 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
379 } 379 }
380 } 380 }
381 381
382 con->content_x -= grow_width / 2; 382 con->pending.content_x -= grow_width / 2;
383 con->content_y -= grow_height / 2; 383 con->pending.content_y -= grow_height / 2;
384 con->content_width += grow_width; 384 con->pending.content_width += grow_width;
385 con->content_height += grow_height; 385 con->pending.content_height += grow_height;
386 386
387 arrange_container(con); 387 arrange_container(con);
388 388
@@ -437,10 +437,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
437 // If 0, don't resize that dimension 437 // If 0, don't resize that dimension
438 struct sway_container *con = config->handler_context.container; 438 struct sway_container *con = config->handler_context.container;
439 if (width.amount <= 0) { 439 if (width.amount <= 0) {
440 width.amount = con->width; 440 width.amount = con->pending.width;
441 } 441 }
442 if (height.amount <= 0) { 442 if (height.amount <= 0) {
443 height.amount = con->height; 443 height.amount = con->pending.height;
444 } 444 }
445 445
446 if (container_is_floating(con)) { 446 if (container_is_floating(con)) {
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
index 34871bc6..c995f2f0 100644
--- a/sway/commands/scratchpad.c
+++ b/sway/commands/scratchpad.c
@@ -21,8 +21,8 @@ static void scratchpad_toggle_auto(void) {
21 // If the focus is in a floating split container, 21 // If the focus is in a floating split container,
22 // operate on the split container instead of the child. 22 // operate on the split container instead of the child.
23 if (focus && container_is_floating_or_child(focus)) { 23 if (focus && container_is_floating_or_child(focus)) {
24 while (focus->parent) { 24 while (focus->pending.parent) {
25 focus = focus->parent; 25 focus = focus->pending.parent;
26 } 26 }
27 } 27 }
28 28
@@ -52,7 +52,7 @@ static void scratchpad_toggle_auto(void) {
52 // In this case we move it to the current workspace. 52 // In this case we move it to the current workspace.
53 for (int i = 0; i < root->scratchpad->length; ++i) { 53 for (int i = 0; i < root->scratchpad->length; ++i) {
54 struct sway_container *con = root->scratchpad->items[i]; 54 struct sway_container *con = root->scratchpad->items[i];
55 if (con->parent) { 55 if (con->pending.parent) {
56 sway_log(SWAY_DEBUG, 56 sway_log(SWAY_DEBUG,
57 "Moving a visible scratchpad window (%s) to this workspace", 57 "Moving a visible scratchpad window (%s) to this workspace",
58 con->title); 58 con->title);
@@ -80,7 +80,7 @@ static void scratchpad_toggle_container(struct sway_container *con) {
80 struct sway_seat *seat = input_manager_current_seat(); 80 struct sway_seat *seat = input_manager_current_seat();
81 struct sway_workspace *ws = seat_get_focused_workspace(seat); 81 struct sway_workspace *ws = seat_get_focused_workspace(seat);
82 // Check if it matches a currently visible scratchpad window and hide it. 82 // Check if it matches a currently visible scratchpad window and hide it.
83 if (con->workspace && ws == con->workspace) { 83 if (con->pending.workspace && ws == con->pending.workspace) {
84 root_scratchpad_hide(con); 84 root_scratchpad_hide(con);
85 return; 85 return;
86 } 86 }
@@ -105,21 +105,22 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
105 return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); 105 return cmd_results_new(CMD_INVALID, "Scratchpad is empty");
106 } 106 }
107 107
108 if (config->handler_context.using_criteria) { 108 if (config->handler_context.node_overridden) {
109 struct sway_container *con = config->handler_context.container; 109 struct sway_container *con = config->handler_context.container;
110 110
111 // If the container is in a floating split container, 111 // If the container is in a floating split container,
112 // operate on the split container instead of the child. 112 // operate on the split container instead of the child.
113 if (container_is_floating_or_child(con)) { 113 if (con && container_is_floating_or_child(con)) {
114 while (con->parent) { 114 while (con->pending.parent) {
115 con = con->parent; 115 con = con->pending.parent;
116 } 116 }
117 } 117 }
118 118
119 // If using criteria, this command is executed for every container which 119 // If using criteria, this command is executed for every container which
120 // matches the criteria. If this container isn't in the scratchpad, 120 // matches the criteria. If this container isn't in the scratchpad,
121 // we'll just silently return a success. 121 // we'll just silently return a success. The same is true if the
122 if (!con->scratchpad) { 122 // overridden node is not a container.
123 if (!con || !con->scratchpad) {
123 return cmd_results_new(CMD_SUCCESS, NULL); 124 return cmd_results_new(CMD_SUCCESS, NULL);
124 } 125 }
125 scratchpad_toggle_container(con); 126 scratchpad_toggle_container(con);
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 84c6ba53..2d197b69 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -8,13 +8,13 @@
8 8
9// must be in order for the bsearch 9// must be in order for the bsearch
10// these handlers perform actions on the seat 10// these handlers perform actions on the seat
11static struct cmd_handler seat_action_handlers[] = { 11static const struct cmd_handler seat_action_handlers[] = {
12 { "cursor", seat_cmd_cursor }, 12 { "cursor", seat_cmd_cursor },
13}; 13};
14 14
15// must be in order for the bsearch 15// must be in order for the bsearch
16// these handlers alter the seat config 16// these handlers alter the seat config
17static struct cmd_handler seat_handlers[] = { 17static const struct cmd_handler seat_handlers[] = {
18 { "attach", seat_cmd_attach }, 18 { "attach", seat_cmd_attach },
19 { "fallback", seat_cmd_fallback }, 19 { "fallback", seat_cmd_fallback },
20 { "hide_cursor", seat_cmd_hide_cursor }, 20 { "hide_cursor", seat_cmd_hide_cursor },
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 7615eef9..00bfdab6 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -12,7 +12,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) {
12 if (!config->handler_context.seat_config) { 12 if (!config->handler_context.seat_config) {
13 return cmd_results_new(CMD_FAILURE, "No seat defined"); 13 return cmd_results_new(CMD_FAILURE, "No seat defined");
14 } 14 }
15 if (config->reading) { 15 if (!config->active) {
16 return cmd_results_new(CMD_DEFER, NULL); 16 return cmd_results_new(CMD_DEFER, NULL);
17 } 17 }
18 18
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 749235eb..504a9f5e 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -111,8 +111,8 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 111 : WLR_AXIS_ORIENTATION_HORIZONTAL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 113 ? -1 : 1;
114 struct wlr_event_pointer_axis event = { 114 struct wlr_pointer_axis_event event = {
115 .device = NULL, 115 .pointer = NULL,
116 .time_msec = 0, 116 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 117 .source = WLR_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 118 .orientation = orientation,
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index b27f9ccd..a6d165dc 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
15 return error; 15 return error;
16 } 16 }
17 17
18 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); 18 if (strcmp(argv[0], "inverse_outer") == 0) {
19 config->smart_gaps = SMART_GAPS_INVERSE_OUTER;
20 } else {
21 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps)
22 ? SMART_GAPS_ON : SMART_GAPS_OFF;
23 }
19 24
20 arrange_root(); 25 arrange_root();
21 26
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 782bab02..c8a2cfc1 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -14,7 +14,7 @@ static struct cmd_results *do_split(int layout) {
14 struct sway_workspace *ws = config->handler_context.workspace; 14 struct sway_workspace *ws = config->handler_context.workspace;
15 if (con) { 15 if (con) {
16 if (container_is_scratchpad_hidden_or_child(con) && 16 if (container_is_scratchpad_hidden_or_child(con) &&
17 con->fullscreen_mode != FULLSCREEN_GLOBAL) { 17 con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
18 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
19 "Cannot split a hidden scratchpad container"); 19 "Cannot split a hidden scratchpad container");
20 } 20 }
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) {
32 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
33} 33}
34 34
35static struct cmd_results *do_unsplit() {
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/sticky.c b/sway/commands/sticky.c
index 3c93a276..9b09a0f9 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -29,14 +29,14 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
29 !container_is_scratchpad_hidden(container)) { 29 !container_is_scratchpad_hidden(container)) {
30 // move container to active workspace 30 // move container to active workspace
31 struct sway_workspace *active_workspace = 31 struct sway_workspace *active_workspace =
32 output_get_active_workspace(container->workspace->output); 32 output_get_active_workspace(container->pending.workspace->output);
33 if (!sway_assert(active_workspace, 33 if (!sway_assert(active_workspace,
34 "Expected output to have a workspace")) { 34 "Expected output to have a workspace")) {
35 return cmd_results_new(CMD_FAILURE, 35 return cmd_results_new(CMD_FAILURE,
36 "Expected output to have a workspace"); 36 "Expected output to have a workspace");
37 } 37 }
38 if (container->workspace != active_workspace) { 38 if (container->pending.workspace != active_workspace) {
39 struct sway_workspace *old_workspace = container->workspace; 39 struct sway_workspace *old_workspace = container->pending.workspace;
40 container_detach(container); 40 container_detach(container);
41 workspace_add_floating(active_workspace, container); 41 workspace_add_floating(active_workspace, container);
42 container_handle_fullscreen_reparent(container); 42 container_handle_fullscreen_reparent(container);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index a7f9691b..9355944d 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -16,46 +16,46 @@ static const char expected_syntax[] =
16static void swap_places(struct sway_container *con1, 16static void swap_places(struct sway_container *con1,
17 struct sway_container *con2) { 17 struct sway_container *con2) {
18 struct sway_container *temp = malloc(sizeof(struct sway_container)); 18 struct sway_container *temp = malloc(sizeof(struct sway_container));
19 temp->x = con1->x; 19 temp->pending.x = con1->pending.x;
20 temp->y = con1->y; 20 temp->pending.y = con1->pending.y;
21 temp->width = con1->width; 21 temp->pending.width = con1->pending.width;
22 temp->height = con1->height; 22 temp->pending.height = con1->pending.height;
23 temp->width_fraction = con1->width_fraction; 23 temp->width_fraction = con1->width_fraction;
24 temp->height_fraction = con1->height_fraction; 24 temp->height_fraction = con1->height_fraction;
25 temp->parent = con1->parent; 25 temp->pending.parent = con1->pending.parent;
26 temp->workspace = con1->workspace; 26 temp->pending.workspace = con1->pending.workspace;
27 bool temp_floating = container_is_floating(con1); 27 bool temp_floating = container_is_floating(con1);
28 28
29 con1->x = con2->x; 29 con1->pending.x = con2->pending.x;
30 con1->y = con2->y; 30 con1->pending.y = con2->pending.y;
31 con1->width = con2->width; 31 con1->pending.width = con2->pending.width;
32 con1->height = con2->height; 32 con1->pending.height = con2->pending.height;
33 con1->width_fraction = con2->width_fraction; 33 con1->width_fraction = con2->width_fraction;
34 con1->height_fraction = con2->height_fraction; 34 con1->height_fraction = con2->height_fraction;
35 35
36 con2->x = temp->x; 36 con2->pending.x = temp->pending.x;
37 con2->y = temp->y; 37 con2->pending.y = temp->pending.y;
38 con2->width = temp->width; 38 con2->pending.width = temp->pending.width;
39 con2->height = temp->height; 39 con2->pending.height = temp->pending.height;
40 con2->width_fraction = temp->width_fraction; 40 con2->width_fraction = temp->width_fraction;
41 con2->height_fraction = temp->height_fraction; 41 con2->height_fraction = temp->height_fraction;
42 42
43 int temp_index = container_sibling_index(con1); 43 int temp_index = container_sibling_index(con1);
44 if (con2->parent) { 44 if (con2->pending.parent) {
45 container_insert_child(con2->parent, con1, 45 container_insert_child(con2->pending.parent, con1,
46 container_sibling_index(con2)); 46 container_sibling_index(con2));
47 } else if (container_is_floating(con2)) { 47 } else if (container_is_floating(con2)) {
48 workspace_add_floating(con2->workspace, con1); 48 workspace_add_floating(con2->pending.workspace, con1);
49 } else { 49 } else {
50 workspace_insert_tiling(con2->workspace, con1, 50 workspace_insert_tiling(con2->pending.workspace, con1,
51 container_sibling_index(con2)); 51 container_sibling_index(con2));
52 } 52 }
53 if (temp->parent) { 53 if (temp->pending.parent) {
54 container_insert_child(temp->parent, con2, temp_index); 54 container_insert_child(temp->pending.parent, con2, temp_index);
55 } else if (temp_floating) { 55 } else if (temp_floating) {
56 workspace_add_floating(temp->workspace, con2); 56 workspace_add_floating(temp->pending.workspace, con2);
57 } else { 57 } else {
58 workspace_insert_tiling(temp->workspace, con2, temp_index); 58 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
59 } 59 }
60 60
61 free(temp); 61 free(temp);
@@ -65,8 +65,8 @@ static void swap_focus(struct sway_container *con1,
65 struct sway_container *con2, struct sway_seat *seat, 65 struct sway_container *con2, struct sway_seat *seat,
66 struct sway_container *focus) { 66 struct sway_container *focus) {
67 if (focus == con1 || focus == con2) { 67 if (focus == con1 || focus == con2) {
68 struct sway_workspace *ws1 = con1->workspace; 68 struct sway_workspace *ws1 = con1->pending.workspace;
69 struct sway_workspace *ws2 = con2->workspace; 69 struct sway_workspace *ws2 = con2->pending.workspace;
70 enum sway_container_layout layout1 = container_parent_layout(con1); 70 enum sway_container_layout layout1 = container_parent_layout(con1);
71 enum sway_container_layout layout2 = container_parent_layout(con2); 71 enum sway_container_layout layout2 = container_parent_layout(con2);
72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { 72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
@@ -125,11 +125,11 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
125 root_scratchpad_remove_container(con2); 125 root_scratchpad_remove_container(con2);
126 } 126 }
127 127
128 enum sway_fullscreen_mode fs1 = con1->fullscreen_mode; 128 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
129 enum sway_fullscreen_mode fs2 = con2->fullscreen_mode;
130 if (fs1) { 129 if (fs1) {
131 container_fullscreen_disable(con1); 130 container_fullscreen_disable(con1);
132 } 131 }
132 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
133 if (fs2) { 133 if (fs2) {
134 container_fullscreen_disable(con2); 134 container_fullscreen_disable(con2);
135 } 135 }
@@ -137,9 +137,9 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
137 struct sway_seat *seat = config->handler_context.seat; 137 struct sway_seat *seat = config->handler_context.seat;
138 struct sway_container *focus = seat_get_focused_container(seat); 138 struct sway_container *focus = seat_get_focused_container(seat);
139 struct sway_workspace *vis1 = 139 struct sway_workspace *vis1 =
140 output_get_active_workspace(con1->workspace->output); 140 output_get_active_workspace(con1->pending.workspace->output);
141 struct sway_workspace *vis2 = 141 struct sway_workspace *vis2 =
142 output_get_active_workspace(con2->workspace->output); 142 output_get_active_workspace(con2->pending.workspace->output);
143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" 143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
144 "workspace. This should not happen")) { 144 "workspace. This should not happen")) {
145 return; 145 return;
@@ -247,6 +247,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
247 } else if (!current) { 247 } else if (!current) {
248 error = cmd_results_new(CMD_FAILURE, 248 error = cmd_results_new(CMD_FAILURE,
249 "Can only swap with containers and views"); 249 "Can only swap with containers and views");
250 } else if (current == other) {
251 error = cmd_results_new(CMD_FAILURE,
252 "Cannot swap a container with itself");
250 } else if (container_has_ancestor(current, other) 253 } else if (container_has_ancestor(current, other)
251 || container_has_ancestor(other, current)) { 254 || container_has_ancestor(other, current)) {
252 error = cmd_results_new(CMD_FAILURE, 255 error = cmd_results_new(CMD_FAILURE,
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 9d312470..a2446b7e 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -23,6 +23,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
23 } 23 }
24 view->title_format = format; 24 view->title_format = format;
25 view_update_title(view, true); 25 view_update_title(view, true);
26 config_update_font_height(true);
27 return cmd_results_new(CMD_SUCCESS, NULL); 26 return cmd_results_new(CMD_SUCCESS, NULL);
28} 27}
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index cedfcfb2..19274dfb 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -21,7 +21,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) {
21struct cmd_results *cmd_unmark(int argc, char **argv) { 21struct cmd_results *cmd_unmark(int argc, char **argv) {
22 // Determine the container 22 // Determine the container
23 struct sway_container *con = NULL; 23 struct sway_container *con = NULL;
24 if (config->handler_context.using_criteria) { 24 if (config->handler_context.node_overridden) {
25 con = config->handler_context.container; 25 con = config->handler_context.container;
26 } 26 }
27 27
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 9ff1c97d..79f3667a 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -182,21 +182,16 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
182 "Can't switch workspaces while fullscreen global"); 182 "Can't switch workspaces while fullscreen global");
183 } 183 }
184 184
185 bool no_auto_back_and_forth = false; 185 bool auto_back_and_forth = true;
186 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { 186 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
187 no_auto_back_and_forth = true; 187 auto_back_and_forth = false;
188 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { 188 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) {
189 return error; 189 return error;
190 } 190 }
191 ++argv; 191 ++argv;
192 } 192 }
193 193
194 bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0;
195 struct sway_seat *seat = config->handler_context.seat; 194 struct sway_seat *seat = config->handler_context.seat;
196 struct sway_workspace *current = seat_get_focused_workspace(seat);
197 if (!current) {
198 return cmd_results_new(CMD_FAILURE, "No workspace to switch from");
199 }
200 195
201 struct sway_workspace *ws = NULL; 196 struct sway_workspace *ws = NULL;
202 if (strcasecmp(argv[0], "number") == 0) { 197 if (strcasecmp(argv[0], "number") == 0) {
@@ -213,14 +208,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
213 ws = workspace_create(NULL, name); 208 ws = workspace_create(NULL, name);
214 free(name); 209 free(name);
215 } 210 }
211 if (ws && auto_back_and_forth) {
212 ws = workspace_auto_back_and_forth(ws);
213 }
216 } else if (strcasecmp(argv[0], "next") == 0 || 214 } else if (strcasecmp(argv[0], "next") == 0 ||
217 strcasecmp(argv[0], "prev") == 0 || 215 strcasecmp(argv[0], "prev") == 0 ||
216 strcasecmp(argv[0], "next_on_output") == 0 ||
217 strcasecmp(argv[0], "prev_on_output") == 0 ||
218 strcasecmp(argv[0], "current") == 0) { 218 strcasecmp(argv[0], "current") == 0) {
219 ws = workspace_by_name(argv[0]); 219 ws = workspace_by_name(argv[0]);
220 } else if (strcasecmp(argv[0], "next_on_output") == 0) {
221 ws = workspace_output_next(current, create);
222 } else if (strcasecmp(argv[0], "prev_on_output") == 0) {
223 ws = workspace_output_prev(current, create);
224 } else if (strcasecmp(argv[0], "back_and_forth") == 0) { 220 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
225 if (!seat->prev_workspace_name) { 221 if (!seat->prev_workspace_name) {
226 return cmd_results_new(CMD_INVALID, 222 return cmd_results_new(CMD_INVALID,
@@ -235,11 +231,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
235 ws = workspace_create(NULL, name); 231 ws = workspace_create(NULL, name);
236 } 232 }
237 free(name); 233 free(name);
234 if (ws && auto_back_and_forth) {
235 ws = workspace_auto_back_and_forth(ws);
236 }
238 } 237 }
239 if (!ws) { 238 if (!ws) {
240 return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); 239 return cmd_results_new(CMD_FAILURE, "No workspace to switch to");
241 } 240 }
242 workspace_switch(ws, no_auto_back_and_forth); 241 workspace_switch(ws);
243 seat_consider_warp_to_focus(seat); 242 seat_consider_warp_to_focus(seat);
244 } 243 }
245 return cmd_results_new(CMD_SUCCESS, NULL); 244 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/config.c b/sway/config.c
index 6e665434..b41dd871 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -26,7 +26,7 @@
26#include "sway/tree/arrange.h" 26#include "sway/tree/arrange.h"
27#include "sway/tree/root.h" 27#include "sway/tree/root.h"
28#include "sway/tree/workspace.h" 28#include "sway/tree/workspace.h"
29#include "cairo.h" 29#include "cairo_util.h"
30#include "pango.h" 30#include "pango.h"
31#include "stringop.h" 31#include "stringop.h"
32#include "list.h" 32#include "list.h"
@@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) {
82 } 82 }
83 list_free(mode->switch_bindings); 83 list_free(mode->switch_bindings);
84 } 84 }
85 if (mode->gesture_bindings) {
86 for (int i = 0; i < mode->gesture_bindings->length; i++) {
87 free_gesture_binding(mode->gesture_bindings->items[i]);
88 }
89 list_free(mode->gesture_bindings);
90 }
85 free(mode); 91 free(mode);
86} 92}
87 93
@@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) {
222 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; 228 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
223 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; 229 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
224 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; 230 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
231 if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
225 list_add(config->modes, config->current_mode); 232 list_add(config->modes, config->current_mode);
226 233
227 config->floating_mod = 0; 234 config->floating_mod = 0;
@@ -236,7 +243,7 @@ static void config_defaults(struct sway_config *config) {
236 config->default_layout = L_NONE; 243 config->default_layout = L_NONE;
237 config->default_orientation = L_NONE; 244 config->default_orientation = L_NONE;
238 if (!(config->font = strdup("monospace 10"))) goto cleanup; 245 if (!(config->font = strdup("monospace 10"))) goto cleanup;
239 config->font_height = 17; // height of monospace 10 246 config->font_description = pango_font_description_from_string(config->font);
240 config->urgent_timeout = 500; 247 config->urgent_timeout = 500;
241 config->focus_on_window_activation = FOWA_URGENT; 248 config->focus_on_window_activation = FOWA_URGENT;
242 config->popup_during_fullscreen = POPUP_SMART; 249 config->popup_during_fullscreen = POPUP_SMART;
@@ -267,7 +274,7 @@ static void config_defaults(struct sway_config *config) {
267 config->tiling_drag = true; 274 config->tiling_drag = true;
268 config->tiling_drag_threshold = 9; 275 config->tiling_drag_threshold = 9;
269 276
270 config->smart_gaps = false; 277 config->smart_gaps = SMART_GAPS_OFF;
271 config->gaps_inner = 0; 278 config->gaps_inner = 0;
272 config->gaps_outer.top = 0; 279 config->gaps_outer.top = 0;
273 config->gaps_outer.right = 0; 280 config->gaps_outer.right = 0;
@@ -291,6 +298,8 @@ static void config_defaults(struct sway_config *config) {
291 config->hide_edge_borders_smart = ESMART_OFF; 298 config->hide_edge_borders_smart = ESMART_OFF;
292 config->hide_lone_tab = false; 299 config->hide_lone_tab = false;
293 300
301 config->has_focused_tab_title = false;
302
294 // border colors 303 // border colors
295 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); 304 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF);
296 color_to_rgba(config->border_colors.focused.background, 0x285577FF); 305 color_to_rgba(config->border_colors.focused.background, 0x285577FF);
@@ -338,35 +347,62 @@ static bool file_exists(const char *path) {
338 return path && access(path, R_OK) != -1; 347 return path && access(path, R_OK) != -1;
339} 348}
340 349
350static char *config_path(const char *prefix, const char *config_folder) {
351 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
352 return NULL;
353 }
354
355 const char *filename = "config";
356
357 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
358 char *path = calloc(size, sizeof(char));
359 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
360 return path;
361}
362
341static char *get_config_path(void) { 363static char *get_config_path(void) {
342 static const char *config_paths[] = { 364 char *path = NULL;
343 "$HOME/.sway/config", 365 const char *home = getenv("HOME");
344 "$XDG_CONFIG_HOME/sway/config", 366 char *config_home_fallback = NULL;
345 "$HOME/.i3/config", 367
346 "$XDG_CONFIG_HOME/i3/config", 368 const char *config_home = getenv("XDG_CONFIG_HOME");
347 SYSCONFDIR "/sway/config", 369 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
348 SYSCONFDIR "/i3/config", 370 size_t size_fallback = 1 + strlen(home) + strlen("/.config");
371 config_home_fallback = calloc(size_fallback, sizeof(char));
372 if (config_home_fallback != NULL)
373 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
374 config_home = config_home_fallback;
375 }
376
377 struct config_path {
378 const char *prefix;
379 const char *config_folder;
349 }; 380 };
350 381
351 char *config_home = getenv("XDG_CONFIG_HOME"); 382 struct config_path config_paths[] = {
352 if (!config_home || !*config_home) { 383 { .prefix = home, .config_folder = ".sway"},
353 config_paths[1] = "$HOME/.config/sway/config"; 384 { .prefix = config_home, .config_folder = "sway"},
354 config_paths[3] = "$HOME/.config/i3/config"; 385 { .prefix = home, .config_folder = ".i3"},
355 } 386 { .prefix = config_home, .config_folder = "i3"},
387 { .prefix = SYSCONFDIR, .config_folder = "sway"},
388 { .prefix = SYSCONFDIR, .config_folder = "i3"}
389 };
356 390
357 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { 391 size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);
358 wordexp_t p; 392 for (size_t i = 0; i < num_config_paths; i++) {
359 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { 393 path = config_path(config_paths[i].prefix, config_paths[i].config_folder);
360 char *path = strdup(p.we_wordv[0]); 394 if (!path) {
361 wordfree(&p); 395 continue;
362 if (file_exists(path)) { 396 }
363 return path; 397 if (file_exists(path)) {
364 } 398 break;
365 free(path);
366 } 399 }
400 free(path);
401 path = NULL;
367 } 402 }
368 403
369 return NULL; 404 free(config_home_fallback);
405 return path;
370} 406}
371 407
372static bool load_config(const char *path, struct sway_config *config, 408static bool load_config(const char *path, struct sway_config *config,
@@ -514,6 +550,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
514 return success; 550 return success;
515 } 551 }
516 552
553 // Only really necessary if not explicitly `font` is set in the config.
554 config_update_font_height();
555
517 if (is_active && !validating) { 556 if (is_active && !validating) {
518 input_manager_verify_fallback_seat(); 557 input_manager_verify_fallback_seat();
519 558
@@ -964,31 +1003,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
964 return lenient_strcmp(wsa->workspace, wsb->workspace); 1003 return lenient_strcmp(wsa->workspace, wsb->workspace);
965} 1004}
966 1005
967static void find_font_height_iterator(struct sway_container *con, void *data) {
968 size_t amount_below_baseline = con->title_height - con->title_baseline;
969 size_t extended_height = config->font_baseline + amount_below_baseline;
970 if (extended_height > config->font_height) {
971 config->font_height = extended_height;
972 }
973}
974
975static void find_baseline_iterator(struct sway_container *con, void *data) {
976 bool *recalculate = data;
977 if (*recalculate) {
978 container_calculate_title_height(con);
979 }
980 if (con->title_baseline > config->font_baseline) {
981 config->font_baseline = con->title_baseline;
982 }
983}
984 1006
985void config_update_font_height(bool recalculate) { 1007void config_update_font_height(void) {
986 size_t prev_max_height = config->font_height; 1008 int prev_max_height = config->font_height;
987 config->font_height = 0;
988 config->font_baseline = 0;
989 1009
990 root_for_each_container(find_baseline_iterator, &recalculate); 1010 get_text_metrics(config->font_description, &config->font_height, &config->font_baseline);
991 root_for_each_container(find_font_height_iterator, NULL);
992 1011
993 if (config->font_height != prev_max_height) { 1012 if (config->font_height != prev_max_height) {
994 arrange_root(); 1013 arrange_root();
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 767534a6..d1b342e6 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -91,7 +91,7 @@ struct bar_config *default_bar_config(void) {
91 } 91 }
92 bar->outputs = NULL; 92 bar->outputs = NULL;
93 bar->position = strdup("bottom"); 93 bar->position = strdup("bottom");
94 bar->pango_markup = false; 94 bar->pango_markup = PANGO_MARKUP_DEFAULT;
95 bar->swaybar_command = NULL; 95 bar->swaybar_command = NULL;
96 bar->font = NULL; 96 bar->font = NULL;
97 bar->height = 0; 97 bar->height = 0;
@@ -217,6 +217,9 @@ static void invoke_swaybar(struct bar_config *bar) {
217 sigset_t set; 217 sigset_t set;
218 sigemptyset(&set); 218 sigemptyset(&set);
219 sigprocmask(SIG_SETMASK, &set, NULL); 219 sigprocmask(SIG_SETMASK, &set, NULL);
220 signal(SIGPIPE, SIG_DFL);
221
222 restore_nofile_limit();
220 223
221 pid = fork(); 224 pid = fork();
222 if (pid < 0) { 225 if (pid < 0) {
diff --git a/sway/config/output.c b/sway/config/output.c
index c9ec6745..b3e8371e 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,5 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <drm_fourcc.h>
3#include <stdbool.h> 4#include <stdbool.h>
4#include <string.h> 5#include <string.h>
5#include <sys/socket.h> 6#include <sys/socket.h>
@@ -8,6 +9,7 @@
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/backend/drm.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"
@@ -25,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) {
25void output_get_identifier(char *identifier, size_t len, 27void output_get_identifier(char *identifier, size_t len,
26 struct sway_output *output) { 28 struct sway_output *output) {
27 struct wlr_output *wlr_output = output->wlr_output; 29 struct wlr_output *wlr_output = output->wlr_output;
28 snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, 30 snprintf(identifier, len, "%s %s %s",
29 wlr_output->serial); 31 wlr_output->make ? wlr_output->make : "Unknown",
32 wlr_output->model ? wlr_output->model : "Unknown",
33 wlr_output->serial ? wlr_output->serial : "Unknown");
30} 34}
31 35
32const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { 36const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
@@ -58,6 +62,7 @@ struct output_config *new_output_config(const char *name) {
58 oc->width = oc->height = -1; 62 oc->width = oc->height = -1;
59 oc->refresh_rate = -1; 63 oc->refresh_rate = -1;
60 oc->custom_mode = -1; 64 oc->custom_mode = -1;
65 oc->drm_mode.type = -1;
61 oc->x = oc->y = -1; 66 oc->x = oc->y = -1;
62 oc->scale = -1; 67 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT; 68 oc->scale_filter = SCALE_FILTER_DEFAULT;
@@ -65,6 +70,8 @@ struct output_config *new_output_config(const char *name) {
65 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; 70 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
66 oc->max_render_time = -1; 71 oc->max_render_time = -1;
67 oc->adaptive_sync = -1; 72 oc->adaptive_sync = -1;
73 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
74 oc->power = -1;
68 return oc; 75 return oc;
69} 76}
70 77
@@ -99,6 +106,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 106 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 107 dst->custom_mode = src->custom_mode;
101 } 108 }
109 if (src->drm_mode.type != (uint32_t) -1) {
110 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
111 }
102 if (src->transform != -1) { 112 if (src->transform != -1) {
103 dst->transform = src->transform; 113 dst->transform = src->transform;
104 } 114 }
@@ -108,6 +118,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
108 if (src->adaptive_sync != -1) { 118 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync; 119 dst->adaptive_sync = src->adaptive_sync;
110 } 120 }
121 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
122 dst->render_bit_depth = src->render_bit_depth;
123 }
111 if (src->background) { 124 if (src->background) {
112 free(dst->background); 125 free(dst->background);
113 dst->background = strdup(src->background); 126 dst->background = strdup(src->background);
@@ -120,8 +133,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
120 free(dst->background_fallback); 133 free(dst->background_fallback);
121 dst->background_fallback = strdup(src->background_fallback); 134 dst->background_fallback = strdup(src->background_fallback);
122 } 135 }
123 if (src->dpms_state != 0) { 136 if (src->power != -1) {
124 dst->dpms_state = src->dpms_state; 137 dst->power = src->power;
125 } 138 }
126} 139}
127 140
@@ -180,11 +193,11 @@ static void merge_id_on_name(struct output_config *oc) {
180 list_add(config->output_configs, ion_oc); 193 list_add(config->output_configs, ion_oc);
181 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" 194 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
182 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " 195 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
183 "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", 196 "transform %d) (bg %s %s) (power %d) (max render time: %d)",
184 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, 197 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, 198 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
186 ion_oc->transform, ion_oc->background, 199 ion_oc->transform, ion_oc->background,
187 ion_oc->background_option, ion_oc->dpms_state, 200 ion_oc->background_option, ion_oc->power,
188 ion_oc->max_render_time); 201 ion_oc->max_render_time);
189 } 202 }
190 } 203 }
@@ -225,18 +238,18 @@ struct output_config *store_output_config(struct output_config *oc) {
225 } 238 }
226 239
227 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 240 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) " 241 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
229 "(max render time: %d)", 242 "(max render time: %d)",
230 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 243 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), 244 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, 245 oc->transform, oc->background, oc->background_option, oc->power,
233 oc->max_render_time); 246 oc->max_render_time);
234 247
235 return oc; 248 return oc;
236} 249}
237 250
238static void set_mode(struct wlr_output *output, int width, int height, 251static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
239 float refresh_rate, bool custom) { 252 int width, int height, float refresh_rate, bool custom) {
240 // Not all floating point integers can be represented exactly 253 // Not all floating point integers can be represented exactly
241 // as (int)(1000 * mHz / 1000.f) 254 // as (int)(1000 * mHz / 1000.f)
242 // round() the result to avoid any error 255 // round() the result to avoid any error
@@ -244,7 +257,7 @@ static void set_mode(struct wlr_output *output, int width, int height,
244 257
245 if (wl_list_empty(&output->modes) || custom) { 258 if (wl_list_empty(&output->modes) || custom) {
246 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 259 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
247 wlr_output_set_custom_mode(output, width, height, 260 wlr_output_state_set_custom_mode(pending, width, height,
248 refresh_rate > 0 ? mhz : 0); 261 refresh_rate > 0 ? mhz : 0);
249 return; 262 return;
250 } 263 }
@@ -268,7 +281,20 @@ static void set_mode(struct wlr_output *output, int width, int height,
268 } else { 281 } else {
269 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 282 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name);
270 } 283 }
271 wlr_output_set_mode(output, best); 284 wlr_output_state_set_mode(pending, best);
285}
286
287static void set_modeline(struct wlr_output *output,
288 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
289 if (!wlr_output_is_drm(output)) {
290 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
291 return;
292 }
293 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
294 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
295 if (mode) {
296 wlr_output_state_set_mode(pending, mode);
297 }
272} 298}
273 299
274/* Some manufacturers hardcode the aspect-ratio of the output in the physical 300/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -289,23 +315,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
289// 1 inch = 25.4 mm 315// 1 inch = 25.4 mm
290#define MM_PER_INCH 25.4 316#define MM_PER_INCH 25.4
291 317
292static int compute_default_scale(struct wlr_output *output) { 318static int compute_default_scale(struct wlr_output *output,
319 struct wlr_output_state *pending) {
293 struct wlr_box box = { .width = output->width, .height = output->height }; 320 struct wlr_box box = { .width = output->width, .height = output->height };
294 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { 321 if (pending->committed & WLR_OUTPUT_STATE_MODE) {
295 switch (output->pending.mode_type) { 322 switch (pending->mode_type) {
296 case WLR_OUTPUT_STATE_MODE_FIXED: 323 case WLR_OUTPUT_STATE_MODE_FIXED:
297 box.width = output->pending.mode->width; 324 box.width = pending->mode->width;
298 box.height = output->pending.mode->height; 325 box.height = pending->mode->height;
299 break; 326 break;
300 case WLR_OUTPUT_STATE_MODE_CUSTOM: 327 case WLR_OUTPUT_STATE_MODE_CUSTOM:
301 box.width = output->pending.custom_mode.width; 328 box.width = pending->custom_mode.width;
302 box.height = output->pending.custom_mode.height; 329 box.height = pending->custom_mode.height;
303 break; 330 break;
304 } 331 }
305 } 332 }
306 enum wl_output_transform transform = output->transform; 333 enum wl_output_transform transform = output->transform;
307 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { 334 if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
308 transform = output->pending.transform; 335 transform = pending->transform;
309 } 336 }
310 wlr_box_transform(&box, &box, transform, box.width, box.height); 337 wlr_box_transform(&box, &box, transform, box.width, box.height);
311 338
@@ -334,42 +361,88 @@ static int compute_default_scale(struct wlr_output *output) {
334 return 2; 361 return 2;
335} 362}
336 363
364/* Lists of formats to try, in order, when a specific render bit depth has
365 * been asked for. The second to last format in each list should always
366 * be XRGB8888, as a reliable backup in case the others are not available;
367 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */
368static const uint32_t *bit_depth_preferences[] = {
369 [RENDER_BIT_DEPTH_8] = (const uint32_t []){
370 DRM_FORMAT_XRGB8888,
371 DRM_FORMAT_INVALID,
372 },
373 [RENDER_BIT_DEPTH_10] = (const uint32_t []){
374 DRM_FORMAT_XRGB2101010,
375 DRM_FORMAT_XBGR2101010,
376 DRM_FORMAT_XRGB8888,
377 DRM_FORMAT_INVALID,
378 },
379};
380
337static void queue_output_config(struct output_config *oc, 381static void queue_output_config(struct output_config *oc,
338 struct sway_output *output) { 382 struct sway_output *output, struct wlr_output_state *pending) {
339 if (output == root->noop_output) { 383 if (output == root->fallback_output) {
340 return; 384 return;
341 } 385 }
342 386
343 struct wlr_output *wlr_output = output->wlr_output; 387 struct wlr_output *wlr_output = output->wlr_output;
344 388
345 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { 389 if (oc && (!oc->enabled || oc->power == 0)) {
346 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 390 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
347 wlr_output_enable(wlr_output, false); 391 wlr_output_state_set_enabled(pending, false);
348 return; 392 return;
349 } 393 }
350 394
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 395 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 396 wlr_output_state_set_enabled(pending, true);
353 397
354 if (oc && oc->width > 0 && oc->height > 0) { 398 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
399 sway_log(SWAY_DEBUG, "Set %s modeline",
400 wlr_output->name);
401 set_modeline(wlr_output, pending, &oc->drm_mode);
402 } else if (oc && oc->width > 0 && oc->height > 0) {
355 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 403 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 404 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 405 set_mode(wlr_output, pending, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 406 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 407 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 408 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 409 struct wlr_output_mode *preferred_mode =
410 wlr_output_preferred_mode(wlr_output);
411 wlr_output_state_set_mode(pending, preferred_mode);
412
413 if (!wlr_output_test_state(wlr_output, pending)) {
414 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
415 "falling back to another mode");
416 struct wlr_output_mode *mode;
417 wl_list_for_each(mode, &wlr_output->modes, link) {
418 if (mode == preferred_mode) {
419 continue;
420 }
421
422 wlr_output_state_set_mode(pending, mode);
423 if (wlr_output_test_state(wlr_output, pending)) {
424 break;
425 }
426 }
427 }
362 } 428 }
363 429
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 430 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
365 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 431 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
366 sway_wl_output_subpixel_to_string(oc->subpixel)); 432 sway_wl_output_subpixel_to_string(oc->subpixel));
367 wlr_output_set_subpixel(wlr_output, oc->subpixel); 433 wlr_output_state_set_subpixel(pending, oc->subpixel);
368 } 434 }
369 435
436 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
370 if (oc && oc->transform >= 0) { 437 if (oc && oc->transform >= 0) {
371 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 438 tr = oc->transform;
372 wlr_output_set_transform(wlr_output, oc->transform); 439 } else if (wlr_output_is_drm(wlr_output)) {
440 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
441 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
442 }
443 if (wlr_output->transform != tr) {
444 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
445 wlr_output_state_set_transform(pending, tr);
373 } 446 }
374 447
375 // Apply the scale last before the commit, because the scale auto-detection 448 // Apply the scale last before the commit, because the scale auto-detection
@@ -378,23 +451,39 @@ static void queue_output_config(struct output_config *oc,
378 if (oc && oc->scale > 0) { 451 if (oc && oc->scale > 0) {
379 scale = oc->scale; 452 scale = oc->scale;
380 } else { 453 } else {
381 scale = compute_default_scale(wlr_output); 454 scale = compute_default_scale(wlr_output, pending);
382 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 455 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
383 } 456 }
384 if (scale != wlr_output->scale) { 457 if (scale != wlr_output->scale) {
385 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); 458 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
386 wlr_output_set_scale(wlr_output, scale); 459 wlr_output_state_set_scale(pending, scale);
387 } 460 }
388 461
389 if (oc && oc->adaptive_sync != -1) { 462 if (oc && oc->adaptive_sync != -1) {
390 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 463 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
391 oc->adaptive_sync); 464 oc->adaptive_sync);
392 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 465 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
466 }
467
468 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
469 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth];
470 assert(fmts);
471
472 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
473 wlr_output_state_set_render_format(pending, fmts[i]);
474 if (wlr_output_test_state(wlr_output, pending)) {
475 break;
476 }
477
478 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
479 "failed to work, falling back to next in "
480 "list, 0x%08x", fmts[i], fmts[i + 1]);
481 }
393 } 482 }
394} 483}
395 484
396bool apply_output_config(struct output_config *oc, struct sway_output *output) { 485bool apply_output_config(struct output_config *oc, struct sway_output *output) {
397 if (output == root->noop_output) { 486 if (output == root->fallback_output) {
398 return false; 487 return false;
399 } 488 }
400 489
@@ -403,14 +492,15 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
403 // Flag to prevent the output mode event handler from calling us 492 // Flag to prevent the output mode event handler from calling us
404 output->enabling = (!oc || oc->enabled); 493 output->enabling = (!oc || oc->enabled);
405 494
406 queue_output_config(oc, output); 495 struct wlr_output_state pending = {0};
496 queue_output_config(oc, output, &pending);
407 497
408 if (!oc || oc->dpms_state != DPMS_OFF) { 498 if (!oc || oc->power != 0) {
409 output->current_mode = wlr_output->pending.mode; 499 output->current_mode = pending.mode;
410 } 500 }
411 501
412 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); 502 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
413 if (!wlr_output_commit(wlr_output)) { 503 if (!wlr_output_commit_state(wlr_output, &pending)) {
414 // Failed to commit output changes, maybe the output is missing a CRTC. 504 // 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 505 // Leave the output disabled for now and try again when the output gets
416 // the mode we asked for. 506 // the mode we asked for.
@@ -462,12 +552,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
462 } 552 }
463 553
464 // Update output->{lx, ly, width, height} 554 // Update output->{lx, ly, width, height}
465 struct wlr_box *output_box = 555 struct wlr_box output_box;
466 wlr_output_layout_get_box(root->output_layout, wlr_output); 556 wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
467 output->lx = output_box->x; 557 output->lx = output_box.x;
468 output->ly = output_box->y; 558 output->ly = output_box.y;
469 output->width = output_box->width; 559 output->width = output_box.width;
470 output->height = output_box->height; 560 output->height = output_box.height;
471 561
472 if (!output->enabled) { 562 if (!output->enabled) {
473 output_enable(output); 563 output_enable(output);
@@ -483,23 +573,25 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
483 // this output came online, and some config items (like map_to_output) are 573 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present. 574 // dependent on an output being present.
485 input_manager_configure_all_inputs(); 575 input_manager_configure_all_inputs();
576 // Reconfigure the cursor images, since the scale may have changed.
577 input_manager_configure_xcursor();
486 return true; 578 return true;
487} 579}
488 580
489bool test_output_config(struct output_config *oc, struct sway_output *output) { 581bool test_output_config(struct output_config *oc, struct sway_output *output) {
490 if (output == root->noop_output) { 582 if (output == root->fallback_output) {
491 return false; 583 return false;
492 } 584 }
493 585
494 queue_output_config(oc, output); 586 struct wlr_output_state pending = {0};
495 bool ok = wlr_output_test(output->wlr_output); 587 queue_output_config(oc, output, &pending);
496 wlr_output_rollback(output->wlr_output); 588 return wlr_output_test_state(output->wlr_output, &pending);
497 return ok;
498} 589}
499 590
500static void default_output_config(struct output_config *oc, 591static void default_output_config(struct output_config *oc,
501 struct wlr_output *wlr_output) { 592 struct wlr_output *wlr_output) {
502 oc->enabled = 1; 593 oc->enabled = 1;
594 oc->power = 1;
503 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 595 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
504 if (mode != NULL) { 596 if (mode != NULL) {
505 oc->width = mode->width; 597 oc->width = mode->width;
@@ -512,7 +604,6 @@ static void default_output_config(struct output_config *oc,
512 struct sway_output *output = wlr_output->data; 604 struct sway_output *output = wlr_output->data;
513 oc->subpixel = output->detected_subpixel; 605 oc->subpixel = output->detected_subpixel;
514 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; 606 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
515 oc->dpms_state = DPMS_ON;
516 oc->max_render_time = 0; 607 oc->max_render_time = 0;
517} 608}
518 609
@@ -567,10 +658,10 @@ static struct output_config *get_output_config(char *identifier,
567 658
568 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" 659 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
569 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" 660 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
570 " (dpms %d) (max render time: %d)", result->name, result->enabled, 661 " (power %d) (max render time: %d)", result->name, result->enabled,
571 result->width, result->height, result->refresh_rate, 662 result->width, result->height, result->refresh_rate,
572 result->x, result->y, result->scale, result->transform, 663 result->x, result->y, result->scale, result->transform,
573 result->background, result->background_option, result->dpms_state, 664 result->background, result->background_option, result->power,
574 result->max_render_time); 665 result->max_render_time);
575 } else if (oc_name) { 666 } else if (oc_name) {
576 // No identifier config, just return a copy of the name config 667 // No identifier config, just return a copy of the name config
@@ -702,6 +793,8 @@ static bool _spawn_swaybg(char **command) {
702 sway_log_errno(SWAY_ERROR, "fork failed"); 793 sway_log_errno(SWAY_ERROR, "fork failed");
703 return false; 794 return false;
704 } else if (pid == 0) { 795 } else if (pid == 0) {
796 restore_nofile_limit();
797
705 pid = fork(); 798 pid = fork();
706 if (pid < 0) { 799 if (pid < 0) {
707 sway_log_errno(SWAY_ERROR, "fork failed"); 800 sway_log_errno(SWAY_ERROR, "fork failed");
diff --git a/sway/criteria.c b/sway/criteria.c
index 409160c5..97cf667e 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -3,7 +3,8 @@
3#include <stdio.h> 3#include <stdio.h>
4#include <stdbool.h> 4#include <stdbool.h>
5#include <strings.h> 5#include <strings.h>
6#include <pcre.h> 6#define PCRE2_CODE_UNIT_WIDTH 8
7#include <pcre2.h>
7#include "sway/criteria.h" 8#include "sway/criteria.h"
8#include "sway/tree/container.h" 9#include "sway/tree/container.h"
9#include "sway/config.h" 10#include "sway/config.h"
@@ -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 }
@@ -99,8 +102,11 @@ void criteria_destroy(struct criteria *criteria) {
99 free(criteria); 102 free(criteria);
100} 103}
101 104
102static int regex_cmp(const char *item, const pcre *regex) { 105static int regex_cmp(const char *item, const pcre2_code *regex) {
103 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); 106 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
107 int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL);
108 pcre2_match_data_free(match_data);
109 return result;
104} 110}
105 111
106#if HAVE_XWAYLAND 112#if HAVE_XWAYLAND
@@ -155,7 +161,7 @@ static bool criteria_matches_container(struct criteria *criteria,
155 bool exists = false; 161 bool exists = false;
156 struct sway_container *con = container; 162 struct sway_container *con = container;
157 for (int i = 0; i < con->marks->length; ++i) { 163 for (int i = 0; i < con->marks->length; ++i) {
158 if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { 164 if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) {
159 exists = true; 165 exists = true;
160 break; 166 break;
161 } 167 }
@@ -192,8 +198,8 @@ static bool criteria_matches_view(struct criteria *criteria,
192 return false; 198 return false;
193 } 199 }
194 break; 200 break;
195 case PATTERN_PCRE: 201 case PATTERN_PCRE2:
196 if (regex_cmp(title, criteria->title->regex) != 0) { 202 if (regex_cmp(title, criteria->title->regex) < 0) {
197 return false; 203 return false;
198 } 204 }
199 break; 205 break;
@@ -212,8 +218,8 @@ static bool criteria_matches_view(struct criteria *criteria,
212 return false; 218 return false;
213 } 219 }
214 break; 220 break;
215 case PATTERN_PCRE: 221 case PATTERN_PCRE2:
216 if (regex_cmp(shell, criteria->shell->regex) != 0) { 222 if (regex_cmp(shell, criteria->shell->regex) < 0) {
217 return false; 223 return false;
218 } 224 }
219 break; 225 break;
@@ -232,8 +238,8 @@ static bool criteria_matches_view(struct criteria *criteria,
232 return false; 238 return false;
233 } 239 }
234 break; 240 break;
235 case PATTERN_PCRE: 241 case PATTERN_PCRE2:
236 if (regex_cmp(app_id, criteria->app_id->regex) != 0) { 242 if (regex_cmp(app_id, criteria->app_id->regex) < 0) {
237 return false; 243 return false;
238 } 244 }
239 break; 245 break;
@@ -264,8 +270,8 @@ static bool criteria_matches_view(struct criteria *criteria,
264 return false; 270 return false;
265 } 271 }
266 break; 272 break;
267 case PATTERN_PCRE: 273 case PATTERN_PCRE2:
268 if (regex_cmp(class, criteria->class->regex) != 0) { 274 if (regex_cmp(class, criteria->class->regex) < 0) {
269 return false; 275 return false;
270 } 276 }
271 break; 277 break;
@@ -284,8 +290,8 @@ static bool criteria_matches_view(struct criteria *criteria,
284 return false; 290 return false;
285 } 291 }
286 break; 292 break;
287 case PATTERN_PCRE: 293 case PATTERN_PCRE2:
288 if (regex_cmp(instance, criteria->instance->regex) != 0) { 294 if (regex_cmp(instance, criteria->instance->regex) < 0) {
289 return false; 295 return false;
290 } 296 }
291 break; 297 break;
@@ -304,8 +310,8 @@ static bool criteria_matches_view(struct criteria *criteria,
304 return false; 310 return false;
305 } 311 }
306 break; 312 break;
307 case PATTERN_PCRE: 313 case PATTERN_PCRE2:
308 if (regex_cmp(window_role, criteria->window_role->regex) != 0) { 314 if (regex_cmp(window_role, criteria->window_role->regex) < 0) {
309 return false; 315 return false;
310 } 316 }
311 break; 317 break;
@@ -351,7 +357,7 @@ static bool criteria_matches_view(struct criteria *criteria,
351 } 357 }
352 358
353 if (criteria->workspace) { 359 if (criteria->workspace) {
354 struct sway_workspace *ws = view->container->workspace; 360 struct sway_workspace *ws = view->container->pending.workspace;
355 if (!ws) { 361 if (!ws) {
356 return false; 362 return false;
357 } 363 }
@@ -359,12 +365,12 @@ static bool criteria_matches_view(struct criteria *criteria,
359 switch (criteria->workspace->match_type) { 365 switch (criteria->workspace->match_type) {
360 case PATTERN_FOCUSED: 366 case PATTERN_FOCUSED:
361 if (focused && 367 if (focused &&
362 strcmp(ws->name, focused->container->workspace->name)) { 368 strcmp(ws->name, focused->container->pending.workspace->name)) {
363 return false; 369 return false;
364 } 370 }
365 break; 371 break;
366 case PATTERN_PCRE: 372 case PATTERN_PCRE2:
367 if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { 373 if (regex_cmp(ws->name, criteria->workspace->regex) < 0) {
368 return false; 374 return false;
369 } 375 }
370 break; 376 break;
@@ -676,7 +682,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
676 } 682 }
677 name = calloc(head - namestart + 1, 1); 683 name = calloc(head - namestart + 1, 1);
678 if (head != namestart) { 684 if (head != namestart) {
679 strncpy(name, namestart, head - namestart); 685 memcpy(name, namestart, head - namestart);
680 } 686 }
681 // Parse token value 687 // Parse token value
682 skip_spaces(&head); 688 skip_spaces(&head);
@@ -703,7 +709,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
703 } 709 }
704 } 710 }
705 value = calloc(head - valuestart + 1, 1); 711 value = calloc(head - valuestart + 1, 1);
706 strncpy(value, valuestart, head - valuestart); 712 memcpy(value, valuestart, head - valuestart);
707 if (in_quotes) { 713 if (in_quotes) {
708 ++head; 714 ++head;
709 in_quotes = false; 715 in_quotes = false;
@@ -734,7 +740,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
734 ++head; 740 ++head;
735 int len = head - raw; 741 int len = head - raw;
736 criteria->raw = calloc(len + 1, 1); 742 criteria->raw = calloc(len + 1, 1);
737 strncpy(criteria->raw, raw, len); 743 memcpy(criteria->raw, raw, len);
738 return criteria; 744 return criteria;
739 745
740cleanup: 746cleanup:
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index ec45d80a..c8d4502c 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) { 6 bool whole) {
7 for (int i = 0; i < root->outputs->length; ++i) { 7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_output *output = root->outputs->items[i]; 8 struct sway_output *output = root->outputs->items[i];
9 struct wlr_box *output_box = wlr_output_layout_get_box( 9 struct wlr_box output_box;
10 root->output_layout, output->wlr_output); 10 wlr_output_layout_get_box(root->output_layout,
11 output_damage_surface(output, lx - output_box->x, 11 output->wlr_output, &output_box);
12 ly - output_box->y, surface, whole); 12 output_damage_surface(output, lx - output_box.x,
13 ly - output_box.y, surface, whole);
13 } 14 }
14} 15}
15 16
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index a5cfd5b2..82353038 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -36,7 +36,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
36 36
37 inhibitor->manager = manager; 37 inhibitor->manager = manager;
38 inhibitor->mode = INHIBIT_IDLE_APPLICATION; 38 inhibitor->mode = INHIBIT_IDLE_APPLICATION;
39 inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); 39 inhibitor->wlr_inhibitor = wlr_inhibitor;
40 wl_list_insert(&manager->inhibitors, &inhibitor->link); 40 wl_list_insert(&manager->inhibitors, &inhibitor->link);
41 41
42 inhibitor->destroy.notify = handle_destroy; 42 inhibitor->destroy.notify = handle_destroy;
@@ -69,8 +69,8 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
69 struct sway_idle_inhibitor_v1 *inhibitor; 69 struct sway_idle_inhibitor_v1 *inhibitor;
70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors,
71 link) { 71 link) {
72 if (inhibitor->view == view && 72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION &&
73 inhibitor->mode != INHIBIT_IDLE_APPLICATION) { 73 inhibitor->view == view) {
74 return inhibitor; 74 return inhibitor;
75 } 75 }
76 } 76 }
@@ -82,8 +82,8 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi
82 struct sway_idle_inhibitor_v1 *inhibitor; 82 struct sway_idle_inhibitor_v1 *inhibitor;
83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors,
84 link) { 84 link) {
85 if (inhibitor->view == view && 85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION &&
86 inhibitor->mode == INHIBIT_IDLE_APPLICATION) { 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
87 return inhibitor; 87 return inhibitor;
88 } 88 }
89 } 89 }
@@ -104,10 +104,10 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy(
104 104
105bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { 105bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {
106 switch (inhibitor->mode) { 106 switch (inhibitor->mode) {
107 case INHIBIT_IDLE_APPLICATION: 107 case INHIBIT_IDLE_APPLICATION:;
108 // If there is no view associated with the inhibitor, assume visible 108 // If there is no view associated with the inhibitor, assume visible
109 return !inhibitor->view || !inhibitor->view->container || 109 struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface);
110 view_is_visible(inhibitor->view); 110 return !view || !view->container || view_is_visible(view);
111 case INHIBIT_IDLE_FOCUS:; 111 case INHIBIT_IDLE_FOCUS:;
112 struct sway_seat *seat = NULL; 112 struct sway_seat *seat = NULL;
113 wl_list_for_each(seat, &server.input->seats, link) { 113 wl_list_for_each(seat, &server.input->seats, link) {
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index d4ca4fb4..d44d6338 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,10 +2,10 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_box.h>
6#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
7#include <wlr/types/wlr_output_damage.h> 6#include <wlr/types/wlr_output_damage.h>
8#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_subcompositor.h>
9#include "log.h" 9#include "log.h"
10#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
@@ -115,9 +115,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
115 // Horizontal axis 115 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT 116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; 117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if ((state->anchor & both_horiz) && box.width == 0) { 118 if (box.width == 0) {
119 box.x = bounds.x; 119 box.x = bounds.x;
120 box.width = bounds.width; 120 } else if ((state->anchor & both_horiz) == both_horiz) {
121 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
122 box.x = bounds.x; 123 box.x = bounds.x;
123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 124 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
@@ -128,9 +129,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
128 // Vertical axis 129 // Vertical axis
129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP 130 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; 131 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
131 if ((state->anchor & both_vert) && box.height == 0) { 132 if (box.height == 0) {
132 box.y = bounds.y; 133 box.y = bounds.y;
133 box.height = bounds.height; 134 } else if ((state->anchor & both_vert) == both_vert) {
135 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
134 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
135 box.y = bounds.y; 137 box.y = bounds.y;
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 138 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
@@ -139,25 +141,30 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
139 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); 141 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
140 } 142 }
141 // Margin 143 // Margin
142 if ((state->anchor & both_horiz) == both_horiz) { 144 if (box.width == 0) {
143 box.x += state->margin.left; 145 box.x += state->margin.left;
144 box.width -= state->margin.left + state->margin.right; 146 box.width = bounds.width -
147 (state->margin.left + state->margin.right);
148 } else if ((state->anchor & both_horiz) == both_horiz) {
149 // don't apply margins
145 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 150 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
146 box.x += state->margin.left; 151 box.x += state->margin.left;
147 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 152 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
148 box.x -= state->margin.right; 153 box.x -= state->margin.right;
149 } 154 }
150 if ((state->anchor & both_vert) == both_vert) { 155 if (box.height == 0) {
151 box.y += state->margin.top; 156 box.y += state->margin.top;
152 box.height -= state->margin.top + state->margin.bottom; 157 box.height = bounds.height -
158 (state->margin.top + state->margin.bottom);
159 } else if ((state->anchor & both_vert) == both_vert) {
160 // don't apply margins
153 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 161 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
154 box.y += state->margin.top; 162 box.y += state->margin.top;
155 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 163 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
156 box.y -= state->margin.bottom; 164 box.y -= state->margin.bottom;
157 } 165 }
158 if (box.width < 0 || box.height < 0) { 166 if (!sway_assert(box.width >= 0 && box.height >= 0,
159 // TODO: Bubble up a protocol error? 167 "Expected layer surface to have positive size")) {
160 wlr_layer_surface_v1_close(layer);
161 continue; 168 continue;
162 } 169 }
163 // Apply 170 // Apply
@@ -191,7 +198,7 @@ void arrange_layers(struct sway_output *output) {
191 arrange_output(output); 198 arrange_output(output);
192 } 199 }
193 200
194 // Arrange non-exlusive surfaces from top->bottom 201 // Arrange non-exclusive surfaces from top->bottom
195 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 202 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
196 &usable_area, false); 203 &usable_area, false);
197 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 204 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
@@ -264,10 +271,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
264 wl_resource_get_client(sway_layer->layer_surface->resource); 271 wl_resource_get_client(sway_layer->layer_surface->resource);
265 bool set_focus = seat->exclusive_client == client; 272 bool set_focus = seat->exclusive_client == client;
266 273
267 wl_list_remove(&sway_layer->output_destroy.link);
268 wl_list_remove(&sway_layer->link);
269 wl_list_init(&sway_layer->link);
270
271 if (set_focus) { 274 if (set_focus) {
272 struct sway_layer_surface *layer = 275 struct sway_layer_surface *layer =
273 find_mapped_layer_by_client(client, sway_layer->layer_surface->output); 276 find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
@@ -276,8 +279,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
276 } 279 }
277 } 280 }
278 281
279 sway_layer->layer_surface->output = NULL; 282 wlr_layer_surface_v1_destroy(sway_layer->layer_surface);
280 wlr_layer_surface_v1_close(sway_layer->layer_surface);
281} 283}
282 284
283static void handle_surface_commit(struct wl_listener *listener, void *data) { 285static void handle_surface_commit(struct wl_listener *listener, void *data) {
@@ -285,26 +287,32 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
285 wl_container_of(listener, layer, surface_commit); 287 wl_container_of(listener, layer, surface_commit);
286 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; 288 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
287 struct wlr_output *wlr_output = layer_surface->output; 289 struct wlr_output *wlr_output = layer_surface->output;
288 if (wlr_output == NULL) { 290 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
289 return; 291 struct sway_output *output = wlr_output->data;
292 struct wlr_box old_extent = layer->extent;
293
294 bool layer_changed = false;
295 if (layer_surface->current.committed != 0
296 || layer->mapped != layer_surface->mapped) {
297 layer->mapped = layer_surface->mapped;
298 layer_changed = layer->layer != layer_surface->current.layer;
299 if (layer_changed) {
300 wl_list_remove(&layer->link);
301 wl_list_insert(&output->layers[layer_surface->current.layer],
302 &layer->link);
303 layer->layer = layer_surface->current.layer;
304 }
305 arrange_layers(output);
290 } 306 }
291 307
292 struct sway_output *output = wlr_output->data; 308 wlr_surface_get_extends(layer_surface->surface, &layer->extent);
293 struct wlr_box old_geo = layer->geo; 309 layer->extent.x += layer->geo.x;
294 arrange_layers(output); 310 layer->extent.y += layer->geo.y;
295 311
296 bool geo_changed = 312 bool extent_changed =
297 memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; 313 memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0;
298 bool layer_changed = layer->layer != layer_surface->current.layer; 314 if (extent_changed || layer_changed) {
299 if (layer_changed) { 315 output_damage_box(output, &old_extent);
300 wl_list_remove(&layer->link);
301 wl_list_insert(&output->layers[layer_surface->current.layer],
302 &layer->link);
303 layer->layer = layer_surface->current.layer;
304 }
305 if (geo_changed || layer_changed) {
306 output_damage_surface(output, old_geo.x, old_geo.y,
307 layer_surface->surface, true);
308 output_damage_surface(output, layer->geo.x, layer->geo.y, 316 output_damage_surface(output, layer->geo.x, layer->geo.y,
309 layer_surface->surface, true); 317 layer_surface->surface, true);
310 } else { 318 } else {
@@ -326,17 +334,14 @@ static void unmap(struct sway_layer_surface *sway_layer) {
326 cursor_rebase_all(); 334 cursor_rebase_all();
327 335
328 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 336 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
329 if (wlr_output == NULL) { 337 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
330 return;
331 }
332 struct sway_output *output = wlr_output->data; 338 struct sway_output *output = wlr_output->data;
333 if (output == NULL) {
334 return;
335 }
336 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 339 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
337 sway_layer->layer_surface->surface, true); 340 sway_layer->layer_surface->surface, true);
338} 341}
339 342
343static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface);
344
340static void handle_destroy(struct wl_listener *listener, void *data) { 345static void handle_destroy(struct wl_listener *listener, void *data) {
341 struct sway_layer_surface *sway_layer = 346 struct sway_layer_surface *sway_layer =
342 wl_container_of(listener, sway_layer, destroy); 347 wl_container_of(listener, sway_layer, destroy);
@@ -345,6 +350,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
345 if (sway_layer->layer_surface->mapped) { 350 if (sway_layer->layer_surface->mapped) {
346 unmap(sway_layer); 351 unmap(sway_layer);
347 } 352 }
353
354 struct sway_layer_subsurface *subsurface, *subsurface_tmp;
355 wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) {
356 layer_subsurface_destroy(subsurface);
357 }
358
348 wl_list_remove(&sway_layer->link); 359 wl_list_remove(&sway_layer->link);
349 wl_list_remove(&sway_layer->destroy.link); 360 wl_list_remove(&sway_layer->destroy.link);
350 wl_list_remove(&sway_layer->map.link); 361 wl_list_remove(&sway_layer->map.link);
@@ -352,22 +363,24 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
352 wl_list_remove(&sway_layer->surface_commit.link); 363 wl_list_remove(&sway_layer->surface_commit.link);
353 wl_list_remove(&sway_layer->new_popup.link); 364 wl_list_remove(&sway_layer->new_popup.link);
354 wl_list_remove(&sway_layer->new_subsurface.link); 365 wl_list_remove(&sway_layer->new_subsurface.link);
355 if (sway_layer->layer_surface->output != NULL) { 366
356 struct sway_output *output = sway_layer->layer_surface->output->data; 367 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
357 if (output != NULL) { 368 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
358 arrange_layers(output); 369 struct sway_output *output = wlr_output->data;
359 transaction_commit_dirty(); 370 arrange_layers(output);
360 } 371 transaction_commit_dirty();
361 wl_list_remove(&sway_layer->output_destroy.link); 372 wl_list_remove(&sway_layer->output_destroy.link);
362 sway_layer->layer_surface->output = NULL; 373 sway_layer->layer_surface->output = NULL;
363 } 374
364 free(sway_layer); 375 free(sway_layer);
365} 376}
366 377
367static void handle_map(struct wl_listener *listener, void *data) { 378static void handle_map(struct wl_listener *listener, void *data) {
368 struct sway_layer_surface *sway_layer = wl_container_of(listener, 379 struct sway_layer_surface *sway_layer = wl_container_of(listener,
369 sway_layer, map); 380 sway_layer, map);
370 struct sway_output *output = sway_layer->layer_surface->output->data; 381 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
382 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
383 struct sway_output *output = wlr_output->data;
371 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 384 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
372 sway_layer->layer_surface->surface, true); 385 sway_layer->layer_surface->surface, true);
373 wlr_surface_send_enter(sway_layer->layer_surface->surface, 386 wlr_surface_send_enter(sway_layer->layer_surface->surface,
@@ -385,9 +398,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface,
385 bool whole) { 398 bool whole) {
386 struct sway_layer_surface *layer = subsurface->layer_surface; 399 struct sway_layer_surface *layer = subsurface->layer_surface;
387 struct wlr_output *wlr_output = layer->layer_surface->output; 400 struct wlr_output *wlr_output = layer->layer_surface->output;
388 if (!wlr_output) { 401 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
389 return;
390 }
391 struct sway_output *output = wlr_output->data; 402 struct sway_output *output = wlr_output->data;
392 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; 403 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
393 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; 404 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
@@ -413,11 +424,8 @@ static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
413 subsurface_damage(subsurface, false); 424 subsurface_damage(subsurface, false);
414} 425}
415 426
416static void subsurface_handle_destroy(struct wl_listener *listener, 427static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) {
417 void *data) { 428 wl_list_remove(&subsurface->link);
418 struct sway_layer_subsurface *subsurface =
419 wl_container_of(listener, subsurface, destroy);
420
421 wl_list_remove(&subsurface->map.link); 429 wl_list_remove(&subsurface->map.link);
422 wl_list_remove(&subsurface->unmap.link); 430 wl_list_remove(&subsurface->unmap.link);
423 wl_list_remove(&subsurface->destroy.link); 431 wl_list_remove(&subsurface->destroy.link);
@@ -425,17 +433,25 @@ static void subsurface_handle_destroy(struct wl_listener *listener,
425 free(subsurface); 433 free(subsurface);
426} 434}
427 435
436static void subsurface_handle_destroy(struct wl_listener *listener,
437 void *data) {
438 struct sway_layer_subsurface *subsurface =
439 wl_container_of(listener, subsurface, destroy);
440 layer_subsurface_destroy(subsurface);
441}
442
428static struct sway_layer_subsurface *create_subsurface( 443static struct sway_layer_subsurface *create_subsurface(
429 struct wlr_subsurface *wlr_subsurface, 444 struct wlr_subsurface *wlr_subsurface,
430 struct sway_layer_surface *layer_surface) { 445 struct sway_layer_surface *layer_surface) {
431 struct sway_layer_subsurface *subsurface = 446 struct sway_layer_subsurface *subsurface =
432 calloc(1, sizeof(struct sway_layer_surface)); 447 calloc(1, sizeof(struct sway_layer_subsurface));
433 if (subsurface == NULL) { 448 if (subsurface == NULL) {
434 return NULL; 449 return NULL;
435 } 450 }
436 451
437 subsurface->wlr_subsurface = wlr_subsurface; 452 subsurface->wlr_subsurface = wlr_subsurface;
438 subsurface->layer_surface = layer_surface; 453 subsurface->layer_surface = layer_surface;
454 wl_list_insert(&layer_surface->subsurfaces, &subsurface->link);
439 455
440 subsurface->map.notify = subsurface_handle_map; 456 subsurface->map.notify = subsurface_handle_map;
441 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 457 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
@@ -468,15 +484,15 @@ static struct sway_layer_surface *popup_get_layer(
468static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 484static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
469 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 485 struct wlr_xdg_popup *popup = layer_popup->wlr_popup;
470 struct wlr_surface *surface = popup->base->surface; 486 struct wlr_surface *surface = popup->base->surface;
471 int popup_sx = popup->geometry.x - popup->base->geometry.x; 487 int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x;
472 int popup_sy = popup->geometry.y - popup->base->geometry.y; 488 int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y;
473 int ox = popup_sx, oy = popup_sy; 489 int ox = popup_sx, oy = popup_sy;
474 struct sway_layer_surface *layer; 490 struct sway_layer_surface *layer;
475 while (true) { 491 while (true) {
476 if (layer_popup->parent_type == LAYER_PARENT_POPUP) { 492 if (layer_popup->parent_type == LAYER_PARENT_POPUP) {
477 layer_popup = layer_popup->parent_popup; 493 layer_popup = layer_popup->parent_popup;
478 ox += layer_popup->wlr_popup->geometry.x; 494 ox += layer_popup->wlr_popup->current.geometry.x;
479 oy += layer_popup->wlr_popup->geometry.y; 495 oy += layer_popup->wlr_popup->current.geometry.y;
480 } else { 496 } else {
481 layer = layer_popup->parent_layer; 497 layer = layer_popup->parent_layer;
482 ox += layer->geo.x; 498 ox += layer->geo.x;
@@ -485,6 +501,7 @@ static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
485 } 501 }
486 } 502 }
487 struct wlr_output *wlr_output = layer->layer_surface->output; 503 struct wlr_output *wlr_output = layer->layer_surface->output;
504 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
488 struct sway_output *output = wlr_output->data; 505 struct sway_output *output = wlr_output->data;
489 output_damage_surface(output, ox, oy, surface, whole); 506 output_damage_surface(output, ox, oy, surface, whole);
490} 507}
@@ -493,6 +510,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) {
493 struct sway_layer_popup *popup = wl_container_of(listener, popup, map); 510 struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
494 struct sway_layer_surface *layer = popup_get_layer(popup); 511 struct sway_layer_surface *layer = popup_get_layer(popup);
495 struct wlr_output *wlr_output = layer->layer_surface->output; 512 struct wlr_output *wlr_output = layer->layer_surface->output;
513 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
496 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); 514 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
497 popup_damage(popup, true); 515 popup_damage(popup, true);
498} 516}
@@ -522,7 +540,9 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
522 struct sway_layer_surface *layer = popup_get_layer(popup); 540 struct sway_layer_surface *layer = popup_get_layer(popup);
523 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; 541 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
524 542
525 struct sway_output *output = layer->layer_surface->output->data; 543 struct wlr_output *wlr_output = layer->layer_surface->output;
544 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
545 struct sway_output *output = wlr_output->data;
526 546
527 // the output box expressed in the coordinate system of the toplevel parent 547 // the output box expressed in the coordinate system of the toplevel parent
528 // of the popup 548 // of the popup
@@ -590,14 +610,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
590 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 610 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32
591 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", 611 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",",
592 layer_surface->namespace, 612 layer_surface->namespace,
593 layer_surface->client_pending.layer, 613 layer_surface->pending.layer,
594 layer_surface->client_pending.anchor, 614 layer_surface->pending.anchor,
595 layer_surface->client_pending.desired_width, 615 layer_surface->pending.desired_width,
596 layer_surface->client_pending.desired_height, 616 layer_surface->pending.desired_height,
597 layer_surface->client_pending.margin.top, 617 layer_surface->pending.margin.top,
598 layer_surface->client_pending.margin.right, 618 layer_surface->pending.margin.right,
599 layer_surface->client_pending.margin.bottom, 619 layer_surface->pending.margin.bottom,
600 layer_surface->client_pending.margin.left); 620 layer_surface->pending.margin.left);
601 621
602 if (!layer_surface->output) { 622 if (!layer_surface->output) {
603 // Assign last active output 623 // Assign last active output
@@ -609,12 +629,16 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
609 output = ws->output; 629 output = ws->output;
610 } 630 }
611 } 631 }
612 if (!output || output == root->noop_output) { 632 if (!output || output == root->fallback_output) {
613 if (!root->outputs->length) { 633 if (!root->outputs->length) {
614 sway_log(SWAY_ERROR, 634 sway_log(SWAY_ERROR,
615 "no output to auto-assign layer surface '%s' to", 635 "no output to auto-assign layer surface '%s' to",
616 layer_surface->namespace); 636 layer_surface->namespace);
617 wlr_layer_surface_v1_close(layer_surface); 637 // Note that layer_surface->output can be NULL
638 // here, but none of our destroy callbacks are
639 // registered yet so we don't have to make them
640 // handle that case.
641 wlr_layer_surface_v1_destroy(layer_surface);
618 return; 642 return;
619 } 643 }
620 output = root->outputs->items[0]; 644 output = root->outputs->items[0];
@@ -628,6 +652,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
628 return; 652 return;
629 } 653 }
630 654
655 wl_list_init(&sway_layer->subsurfaces);
656
631 sway_layer->surface_commit.notify = handle_surface_commit; 657 sway_layer->surface_commit.notify = handle_surface_commit;
632 wl_signal_add(&layer_surface->surface->events.commit, 658 wl_signal_add(&layer_surface->surface->events.commit,
633 &sway_layer->surface_commit); 659 &sway_layer->surface_commit);
@@ -649,15 +675,15 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
649 675
650 struct sway_output *output = layer_surface->output->data; 676 struct sway_output *output = layer_surface->output->data;
651 sway_layer->output_destroy.notify = handle_output_destroy; 677 sway_layer->output_destroy.notify = handle_output_destroy;
652 wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); 678 wl_signal_add(&output->events.disable, &sway_layer->output_destroy);
653 679
654 wl_list_insert(&output->layers[layer_surface->client_pending.layer], 680 wl_list_insert(&output->layers[layer_surface->pending.layer],
655 &sway_layer->link); 681 &sway_layer->link);
656 682
657 // Temporarily set the layer's current state to client_pending 683 // Temporarily set the layer's current state to pending
658 // So that we can easily arrange it 684 // So that we can easily arrange it
659 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 685 struct wlr_layer_surface_v1_state old_state = layer_surface->current;
660 layer_surface->current = layer_surface->client_pending; 686 layer_surface->current = layer_surface->pending;
661 arrange_layers(output); 687 arrange_layers(output);
662 layer_surface->current = old_state; 688 layer_surface->current = old_state;
663} 689}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 5edc8f96..7bb9dab2 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -4,15 +4,17 @@
4#include <strings.h> 4#include <strings.h>
5#include <time.h> 5#include <time.h>
6#include <wayland-server-core.h> 6#include <wayland-server-core.h>
7#include <wlr/backend/drm.h>
8#include <wlr/backend/headless.h>
7#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_drm_lease_v1.h>
10#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
11#include <wlr/types/wlr_output_damage.h> 13#include <wlr/types/wlr_output_damage.h>
12#include <wlr/types/wlr_output_layout.h> 14#include <wlr/types/wlr_output_layout.h>
13#include <wlr/types/wlr_output.h> 15#include <wlr/types/wlr_output.h>
14#include <wlr/types/wlr_presentation_time.h> 16#include <wlr/types/wlr_presentation_time.h>
15#include <wlr/types/wlr_surface.h> 17#include <wlr/types/wlr_compositor.h>
16#include <wlr/util/region.h> 18#include <wlr/util/region.h>
17#include "config.h" 19#include "config.h"
18#include "log.h" 20#include "log.h"
@@ -56,26 +58,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
56 return NULL; 58 return NULL;
57} 59}
58 60
59/**
60 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
61 * the child position is (*sx, *sy) and its size is (sw, sh).
62 */
63static void rotate_child_position(double *sx, double *sy, double sw, double sh,
64 double pw, double ph, float rotation) {
65 if (rotation == 0.0f) {
66 return;
67 }
68
69 // Coordinates relative to the center of the subsurface
70 double ox = *sx - pw/2 + sw/2,
71 oy = *sy - ph/2 + sh/2;
72 // Rotated coordinates
73 double rx = cos(-rotation)*ox - sin(-rotation)*oy,
74 ry = cos(-rotation)*oy + sin(-rotation)*ox;
75 *sx = rx + pw/2 - sw/2;
76 *sy = ry + ph/2 - sh/2;
77}
78
79struct surface_iterator_data { 61struct surface_iterator_data {
80 sway_surface_iterator_func_t user_iterator; 62 sway_surface_iterator_func_t user_iterator;
81 void *user_data; 63 void *user_data;
@@ -84,7 +66,6 @@ struct surface_iterator_data {
84 struct sway_view *view; 66 struct sway_view *view;
85 double ox, oy; 67 double ox, oy;
86 int width, height; 68 int width, height;
87 float rotation;
88}; 69};
89 70
90static bool get_surface_box(struct surface_iterator_data *data, 71static bool get_surface_box(struct surface_iterator_data *data,
@@ -99,14 +80,9 @@ static bool get_surface_box(struct surface_iterator_data *data,
99 int sw = surface->current.width; 80 int sw = surface->current.width;
100 int sh = surface->current.height; 81 int sh = surface->current.height;
101 82
102 double _sx = sx + surface->sx;
103 double _sy = sy + surface->sy;
104 rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height,
105 data->rotation);
106
107 struct wlr_box box = { 83 struct wlr_box box = {
108 .x = data->ox + _sx, 84 .x = floor(data->ox + sx),
109 .y = data->oy + _sy, 85 .y = floor(data->oy + sy),
110 .width = sw, 86 .width = sw,
111 .height = sh, 87 .height = sh,
112 }; 88 };
@@ -114,16 +90,13 @@ static bool get_surface_box(struct surface_iterator_data *data,
114 memcpy(surface_box, &box, sizeof(struct wlr_box)); 90 memcpy(surface_box, &box, sizeof(struct wlr_box));
115 } 91 }
116 92
117 struct wlr_box rotated_box;
118 wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
119
120 struct wlr_box output_box = { 93 struct wlr_box output_box = {
121 .width = output->width, 94 .width = output->width,
122 .height = output->height, 95 .height = output->height,
123 }; 96 };
124 97
125 struct wlr_box intersection; 98 struct wlr_box intersection;
126 return wlr_box_intersection(&intersection, &output_box, &rotated_box); 99 return wlr_box_intersection(&intersection, &output_box, &box);
127} 100}
128 101
129static void output_for_each_surface_iterator(struct wlr_surface *surface, 102static void output_for_each_surface_iterator(struct wlr_surface *surface,
@@ -136,7 +109,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface,
136 return; 109 return;
137 } 110 }
138 111
139 data->user_iterator(data->output, data->view, surface, &box, data->rotation, 112 data->user_iterator(data->output, data->view, surface, &box,
140 data->user_data); 113 data->user_data);
141} 114}
142 115
@@ -152,7 +125,6 @@ void output_surface_for_each_surface(struct sway_output *output,
152 .oy = oy, 125 .oy = oy,
153 .width = surface->current.width, 126 .width = surface->current.width,
154 .height = surface->current.height, 127 .height = surface->current.height,
155 .rotation = 0,
156 }; 128 };
157 129
158 wlr_surface_for_each_surface(surface, 130 wlr_surface_for_each_surface(surface,
@@ -173,7 +145,6 @@ void output_view_for_each_surface(struct sway_output *output,
173 - view->geometry.y, 145 - view->geometry.y,
174 .width = view->container->current.content_width, 146 .width = view->container->current.content_width,
175 .height = view->container->current.content_height, 147 .height = view->container->current.content_height,
176 .rotation = 0, // TODO
177 }; 148 };
178 149
179 view_for_each_surface(view, output_for_each_surface_iterator, &data); 150 view_for_each_surface(view, output_for_each_surface_iterator, &data);
@@ -193,7 +164,6 @@ void output_view_for_each_popup_surface(struct sway_output *output,
193 - view->geometry.y, 164 - view->geometry.y,
194 .width = view->container->current.content_width, 165 .width = view->container->current.content_width,
195 .height = view->container->current.content_height, 166 .height = view->container->current.content_height,
196 .rotation = 0, // TODO
197 }; 167 };
198 168
199 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); 169 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
@@ -206,40 +176,19 @@ void output_layer_for_each_surface(struct sway_output *output,
206 wl_list_for_each(layer_surface, layer_surfaces, link) { 176 wl_list_for_each(layer_surface, layer_surfaces, link) {
207 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 177 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
208 layer_surface->layer_surface; 178 layer_surface->layer_surface;
209 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, 179 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
210 layer_surface->geo.x, layer_surface->geo.y, iterator, 180 struct surface_iterator_data data = {
211 user_data); 181 .user_iterator = iterator,
212 182 .user_data = user_data,
213 struct wlr_xdg_popup *state; 183 .output = output,
214 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 184 .view = NULL,
215 struct wlr_xdg_surface *popup = state->base; 185 .ox = layer_surface->geo.x,
216 if (!popup->configured) { 186 .oy = layer_surface->geo.y,
217 continue; 187 .width = surface->current.width,
218 } 188 .height = surface->current.height,
219 189 };
220 double popup_sx, popup_sy; 190 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
221 popup_sx = layer_surface->geo.x + 191 output_for_each_surface_iterator, &data);
222 popup->popup->geometry.x - popup->geometry.x;
223 popup_sy = layer_surface->geo.y +
224 popup->popup->geometry.y - popup->geometry.y;
225
226 struct wlr_surface *surface = popup->surface;
227
228 struct surface_iterator_data data = {
229 .user_iterator = iterator,
230 .user_data = user_data,
231 .output = output,
232 .view = NULL,
233 .ox = popup_sx,
234 .oy = popup_sy,
235 .width = surface->current.width,
236 .height = surface->current.height,
237 .rotation = 0,
238 };
239
240 wlr_xdg_surface_for_each_surface(
241 popup, output_for_each_surface_iterator, &data);
242 }
243 } 192 }
244} 193}
245 194
@@ -264,37 +213,19 @@ void output_layer_for_each_popup_surface(struct sway_output *output,
264 wl_list_for_each(layer_surface, layer_surfaces, link) { 213 wl_list_for_each(layer_surface, layer_surfaces, link) {
265 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 214 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
266 layer_surface->layer_surface; 215 layer_surface->layer_surface;
267 216 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
268 struct wlr_xdg_popup *state; 217 struct surface_iterator_data data = {
269 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 218 .user_iterator = iterator,
270 struct wlr_xdg_surface *popup = state->base; 219 .user_data = user_data,
271 if (!popup->configured) { 220 .output = output,
272 continue; 221 .view = NULL,
273 } 222 .ox = layer_surface->geo.x,
274 223 .oy = layer_surface->geo.y,
275 double popup_sx, popup_sy; 224 .width = surface->current.width,
276 popup_sx = layer_surface->geo.x + 225 .height = surface->current.height,
277 popup->popup->geometry.x - popup->geometry.x; 226 };
278 popup_sy = layer_surface->geo.y + 227 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
279 popup->popup->geometry.y - popup->geometry.y; 228 output_for_each_surface_iterator, &data);
280
281 struct wlr_surface *surface = popup->surface;
282
283 struct surface_iterator_data data = {
284 .user_iterator = iterator,
285 .user_data = user_data,
286 .output = output,
287 .view = NULL,
288 .ox = popup_sx,
289 .oy = popup_sy,
290 .width = surface->current.width,
291 .height = surface->current.height,
292 .rotation = 0,
293 };
294
295 wlr_xdg_surface_for_each_surface(
296 popup, output_for_each_surface_iterator, &data);
297 }
298 } 229 }
299} 230}
300 231
@@ -344,6 +275,25 @@ static void for_each_surface_container_iterator(struct sway_container *con,
344 275
345static void output_for_each_surface(struct sway_output *output, 276static void output_for_each_surface(struct sway_output *output,
346 sway_surface_iterator_func_t iterator, void *user_data) { 277 sway_surface_iterator_func_t iterator, void *user_data) {
278 if (server.session_lock.locked) {
279 if (server.session_lock.lock == NULL) {
280 return;
281 }
282 struct wlr_session_lock_surface_v1 *lock_surface;
283 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
284 if (lock_surface->output != output->wlr_output) {
285 continue;
286 }
287 if (!lock_surface->mapped) {
288 continue;
289 }
290
291 output_surface_for_each_surface(output, lock_surface->surface,
292 0.0, 0.0, iterator, user_data);
293 }
294 return;
295 }
296
347 if (output_has_opaque_overlay_layer_surface(output)) { 297 if (output_has_opaque_overlay_layer_surface(output)) {
348 goto overlay; 298 goto overlay;
349 } 299 }
@@ -463,9 +413,9 @@ struct send_frame_done_data {
463 int msec_until_refresh; 413 int msec_until_refresh;
464}; 414};
465 415
466static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, 416static void send_frame_done_iterator(struct sway_output *output,
467 struct wlr_surface *surface, struct wlr_box *box, float rotation, 417 struct sway_view *view, struct wlr_surface *surface,
468 void *user_data) { 418 struct wlr_box *box, void *user_data) {
469 int view_max_render_time = 0; 419 int view_max_render_time = 0;
470 if (view != NULL) { 420 if (view != NULL) {
471 view_max_render_time = view->max_render_time; 421 view_max_render_time = view->max_render_time;
@@ -488,9 +438,9 @@ static void send_frame_done(struct sway_output *output, struct send_frame_done_d
488 output_for_each_surface(output, send_frame_done_iterator, data); 438 output_for_each_surface(output, send_frame_done_iterator, data);
489} 439}
490 440
491static void count_surface_iterator(struct sway_output *output, struct sway_view *view, 441static void count_surface_iterator(struct sway_output *output,
492 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 442 struct sway_view *view, struct wlr_surface *surface,
493 void *data) { 443 struct wlr_box *box, void *data) {
494 size_t *n = data; 444 size_t *n = data;
495 (*n)++; 445 (*n)++;
496} 446}
@@ -503,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output,
503 return false; 453 return false;
504 } 454 }
505 455
456 if (server.session_lock.locked) {
457 return false;
458 }
459
506 if (!wl_list_empty(&view->saved_buffers)) { 460 if (!wl_list_empty(&view->saved_buffers)) {
507 return false; 461 return false;
508 } 462 }
@@ -577,7 +531,7 @@ static int output_repaint_timer_handler(void *data) {
577 fullscreen_con = workspace->current.fullscreen; 531 fullscreen_con = workspace->current.fullscreen;
578 } 532 }
579 533
580 if (fullscreen_con && fullscreen_con->view) { 534 if (fullscreen_con && fullscreen_con->view && !debug.noscanout) {
581 // Try to scan-out the fullscreen view 535 // Try to scan-out the fullscreen view
582 static bool last_scanned_out = false; 536 static bool last_scanned_out = false;
583 bool scanned_out = 537 bool scanned_out =
@@ -590,6 +544,7 @@ static int output_repaint_timer_handler(void *data) {
590 if (last_scanned_out && !scanned_out) { 544 if (last_scanned_out && !scanned_out) {
591 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 545 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s",
592 output->wlr_output->name); 546 output->wlr_output->name);
547 output_damage_whole(output);
593 } 548 }
594 last_scanned_out = scanned_out; 549 last_scanned_out = scanned_out;
595 550
@@ -693,38 +648,30 @@ void output_damage_whole(struct sway_output *output) {
693 } 648 }
694} 649}
695 650
696static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, 651static void damage_surface_iterator(struct sway_output *output,
697 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 652 struct sway_view *view, struct wlr_surface *surface,
698 void *_data) { 653 struct wlr_box *_box, void *_data) {
699 bool *data = _data; 654 bool *data = _data;
700 bool whole = *data; 655 bool whole = *data;
701 656
702 struct wlr_box box = *_box; 657 struct wlr_box box = *_box;
703 scale_box(&box, output->wlr_output->scale); 658 scale_box(&box, output->wlr_output->scale);
704 659
705 int center_x = box.x + box.width/2; 660 pixman_region32_t damage;
706 int center_y = box.y + box.height/2; 661 pixman_region32_init(&damage);
707 662 wlr_surface_get_effective_damage(surface, &damage);
708 if (pixman_region32_not_empty(&surface->buffer_damage)) { 663 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
709 pixman_region32_t damage; 664 if (ceil(output->wlr_output->scale) > surface->current.scale) {
710 pixman_region32_init(&damage); 665 // When scaling up a surface, it'll become blurry so we need to
711 wlr_surface_get_effective_damage(surface, &damage); 666 // expand the damage region
712 wlr_region_scale(&damage, &damage, output->wlr_output->scale); 667 wlr_region_expand(&damage, &damage,
713 if (ceil(output->wlr_output->scale) > surface->current.scale) { 668 ceil(output->wlr_output->scale) - surface->current.scale);
714 // When scaling up a surface, it'll become blurry so we need to
715 // expand the damage region
716 wlr_region_expand(&damage, &damage,
717 ceil(output->wlr_output->scale) - surface->current.scale);
718 }
719 pixman_region32_translate(&damage, box.x, box.y);
720 wlr_region_rotated_bounds(&damage, &damage, rotation,
721 center_x, center_y);
722 wlr_output_damage_add(output->damage, &damage);
723 pixman_region32_fini(&damage);
724 } 669 }
670 pixman_region32_translate(&damage, box.x, box.y);
671 wlr_output_damage_add(output->damage, &damage);
672 pixman_region32_fini(&damage);
725 673
726 if (whole) { 674 if (whole) {
727 wlr_box_rotated_bounds(&box, &box, rotation);
728 wlr_output_damage_add_box(output->damage, &box); 675 wlr_output_damage_add_box(output->damage, &box);
729 } 676 }
730 677
@@ -808,19 +755,20 @@ static void update_output_manager_config(struct sway_server *server) {
808 755
809 struct sway_output *output; 756 struct sway_output *output;
810 wl_list_for_each(output, &root->all_outputs, link) { 757 wl_list_for_each(output, &root->all_outputs, link) {
811 if (output == root->noop_output) { 758 if (output == root->fallback_output) {
812 continue; 759 continue;
813 } 760 }
814 struct wlr_output_configuration_head_v1 *config_head = 761 struct wlr_output_configuration_head_v1 *config_head =
815 wlr_output_configuration_head_v1_create(config, output->wlr_output); 762 wlr_output_configuration_head_v1_create(config, output->wlr_output);
816 struct wlr_box *output_box = wlr_output_layout_get_box( 763 struct wlr_box output_box;
817 root->output_layout, output->wlr_output); 764 wlr_output_layout_get_box(root->output_layout,
818 // We mark the output enabled even if it is switched off by DPMS 765 output->wlr_output, &output_box);
819 config_head->state.enabled = output->enabled; 766 // We mark the output enabled when it's switched off but not disabled
767 config_head->state.enabled = output->current_mode != NULL && output->enabled;
820 config_head->state.mode = output->current_mode; 768 config_head->state.mode = output->current_mode;
821 if (output_box) { 769 if (!wlr_box_empty(&output_box)) {
822 config_head->state.x = output_box->x; 770 config_head->state.x = output_box.x;
823 config_head->state.y = output_box->y; 771 config_head->state.y = output_box.y;
824 } 772 }
825 } 773 }
826 774
@@ -830,18 +778,22 @@ static void update_output_manager_config(struct sway_server *server) {
830static void handle_destroy(struct wl_listener *listener, void *data) { 778static void handle_destroy(struct wl_listener *listener, void *data) {
831 struct sway_output *output = wl_container_of(listener, output, destroy); 779 struct sway_output *output = wl_container_of(listener, output, destroy);
832 struct sway_server *server = output->server; 780 struct sway_server *server = output->server;
833 wl_signal_emit(&output->events.destroy, output); 781 output_begin_destroy(output);
834 782
835 if (output->enabled) { 783 if (output->enabled) {
836 output_disable(output); 784 output_disable(output);
837 } 785 }
838 output_begin_destroy(output); 786
787 wl_list_remove(&output->link);
839 788
840 wl_list_remove(&output->destroy.link); 789 wl_list_remove(&output->destroy.link);
841 wl_list_remove(&output->commit.link); 790 wl_list_remove(&output->commit.link);
842 wl_list_remove(&output->mode.link); 791 wl_list_remove(&output->mode.link);
843 wl_list_remove(&output->present.link); 792 wl_list_remove(&output->present.link);
844 793
794 output->wlr_output->data = NULL;
795 output->wlr_output = NULL;
796
845 transaction_commit_dirty(); 797 transaction_commit_dirty();
846 798
847 update_output_manager_config(server); 799 update_output_manager_config(server);
@@ -902,7 +854,7 @@ static void handle_present(struct wl_listener *listener, void *data) {
902 struct sway_output *output = wl_container_of(listener, output, present); 854 struct sway_output *output = wl_container_of(listener, output, present);
903 struct wlr_output_event_present *output_event = data; 855 struct wlr_output_event_present *output_event = data;
904 856
905 if (!output->enabled) { 857 if (!output->enabled || !output_event->presented) {
906 return; 858 return;
907 } 859 }
908 860
@@ -910,10 +862,39 @@ static void handle_present(struct wl_listener *listener, void *data) {
910 output->refresh_nsec = output_event->refresh; 862 output->refresh_nsec = output_event->refresh;
911} 863}
912 864
865static unsigned int last_headless_num = 0;
866
913void handle_new_output(struct wl_listener *listener, void *data) { 867void handle_new_output(struct wl_listener *listener, void *data) {
914 struct sway_server *server = wl_container_of(listener, server, new_output); 868 struct sway_server *server = wl_container_of(listener, server, new_output);
915 struct wlr_output *wlr_output = data; 869 struct wlr_output *wlr_output = data;
916 sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 870
871 if (wlr_output == root->fallback_output->wlr_output) {
872 return;
873 }
874
875 if (wlr_output_is_headless(wlr_output)) {
876 char name[64];
877 snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num);
878 wlr_output_set_name(wlr_output, name);
879 }
880
881 sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)",
882 wlr_output, wlr_output->name, wlr_output->non_desktop);
883
884 if (wlr_output->non_desktop) {
885 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
886 if (server->drm_lease_manager) {
887 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
888 wlr_output);
889 }
890 return;
891 }
892
893 if (!wlr_output_init_render(wlr_output, server->allocator,
894 server->renderer)) {
895 sway_log(SWAY_ERROR, "Failed to init output render");
896 return;
897 }
917 898
918 struct sway_output *output = output_create(wlr_output); 899 struct sway_output *output = output_create(wlr_output);
919 if (!output) { 900 if (!output) {
@@ -1047,10 +1028,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
1047 struct output_config *oc = new_output_config(output->wlr_output->name); 1028 struct output_config *oc = new_output_config(output->wlr_output->name);
1048 switch (event->mode) { 1029 switch (event->mode) {
1049 case ZWLR_OUTPUT_POWER_V1_MODE_OFF: 1030 case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
1050 oc->dpms_state = DPMS_OFF; 1031 oc->power = 0;
1051 break; 1032 break;
1052 case ZWLR_OUTPUT_POWER_V1_MODE_ON: 1033 case ZWLR_OUTPUT_POWER_V1_MODE_ON:
1053 oc->dpms_state = DPMS_ON; 1034 oc->power = 1;
1054 break; 1035 break;
1055 } 1036 }
1056 oc = store_output_config(oc); 1037 oc = store_output_config(oc);
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index bd85282c..ed9ad490 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -7,13 +7,12 @@
7#include <wayland-server-core.h> 7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h> 8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_box.h>
11#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
12#include <wlr/types/wlr_matrix.h> 11#include <wlr/types/wlr_matrix.h>
13#include <wlr/types/wlr_output_damage.h> 12#include <wlr/types/wlr_output_damage.h>
14#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
15#include <wlr/types/wlr_output.h> 14#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_surface.h> 15#include <wlr/types/wlr_compositor.h>
17#include <wlr/util/region.h> 16#include <wlr/util/region.h>
18#include "log.h" 17#include "log.h"
19#include "config.h" 18#include "config.h"
@@ -32,6 +31,7 @@
32struct render_data { 31struct render_data {
33 pixman_region32_t *damage; 32 pixman_region32_t *damage;
34 float alpha; 33 float alpha;
34 struct wlr_box *clip_box;
35}; 35};
36 36
37/** 37/**
@@ -52,7 +52,7 @@ static int scale_length(int length, int offset, float scale) {
52 52
53static void scissor_output(struct wlr_output *wlr_output, 53static void scissor_output(struct wlr_output *wlr_output,
54 pixman_box32_t *rect) { 54 pixman_box32_t *rect) {
55 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); 55 struct wlr_renderer *renderer = wlr_output->renderer;
56 assert(renderer); 56 assert(renderer);
57 57
58 struct wlr_box box = { 58 struct wlr_box box = {
@@ -100,13 +100,9 @@ static void render_texture(struct wlr_output *wlr_output,
100 pixman_region32_t *output_damage, struct wlr_texture *texture, 100 pixman_region32_t *output_damage, struct wlr_texture *texture,
101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box, 101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
102 const float matrix[static 9], float alpha) { 102 const float matrix[static 9], float alpha) {
103 struct wlr_renderer *renderer = 103 struct wlr_renderer *renderer = wlr_output->renderer;
104 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data; 104 struct sway_output *output = wlr_output->data;
106 105
107 struct wlr_gles2_texture_attribs attribs;
108 wlr_gles2_texture_get_attribs(texture, &attribs);
109
110 pixman_region32_t damage; 106 pixman_region32_t damage;
111 pixman_region32_init(&damage); 107 pixman_region32_init(&damage);
112 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, 108 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
@@ -133,9 +129,9 @@ damage_finish:
133 pixman_region32_fini(&damage); 129 pixman_region32_fini(&damage);
134} 130}
135 131
136static void render_surface_iterator(struct sway_output *output, struct sway_view *view, 132static void render_surface_iterator(struct sway_output *output,
137 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 133 struct sway_view *view, struct wlr_surface *surface,
138 void *_data) { 134 struct wlr_box *_box, void *_data) {
139 struct render_data *data = _data; 135 struct render_data *data = _data;
140 struct wlr_output *wlr_output = output->wlr_output; 136 struct wlr_output *wlr_output = output->wlr_output;
141 pixman_region32_t *output_damage = data->damage; 137 pixman_region32_t *output_damage = data->damage;
@@ -149,15 +145,23 @@ static void render_surface_iterator(struct sway_output *output, struct sway_view
149 struct wlr_fbox src_box; 145 struct wlr_fbox src_box;
150 wlr_surface_get_buffer_source_box(surface, &src_box); 146 wlr_surface_get_buffer_source_box(surface, &src_box);
151 147
152 struct wlr_box dst_box = *_box; 148 struct wlr_box proj_box = *_box;
153 scale_box(&dst_box, wlr_output->scale); 149 scale_box(&proj_box, wlr_output->scale);
154 150
155 float matrix[9]; 151 float matrix[9];
156 enum wl_output_transform transform = 152 enum wl_output_transform transform =
157 wlr_output_transform_invert(surface->current.transform); 153 wlr_output_transform_invert(surface->current.transform);
158 wlr_matrix_project_box(matrix, &dst_box, transform, rotation, 154 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
159 wlr_output->transform_matrix); 155 wlr_output->transform_matrix);
160 156
157 struct wlr_box dst_box = *_box;
158 struct wlr_box *clip_box = data->clip_box;
159 if (clip_box != NULL) {
160 dst_box.width = fmin(dst_box.width, clip_box->width);
161 dst_box.height = fmin(dst_box.height, clip_box->height);
162 }
163 scale_box(&dst_box, wlr_output->scale);
164
161 render_texture(wlr_output, output_damage, texture, 165 render_texture(wlr_output, output_damage, texture,
162 &src_box, &dst_box, matrix, alpha); 166 &src_box, &dst_box, matrix, alpha);
163 167
@@ -213,8 +217,7 @@ void render_rect(struct sway_output *output,
213 pixman_region32_t *output_damage, const struct wlr_box *_box, 217 pixman_region32_t *output_damage, const struct wlr_box *_box,
214 float color[static 4]) { 218 float color[static 4]) {
215 struct wlr_output *wlr_output = output->wlr_output; 219 struct wlr_output *wlr_output = output->wlr_output;
216 struct wlr_renderer *renderer = 220 struct wlr_renderer *renderer = wlr_output->renderer;
217 wlr_backend_get_renderer(wlr_output->backend);
218 221
219 struct wlr_box box; 222 struct wlr_box box;
220 memcpy(&box, _box, sizeof(struct wlr_box)); 223 memcpy(&box, _box, sizeof(struct wlr_box));
@@ -256,6 +259,14 @@ static void render_view_toplevels(struct sway_view *view,
256 .damage = damage, 259 .damage = damage,
257 .alpha = alpha, 260 .alpha = alpha,
258 }; 261 };
262 struct wlr_box clip_box;
263 if (!container_is_current_floating(view->container)) {
264 // As we pass the geometry offsets to the surface iterator, we will
265 // need to account for the offsets in the clip dimensions.
266 clip_box.width = view->container->current.content_width + view->geometry.x;
267 clip_box.height = view->container->current.content_height + view->geometry.y;
268 data.clip_box = &clip_box;
269 }
259 // Render all toplevels without descending into popups 270 // Render all toplevels without descending into popups
260 double ox = view->container->surface_x - 271 double ox = view->container->surface_x -
261 output->lx - view->geometry.x; 272 output->lx - view->geometry.x;
@@ -282,17 +293,18 @@ static void render_saved_view(struct sway_view *view,
282 if (wl_list_empty(&view->saved_buffers)) { 293 if (wl_list_empty(&view->saved_buffers)) {
283 return; 294 return;
284 } 295 }
296
297 bool floating = container_is_current_floating(view->container);
298
285 struct sway_saved_buffer *saved_buf; 299 struct sway_saved_buffer *saved_buf;
286 wl_list_for_each(saved_buf, &view->saved_buffers, link) { 300 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
287 if (!saved_buf->buffer->texture) { 301 if (!saved_buf->buffer->texture) {
288 continue; 302 continue;
289 } 303 }
290 304
291 struct wlr_box box = { 305 struct wlr_box proj_box = {
292 .x = view->container->surface_x - output->lx - 306 .x = saved_buf->x - view->saved_geometry.x - output->lx,
293 view->saved_geometry.x + saved_buf->x, 307 .y = saved_buf->y - view->saved_geometry.y - output->ly,
294 .y = view->container->surface_y - output->ly -
295 view->saved_geometry.y + saved_buf->y,
296 .width = saved_buf->width, 308 .width = saved_buf->width,
297 .height = saved_buf->height, 309 .height = saved_buf->height,
298 }; 310 };
@@ -303,20 +315,31 @@ static void render_saved_view(struct sway_view *view,
303 }; 315 };
304 316
305 struct wlr_box intersection; 317 struct wlr_box intersection;
306 bool intersects = wlr_box_intersection(&intersection, &output_box, &box); 318 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
307 if (!intersects) { 319 if (!intersects) {
308 continue; 320 continue;
309 } 321 }
310 322
311 scale_box(&box, wlr_output->scale); 323 struct wlr_box dst_box = proj_box;
324 scale_box(&proj_box, wlr_output->scale);
312 325
313 float matrix[9]; 326 float matrix[9];
314 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); 327 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
315 wlr_matrix_project_box(matrix, &box, transform, 0, 328 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
316 wlr_output->transform_matrix); 329 wlr_output->transform_matrix);
317 330
331 if (!floating) {
332 dst_box.width = fmin(dst_box.width,
333 view->container->current.content_width -
334 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
335 dst_box.height = fmin(dst_box.height,
336 view->container->current.content_height -
337 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
338 }
339 scale_box(&dst_box, wlr_output->scale);
340
318 render_texture(wlr_output, damage, saved_buf->buffer->texture, 341 render_texture(wlr_output, damage, saved_buf->buffer->texture,
319 &saved_buf->source_box, &box, matrix, alpha); 342 &saved_buf->source_box, &dst_box, matrix, alpha);
320 } 343 }
321 344
322 // FIXME: we should set the surface that this saved buffer originates from 345 // FIXME: we should set the surface that this saved buffer originates from
@@ -348,8 +371,8 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
348 if (state->border_left) { 371 if (state->border_left) {
349 memcpy(&color, colors->child_border, sizeof(float) * 4); 372 memcpy(&color, colors->child_border, sizeof(float) * 4);
350 premultiply_alpha(color, con->alpha); 373 premultiply_alpha(color, con->alpha);
351 box.x = state->x; 374 box.x = floor(state->x);
352 box.y = state->content_y; 375 box.y = floor(state->content_y);
353 box.width = state->border_thickness; 376 box.width = state->border_thickness;
354 box.height = state->content_height; 377 box.height = state->content_height;
355 scale_box(&box, output_scale); 378 scale_box(&box, output_scale);
@@ -361,14 +384,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
361 container_current_parent_layout(con); 384 container_current_parent_layout(con);
362 385
363 if (state->border_right) { 386 if (state->border_right) {
364 if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { 387 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
365 memcpy(&color, colors->indicator, sizeof(float) * 4); 388 memcpy(&color, colors->indicator, sizeof(float) * 4);
366 } else { 389 } else {
367 memcpy(&color, colors->child_border, sizeof(float) * 4); 390 memcpy(&color, colors->child_border, sizeof(float) * 4);
368 } 391 }
369 premultiply_alpha(color, con->alpha); 392 premultiply_alpha(color, con->alpha);
370 box.x = state->content_x + state->content_width; 393 box.x = floor(state->content_x + state->content_width);
371 box.y = state->content_y; 394 box.y = floor(state->content_y);
372 box.width = state->border_thickness; 395 box.width = state->border_thickness;
373 box.height = state->content_height; 396 box.height = state->content_height;
374 scale_box(&box, output_scale); 397 scale_box(&box, output_scale);
@@ -376,14 +399,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
376 } 399 }
377 400
378 if (state->border_bottom) { 401 if (state->border_bottom) {
379 if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { 402 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
380 memcpy(&color, colors->indicator, sizeof(float) * 4); 403 memcpy(&color, colors->indicator, sizeof(float) * 4);
381 } else { 404 } else {
382 memcpy(&color, colors->child_border, sizeof(float) * 4); 405 memcpy(&color, colors->child_border, sizeof(float) * 4);
383 } 406 }
384 premultiply_alpha(color, con->alpha); 407 premultiply_alpha(color, con->alpha);
385 box.x = state->x; 408 box.x = floor(state->x);
386 box.y = state->content_y + state->content_height; 409 box.y = floor(state->content_y + state->content_height);
387 box.width = state->width; 410 box.width = state->width;
388 box.height = state->border_thickness; 411 box.height = state->border_thickness;
389 scale_box(&box, output_scale); 412 scale_box(&box, output_scale);
@@ -464,9 +487,10 @@ static void render_titlebar(struct sway_output *output,
464 int ob_marks_x = 0; // output-buffer-local 487 int ob_marks_x = 0; // output-buffer-local
465 int ob_marks_width = 0; // output-buffer-local 488 int ob_marks_width = 0; // output-buffer-local
466 if (config->show_marks && marks_texture) { 489 if (config->show_marks && marks_texture) {
467 struct wlr_box texture_box; 490 struct wlr_box texture_box = {
468 wlr_texture_get_size(marks_texture, 491 .width = marks_texture->width,
469 &texture_box.width, &texture_box.height); 492 .height = marks_texture->height,
493 };
470 ob_marks_width = texture_box.width; 494 ob_marks_width = texture_box.width;
471 495
472 // The marks texture might be shorter than the config->font_height, in 496 // The marks texture might be shorter than the config->font_height, in
@@ -517,15 +541,23 @@ static void render_titlebar(struct sway_output *output,
517 int ob_title_x = 0; // output-buffer-local 541 int ob_title_x = 0; // output-buffer-local
518 int ob_title_width = 0; // output-buffer-local 542 int ob_title_width = 0; // output-buffer-local
519 if (title_texture) { 543 if (title_texture) {
520 struct wlr_box texture_box; 544 struct wlr_box texture_box = {
521 wlr_texture_get_size(title_texture, 545 .width = title_texture->width,
522 &texture_box.width, &texture_box.height); 546 .height = title_texture->height,
547 };
548
549 // The effective output may be NULL when con is not on any output.
550 // This can happen because we render all children of containers,
551 // even those that are out of the bounds of any output.
552 struct sway_output *effective = container_get_effective_output(con);
553 float title_scale = effective ? effective->wlr_output->scale : output_scale;
554 texture_box.width = texture_box.width * output_scale / title_scale;
555 texture_box.height = texture_box.height * output_scale / title_scale;
523 ob_title_width = texture_box.width; 556 ob_title_width = texture_box.width;
524 557
525 // The title texture might be shorter than the config->font_height, 558 // The title texture might be shorter than the config->font_height,
526 // in which case we need to pad it above and below. 559 // in which case we need to pad it above and below.
527 int ob_padding_above = round((config->font_baseline - 560 int ob_padding_above = round((titlebar_v_padding -
528 con->title_baseline + titlebar_v_padding -
529 titlebar_border_thickness) * output_scale); 561 titlebar_border_thickness) * output_scale);
530 int ob_padding_below = ob_bg_height - ob_padding_above - 562 int ob_padding_below = ob_bg_height - ob_padding_above -
531 texture_box.height; 563 texture_box.height;
@@ -660,8 +692,8 @@ static void render_top_border(struct sway_output *output,
660 // Child border - top edge 692 // Child border - top edge
661 memcpy(&color, colors->child_border, sizeof(float) * 4); 693 memcpy(&color, colors->child_border, sizeof(float) * 4);
662 premultiply_alpha(color, con->alpha); 694 premultiply_alpha(color, con->alpha);
663 box.x = state->x; 695 box.x = floor(state->x);
664 box.y = state->y; 696 box.y = floor(state->y);
665 box.width = state->width; 697 box.width = state->width;
666 box.height = state->border_thickness; 698 box.height = state->border_thickness;
667 scale_box(&box, output_scale); 699 scale_box(&box, output_scale);
@@ -716,8 +748,8 @@ static void render_containers_linear(struct sway_output *output,
716 } 748 }
717 749
718 if (state->border == B_NORMAL) { 750 if (state->border == B_NORMAL) {
719 render_titlebar(output, damage, child, state->x, 751 render_titlebar(output, damage, child, floor(state->x),
720 state->y, state->width, colors, 752 floor(state->y), state->width, colors,
721 title_texture, marks_texture); 753 title_texture, marks_texture);
722 } else if (state->border == B_PIXEL) { 754 } else if (state->border == B_PIXEL) {
723 render_top_border(output, damage, child, colors); 755 render_top_border(output, damage, child, colors);
@@ -730,6 +762,14 @@ static void render_containers_linear(struct sway_output *output,
730 } 762 }
731} 763}
732 764
765static bool container_is_focused(struct sway_container *con, void *data) {
766 return con->current.focused;
767}
768
769static bool container_has_focused_child(struct sway_container *con) {
770 return container_find_child(con, container_is_focused, NULL);
771}
772
733/** 773/**
734 * Render a container's children using the L_TABBED layout. 774 * Render a container's children using the L_TABBED layout.
735 */ 775 */
@@ -761,6 +801,10 @@ static void render_containers_tabbed(struct sway_output *output,
761 colors = &config->border_colors.focused; 801 colors = &config->border_colors.focused;
762 title_texture = child->title_focused; 802 title_texture = child->title_focused;
763 marks_texture = child->marks_focused; 803 marks_texture = child->marks_focused;
804 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
805 colors = &config->border_colors.focused_tab_title;
806 title_texture = child->title_focused_tab_title;
807 marks_texture = child->marks_focused_tab_title;
764 } else if (child == parent->active_child) { 808 } else if (child == parent->active_child) {
765 colors = &config->border_colors.focused_inactive; 809 colors = &config->border_colors.focused_inactive;
766 title_texture = child->title_focused_inactive; 810 title_texture = child->title_focused_inactive;
@@ -771,7 +815,7 @@ static void render_containers_tabbed(struct sway_output *output,
771 marks_texture = child->marks_unfocused; 815 marks_texture = child->marks_unfocused;
772 } 816 }
773 817
774 int x = cstate->x + tab_width * i; 818 int x = floor(cstate->x + tab_width * i);
775 819
776 // Make last tab use the remaining width of the parent 820 // Make last tab use the remaining width of the parent
777 if (i == parent->children->length - 1) { 821 if (i == parent->children->length - 1) {
@@ -826,7 +870,11 @@ static void render_containers_stacked(struct sway_output *output,
826 colors = &config->border_colors.focused; 870 colors = &config->border_colors.focused;
827 title_texture = child->title_focused; 871 title_texture = child->title_focused;
828 marks_texture = child->marks_focused; 872 marks_texture = child->marks_focused;
829 } else if (child == parent->active_child) { 873 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
874 colors = &config->border_colors.focused_tab_title;
875 title_texture = child->title_focused_tab_title;
876 marks_texture = child->marks_focused_tab_title;
877 } else if (child == parent->active_child) {
830 colors = &config->border_colors.focused_inactive; 878 colors = &config->border_colors.focused_inactive;
831 title_texture = child->title_focused_inactive; 879 title_texture = child->title_focused_inactive;
832 marks_texture = child->marks_focused_inactive; 880 marks_texture = child->marks_focused_inactive;
@@ -884,8 +932,8 @@ static void render_container(struct sway_output *output,
884 struct parent_data data = { 932 struct parent_data data = {
885 .layout = con->current.layout, 933 .layout = con->current.layout,
886 .box = { 934 .box = {
887 .x = con->current.x, 935 .x = floor(con->current.x),
888 .y = con->current.y, 936 .y = floor(con->current.y),
889 .width = con->current.width, 937 .width = con->current.width,
890 .height = con->current.height, 938 .height = con->current.height,
891 }, 939 },
@@ -901,8 +949,8 @@ static void render_workspace(struct sway_output *output,
901 struct parent_data data = { 949 struct parent_data data = {
902 .layout = ws->current.layout, 950 .layout = ws->current.layout,
903 .box = { 951 .box = {
904 .x = ws->current.x, 952 .x = floor(ws->current.x),
905 .y = ws->current.y, 953 .y = floor(ws->current.y),
906 .width = ws->current.width, 954 .width = ws->current.width,
907 .height = ws->current.height, 955 .height = ws->current.height,
908 }, 956 },
@@ -936,8 +984,8 @@ static void render_floating_container(struct sway_output *soutput,
936 } 984 }
937 985
938 if (con->current.border == B_NORMAL) { 986 if (con->current.border == B_NORMAL) {
939 render_titlebar(soutput, damage, con, con->current.x, 987 render_titlebar(soutput, damage, con, floor(con->current.x),
940 con->current.y, con->current.width, colors, 988 floor(con->current.y), con->current.width, colors,
941 title_texture, marks_texture); 989 title_texture, marks_texture);
942 } else if (con->current.border == B_PIXEL) { 990 } else if (con->current.border == B_PIXEL) {
943 render_top_border(soutput, damage, con, colors); 991 render_top_border(soutput, damage, con, colors);
@@ -959,7 +1007,7 @@ static void render_floating(struct sway_output *soutput,
959 } 1007 }
960 for (int k = 0; k < ws->current.floating->length; ++k) { 1008 for (int k = 0; k < ws->current.floating->length; ++k) {
961 struct sway_container *floater = ws->current.floating->items[k]; 1009 struct sway_container *floater = ws->current.floating->items[k];
962 if (floater->fullscreen_mode != FULLSCREEN_NONE) { 1010 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
963 continue; 1011 continue;
964 } 1012 }
965 render_floating_container(soutput, damage, floater); 1013 render_floating_container(soutput, damage, floater);
@@ -979,13 +1027,7 @@ static void render_seatops(struct sway_output *output,
979void output_render(struct sway_output *output, struct timespec *when, 1027void output_render(struct sway_output *output, struct timespec *when,
980 pixman_region32_t *damage) { 1028 pixman_region32_t *damage) {
981 struct wlr_output *wlr_output = output->wlr_output; 1029 struct wlr_output *wlr_output = output->wlr_output;
982 1030 struct wlr_renderer *renderer = output->server->renderer;
983 struct wlr_renderer *renderer =
984 wlr_backend_get_renderer(wlr_output->backend);
985 if (!sway_assert(renderer != NULL,
986 "expected the output backend to have a renderer")) {
987 return;
988 }
989 1031
990 struct sway_workspace *workspace = output->current.active_workspace; 1032 struct sway_workspace *workspace = output->current.active_workspace;
991 if (workspace == NULL) { 1033 if (workspace == NULL) {
@@ -999,6 +1041,12 @@ void output_render(struct sway_output *output, struct timespec *when,
999 1041
1000 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); 1042 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1001 1043
1044 if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1002 if (!pixman_region32_not_empty(damage)) { 1050 if (!pixman_region32_not_empty(damage)) {
1003 // Output isn't damaged but needs buffer swap 1051 // Output isn't damaged but needs buffer swap
1004 goto renderer_end; 1052 goto renderer_end;
@@ -1006,10 +1054,41 @@ void output_render(struct sway_output *output, struct timespec *when,
1006 1054
1007 if (debug.damage == DAMAGE_HIGHLIGHT) { 1055 if (debug.damage == DAMAGE_HIGHLIGHT) {
1008 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); 1056 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1009 } else if (debug.damage == DAMAGE_RERENDER) { 1057 }
1010 int width, height; 1058
1011 wlr_output_transformed_resolution(wlr_output, &width, &height); 1059 if (server.session_lock.locked) {
1012 pixman_region32_union_rect(damage, damage, 0, 0, width, height); 1060 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1061 if (server.session_lock.lock == NULL) {
1062 // abandoned lock -> red BG
1063 clear_color[0] = 1.f;
1064 }
1065 int nrects;
1066 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1067 for (int i = 0; i < nrects; ++i) {
1068 scissor_output(wlr_output, &rects[i]);
1069 wlr_renderer_clear(renderer, clear_color);
1070 }
1071
1072 if (server.session_lock.lock != NULL) {
1073 struct render_data data = {
1074 .damage = damage,
1075 .alpha = 1.0f,
1076 };
1077
1078 struct wlr_session_lock_surface_v1 *lock_surface;
1079 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
1080 if (lock_surface->output != wlr_output) {
1081 continue;
1082 }
1083 if (!lock_surface->mapped) {
1084 continue;
1085 }
1086
1087 output_surface_for_each_surface(output, lock_surface->surface,
1088 0.0, 0.0, render_surface_iterator, &data);
1089 }
1090 }
1091 goto renderer_end;
1013 } 1092 }
1014 1093
1015 if (output_has_opaque_overlay_layer_surface(output)) { 1094 if (output_has_opaque_overlay_layer_surface(output)) {
@@ -1110,7 +1189,7 @@ renderer_end:
1110 wlr_region_transform(&frame_damage, &output->damage->current, 1189 wlr_region_transform(&frame_damage, &output->damage->current,
1111 transform, width, height); 1190 transform, width, height);
1112 1191
1113 if (debug.damage == DAMAGE_HIGHLIGHT) { 1192 if (debug.damage != DAMAGE_DEFAULT) {
1114 pixman_region32_union_rect(&frame_damage, &frame_damage, 1193 pixman_region32_union_rect(&frame_damage, &frame_damage,
1115 0, 0, wlr_output->width, wlr_output->height); 1194 0, 0, wlr_output->width, wlr_output->height);
1116 } 1195 }
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
index 767b2045..1d7b536d 100644
--- a/sway/desktop/surface.c
+++ b/sway/desktop/surface.c
@@ -1,7 +1,7 @@
1#define _POSIX_C_SOURCE 200112L 1#define _POSIX_C_SOURCE 200112L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <time.h> 3#include <time.h>
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_compositor.h>
5#include "sway/server.h" 5#include "sway/server.h"
6#include "sway/surface.h" 6#include "sway/surface.h"
7 7
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index eac38991..f5a3a053 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -35,6 +35,8 @@ struct sway_transaction_instruction {
35 struct sway_container_state container_state; 35 struct sway_container_state container_state;
36 }; 36 };
37 uint32_t serial; 37 uint32_t serial;
38 bool server_request;
39 bool waiting;
38}; 40};
39 41
40static struct sway_transaction *transaction_create(void) { 42static struct sway_transaction *transaction_create(void) {
@@ -86,7 +88,11 @@ static void transaction_destroy(struct sway_transaction *transaction) {
86static void copy_output_state(struct sway_output *output, 88static void copy_output_state(struct sway_output *output,
87 struct sway_transaction_instruction *instruction) { 89 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state = &instruction->output_state; 90 struct sway_output_state *state = &instruction->output_state;
89 state->workspaces = create_list(); 91 if (state->workspaces) {
92 state->workspaces->length = 0;
93 } else {
94 state->workspaces = create_list();
95 }
90 list_cat(state->workspaces, output->workspaces); 96 list_cat(state->workspaces, output->workspaces);
91 97
92 state->active_workspace = output_get_active_workspace(output); 98 state->active_workspace = output_get_active_workspace(output);
@@ -104,8 +110,16 @@ static void copy_workspace_state(struct sway_workspace *ws,
104 state->layout = ws->layout; 110 state->layout = ws->layout;
105 111
106 state->output = ws->output; 112 state->output = ws->output;
107 state->floating = create_list(); 113 if (state->floating) {
108 state->tiling = create_list(); 114 state->floating->length = 0;
115 } else {
116 state->floating = create_list();
117 }
118 if (state->tiling) {
119 state->tiling->length = 0;
120 } else {
121 state->tiling = create_list();
122 }
109 list_cat(state->floating, ws->floating); 123 list_cat(state->floating, ws->floating);
110 list_cat(state->tiling, ws->tiling); 124 list_cat(state->tiling, ws->tiling);
111 125
@@ -115,8 +129,8 @@ static void copy_workspace_state(struct sway_workspace *ws,
115 // Set focused_inactive_child to the direct tiling child 129 // Set focused_inactive_child to the direct tiling child
116 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); 130 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws);
117 if (focus) { 131 if (focus) {
118 while (focus->parent) { 132 while (focus->pending.parent) {
119 focus = focus->parent; 133 focus = focus->pending.parent;
120 } 134 }
121 } 135 }
122 state->focused_inactive_child = focus; 136 state->focused_inactive_child = focus;
@@ -126,28 +140,19 @@ static void copy_container_state(struct sway_container *container,
126 struct sway_transaction_instruction *instruction) { 140 struct sway_transaction_instruction *instruction) {
127 struct sway_container_state *state = &instruction->container_state; 141 struct sway_container_state *state = &instruction->container_state;
128 142
129 state->layout = container->layout; 143 if (state->children) {
130 state->x = container->x; 144 list_free(state->children);
131 state->y = container->y; 145 }
132 state->width = container->width; 146
133 state->height = container->height; 147 memcpy(state, &container->pending, sizeof(struct sway_container_state));
134 state->fullscreen_mode = container->fullscreen_mode;
135 state->parent = container->parent;
136 state->workspace = container->workspace;
137 state->border = container->border;
138 state->border_thickness = container->border_thickness;
139 state->border_top = container->border_top;
140 state->border_left = container->border_left;
141 state->border_right = container->border_right;
142 state->border_bottom = container->border_bottom;
143 state->content_x = container->content_x;
144 state->content_y = container->content_y;
145 state->content_width = container->content_width;
146 state->content_height = container->content_height;
147 148
148 if (!container->view) { 149 if (!container->view) {
150 // We store a copy of the child list to avoid having it mutated after
151 // we copy the state.
149 state->children = create_list(); 152 state->children = create_list();
150 list_cat(state->children, container->children); 153 list_cat(state->children, container->pending.children);
154 } else {
155 state->children = NULL;
151 } 156 }
152 157
153 struct sway_seat *seat = input_manager_current_seat(); 158 struct sway_seat *seat = input_manager_current_seat();
@@ -161,14 +166,36 @@ static void copy_container_state(struct sway_container *container,
161} 166}
162 167
163static void transaction_add_node(struct sway_transaction *transaction, 168static void transaction_add_node(struct sway_transaction *transaction,
164 struct sway_node *node) { 169 struct sway_node *node, bool server_request) {
165 struct sway_transaction_instruction *instruction = 170 struct sway_transaction_instruction *instruction = NULL;
166 calloc(1, sizeof(struct sway_transaction_instruction)); 171
167 if (!sway_assert(instruction, "Unable to allocate instruction")) { 172 // Check if we have an instruction for this node already, in which case we
168 return; 173 // update that instead of creating a new one.
174 if (node->ntxnrefs > 0) {
175 for (int idx = 0; idx < transaction->instructions->length; idx++) {
176 struct sway_transaction_instruction *other =
177 transaction->instructions->items[idx];
178 if (other->node == node) {
179 instruction = other;
180 break;
181 }
182 }
183 }
184
185 if (!instruction) {
186 instruction = calloc(1, sizeof(struct sway_transaction_instruction));
187 if (!sway_assert(instruction, "Unable to allocate instruction")) {
188 return;
189 }
190 instruction->transaction = transaction;
191 instruction->node = node;
192 instruction->server_request = server_request;
193
194 list_add(transaction->instructions, instruction);
195 node->ntxnrefs++;
196 } else if (server_request) {
197 instruction->server_request = true;
169 } 198 }
170 instruction->transaction = transaction;
171 instruction->node = node;
172 199
173 switch (node->type) { 200 switch (node->type) {
174 case N_ROOT: 201 case N_ROOT:
@@ -183,9 +210,6 @@ static void transaction_add_node(struct sway_transaction *transaction,
183 copy_container_state(node->sway_container, instruction); 210 copy_container_state(node->sway_container, instruction);
184 break; 211 break;
185 } 212 }
186
187 list_add(transaction->instructions, instruction);
188 node->ntxnrefs++;
189} 213}
190 214
191static void apply_output_state(struct sway_output *output, 215static void apply_output_state(struct sway_output *output,
@@ -214,8 +238,8 @@ static void apply_container_state(struct sway_container *container,
214 struct sway_saved_buffer *saved_buf; 238 struct sway_saved_buffer *saved_buf;
215 wl_list_for_each(saved_buf, &view->saved_buffers, link) { 239 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
216 struct wlr_box box = { 240 struct wlr_box box = {
217 .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, 241 .x = saved_buf->x - view->saved_geometry.x,
218 .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, 242 .y = saved_buf->y - view->saved_geometry.y,
219 .width = saved_buf->width, 243 .width = saved_buf->width,
220 .height = saved_buf->height, 244 .height = saved_buf->height,
221 }; 245 };
@@ -238,6 +262,13 @@ static void apply_container_state(struct sway_container *container,
238 } 262 }
239 } 263 }
240 264
265 // If the view hasn't responded to the configure, center it within
266 // the container. This is important for fullscreen views which
267 // refuse to resize to the size of the output.
268 if (view && view->surface) {
269 view_center_surface(view);
270 }
271
241 // Damage the new location 272 // Damage the new location
242 desktop_damage_whole_container(container); 273 desktop_damage_whole_container(container);
243 if (view && view->surface) { 274 if (view && view->surface) {
@@ -251,24 +282,6 @@ static void apply_container_state(struct sway_container *container,
251 desktop_damage_box(&box); 282 desktop_damage_box(&box);
252 } 283 }
253 284
254 // If the view hasn't responded to the configure, center it within
255 // the container. This is important for fullscreen views which
256 // refuse to resize to the size of the output.
257 if (view && view->surface) {
258 if (view->geometry.width < container->current.content_width) {
259 container->surface_x = container->current.content_x +
260 (container->current.content_width - view->geometry.width) / 2;
261 } else {
262 container->surface_x = container->current.content_x;
263 }
264 if (view->geometry.height < container->current.content_height) {
265 container->surface_y = container->current.content_y +
266 (container->current.content_height - view->geometry.height) / 2;
267 } else {
268 container->surface_y = container->current.content_y;
269 }
270 }
271
272 if (!container->node.destroying) { 285 if (!container->node.destroying) {
273 container_discover_outputs(container); 286 container_discover_outputs(container);
274 } 287 }
@@ -317,70 +330,25 @@ static void transaction_apply(struct sway_transaction *transaction) {
317 cursor_rebase_all(); 330 cursor_rebase_all();
318} 331}
319 332
320static void transaction_commit(struct sway_transaction *transaction); 333static void transaction_commit_pending(void);
321 334
322// Return true if both transactions operate on the same nodes 335static void transaction_progress(void) {
323static bool transaction_same_nodes(struct sway_transaction *a, 336 if (!server.queued_transaction) {
324 struct sway_transaction *b) {
325 if (a->instructions->length != b->instructions->length) {
326 return false;
327 }
328 for (int i = 0; i < a->instructions->length; ++i) {
329 struct sway_transaction_instruction *a_inst = a->instructions->items[i];
330 struct sway_transaction_instruction *b_inst = b->instructions->items[i];
331 if (a_inst->node != b_inst->node) {
332 return false;
333 }
334 }
335 return true;
336}
337
338static void transaction_progress_queue(void) {
339 if (!server.transactions->length) {
340 return; 337 return;
341 } 338 }
342 // Only the first transaction in the queue is committed, so that's the one 339 if (server.queued_transaction->num_waiting > 0) {
343 // we try to process.
344 struct sway_transaction *transaction = server.transactions->items[0];
345 if (transaction->num_waiting) {
346 return; 340 return;
347 } 341 }
348 transaction_apply(transaction); 342 transaction_apply(server.queued_transaction);
349 transaction_destroy(transaction); 343 transaction_destroy(server.queued_transaction);
350 list_del(server.transactions, 0); 344 server.queued_transaction = NULL;
351 345
352 if (server.transactions->length == 0) { 346 if (!server.pending_transaction) {
353 // The transaction queue is empty, so we're done.
354 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 347 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
355 return; 348 return;
356 } 349 }
357 350
358 // If there's a bunch of consecutive transactions which all apply to the 351 transaction_commit_pending();
359 // same views, skip all except the last one.
360 while (server.transactions->length >= 2) {
361 struct sway_transaction *txn = server.transactions->items[0];
362 struct sway_transaction *dup = NULL;
363
364 for (int i = 1; i < server.transactions->length; i++) {
365 struct sway_transaction *maybe_dup = server.transactions->items[i];
366 if (transaction_same_nodes(txn, maybe_dup)) {
367 dup = maybe_dup;
368 break;
369 }
370 }
371
372 if (dup) {
373 list_del(server.transactions, 0);
374 transaction_destroy(txn);
375 } else {
376 break;
377 }
378 }
379
380 // We again commit the first transaction in the queue to process it.
381 transaction = server.transactions->items[0];
382 transaction_commit(transaction);
383 transaction_progress_queue();
384} 352}
385 353
386static int handle_timeout(void *data) { 354static int handle_timeout(void *data) {
@@ -388,7 +356,7 @@ static int handle_timeout(void *data) {
388 sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", 356 sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)",
389 transaction, transaction->num_waiting); 357 transaction, transaction->num_waiting);
390 transaction->num_waiting = 0; 358 transaction->num_waiting = 0;
391 transaction_progress_queue(); 359 transaction_progress();
392 return 0; 360 return 0;
393} 361}
394 362
@@ -400,6 +368,9 @@ static bool should_configure(struct sway_node *node,
400 if (node->destroying) { 368 if (node->destroying) {
401 return false; 369 return false;
402 } 370 }
371 if (!instruction->server_request) {
372 return false;
373 }
403 struct sway_container_state *cstate = &node->sway_container->current; 374 struct sway_container_state *cstate = &node->sway_container->current;
404 struct sway_container_state *istate = &instruction->container_state; 375 struct sway_container_state *istate = &instruction->container_state;
405#if HAVE_XWAYLAND 376#if HAVE_XWAYLAND
@@ -431,13 +402,18 @@ static void transaction_commit(struct sway_transaction *transaction) {
431 struct sway_transaction_instruction *instruction = 402 struct sway_transaction_instruction *instruction =
432 transaction->instructions->items[i]; 403 transaction->instructions->items[i];
433 struct sway_node *node = instruction->node; 404 struct sway_node *node = instruction->node;
405 bool hidden = node_is_view(node) && !node->destroying &&
406 !view_is_visible(node->sway_container->view);
434 if (should_configure(node, instruction)) { 407 if (should_configure(node, instruction)) {
435 instruction->serial = view_configure(node->sway_container->view, 408 instruction->serial = view_configure(node->sway_container->view,
436 instruction->container_state.content_x, 409 instruction->container_state.content_x,
437 instruction->container_state.content_y, 410 instruction->container_state.content_y,
438 instruction->container_state.content_width, 411 instruction->container_state.content_width,
439 instruction->container_state.content_height); 412 instruction->container_state.content_height);
440 ++transaction->num_waiting; 413 if (!hidden) {
414 instruction->waiting = true;
415 ++transaction->num_waiting;
416 }
441 417
442 // From here on we are rendering a saved buffer of the view, which 418 // From here on we are rendering a saved buffer of the view, which
443 // means we can send a frame done event to make the client redraw it 419 // means we can send a frame done event to make the client redraw it
@@ -448,7 +424,8 @@ static void transaction_commit(struct sway_transaction *transaction) {
448 wlr_surface_send_frame_done( 424 wlr_surface_send_frame_done(
449 node->sway_container->view->surface, &now); 425 node->sway_container->view->surface, &now);
450 } 426 }
451 if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { 427 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) {
452 view_save_buffer(node->sway_container->view); 429 view_save_buffer(node->sway_container->view);
453 memcpy(&node->sway_container->view->saved_geometry, 430 memcpy(&node->sway_container->view->saved_geometry,
454 &node->sway_container->view->geometry, 431 &node->sway_container->view->geometry,
@@ -483,6 +460,17 @@ static void transaction_commit(struct sway_transaction *transaction) {
483 } 460 }
484} 461}
485 462
463static void transaction_commit_pending(void) {
464 if (server.queued_transaction) {
465 return;
466 }
467 struct sway_transaction *transaction = server.pending_transaction;
468 server.pending_transaction = NULL;
469 server.queued_transaction = transaction;
470 transaction_commit(transaction);
471 transaction_progress();
472}
473
486static void set_instruction_ready( 474static void set_instruction_ready(
487 struct sway_transaction_instruction *instruction) { 475 struct sway_transaction_instruction *instruction) {
488 struct sway_transaction *transaction = instruction->transaction; 476 struct sway_transaction *transaction = instruction->transaction;
@@ -501,13 +489,14 @@ static void set_instruction_ready(
501 } 489 }
502 490
503 // If the transaction has timed out then its num_waiting will be 0 already. 491 // If the transaction has timed out then its num_waiting will be 0 already.
504 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { 492 if (instruction->waiting && transaction->num_waiting > 0 &&
493 --transaction->num_waiting == 0) {
505 sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); 494 sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction);
506 wl_event_source_timer_update(transaction->timer, 0); 495 wl_event_source_timer_update(transaction->timer, 0);
507 } 496 }
508 497
509 instruction->node->instruction = NULL; 498 instruction->node->instruction = NULL;
510 transaction_progress_queue(); 499 transaction_progress();
511} 500}
512 501
513void transaction_notify_view_ready_by_serial(struct sway_view *view, 502void transaction_notify_view_ready_by_serial(struct sway_view *view,
@@ -532,36 +521,32 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view,
532 } 521 }
533} 522}
534 523
535void transaction_notify_view_ready_immediately(struct sway_view *view) { 524static void _transaction_commit_dirty(bool server_request) {
536 struct sway_transaction_instruction *instruction =
537 view->container->node.instruction;
538 if (instruction != NULL) {
539 set_instruction_ready(instruction);
540 }
541}
542
543void transaction_commit_dirty(void) {
544 if (!server.dirty_nodes->length) { 525 if (!server.dirty_nodes->length) {
545 return; 526 return;
546 } 527 }
547 struct sway_transaction *transaction = transaction_create(); 528
548 if (!transaction) { 529 if (!server.pending_transaction) {
549 return; 530 server.pending_transaction = transaction_create();
531 if (!server.pending_transaction) {
532 return;
533 }
550 } 534 }
535
551 for (int i = 0; i < server.dirty_nodes->length; ++i) { 536 for (int i = 0; i < server.dirty_nodes->length; ++i) {
552 struct sway_node *node = server.dirty_nodes->items[i]; 537 struct sway_node *node = server.dirty_nodes->items[i];
553 transaction_add_node(transaction, node); 538 transaction_add_node(server.pending_transaction, node, server_request);
554 node->dirty = false; 539 node->dirty = false;
555 } 540 }
556 server.dirty_nodes->length = 0; 541 server.dirty_nodes->length = 0;
557 542
558 list_add(server.transactions, transaction); 543 transaction_commit_pending();
544}
559 545
560 // We only commit the first transaction added to the queue. 546void transaction_commit_dirty(void) {
561 if (server.transactions->length == 1) { 547 _transaction_commit_dirty(true);
562 transaction_commit(transaction); 548}
563 // Attempting to progress the queue here is useful 549
564 // if the transaction has nothing to wait for. 550void transaction_commit_dirty_client(void) {
565 transaction_progress_queue(); 551 _transaction_commit_dirty(false);
566 }
567} 552}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 667fb9e5..8da922d5 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -21,18 +21,15 @@
21 21
22static const struct sway_view_child_impl popup_impl; 22static const struct sway_view_child_impl popup_impl;
23 23
24static void popup_get_root_coords(struct sway_view_child *child, 24static void popup_get_view_coords(struct sway_view_child *child,
25 int *root_sx, int *root_sy) { 25 int *sx, int *sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; 26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; 27 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
28 28
29 int x_offset = -child->view->geometry.x - surface->geometry.x; 29 wlr_xdg_popup_get_toplevel_coords(wlr_popup,
30 int y_offset = -child->view->geometry.y - surface->geometry.y; 30 wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x,
31 31 wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y,
32 wlr_xdg_popup_get_toplevel_coords(surface->popup, 32 sx, sy);
33 x_offset + surface->popup->geometry.x,
34 y_offset + surface->popup->geometry.y,
35 root_sx, root_sy);
36} 33}
37 34
38static void popup_destroy(struct sway_view_child *child) { 35static void popup_destroy(struct sway_view_child *child) {
@@ -47,7 +44,7 @@ static void popup_destroy(struct sway_view_child *child) {
47} 44}
48 45
49static const struct sway_view_child_impl popup_impl = { 46static const struct sway_view_child_impl popup_impl = {
50 .get_root_coords = popup_get_root_coords, 47 .get_view_coords = popup_get_view_coords,
51 .destroy = popup_destroy, 48 .destroy = popup_destroy,
52}; 49};
53 50
@@ -68,15 +65,15 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
68 65
69static void popup_unconstrain(struct sway_xdg_popup *popup) { 66static void popup_unconstrain(struct sway_xdg_popup *popup) {
70 struct sway_view *view = popup->child.view; 67 struct sway_view *view = popup->child.view;
71 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 68 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
72 69
73 struct sway_output *output = view->container->workspace->output; 70 struct sway_output *output = view->container->pending.workspace->output;
74 71
75 // the output box expressed in the coordinate system of the toplevel parent 72 // the output box expressed in the coordinate system of the toplevel parent
76 // of the popup 73 // of the popup
77 struct wlr_box output_toplevel_sx_box = { 74 struct wlr_box output_toplevel_sx_box = {
78 .x = output->lx - view->container->content_x, 75 .x = output->lx - view->container->pending.content_x + view->geometry.x,
79 .y = output->ly - view->container->content_y, 76 .y = output->ly - view->container->pending.content_y + view->geometry.y,
80 .width = output->width, 77 .width = output->width,
81 .height = output->height, 78 .height = output->height,
82 }; 79 };
@@ -94,7 +91,7 @@ static struct sway_xdg_popup *popup_create(
94 return NULL; 91 return NULL;
95 } 92 }
96 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 93 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
97 popup->wlr_xdg_surface = xdg_surface; 94 popup->wlr_xdg_popup = xdg_surface->popup;
98 95
99 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 96 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
100 popup->new_popup.notify = popup_handle_new_popup; 97 popup->new_popup.notify = popup_handle_new_popup;
@@ -122,7 +119,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
122static void get_constraints(struct sway_view *view, double *min_width, 119static void get_constraints(struct sway_view *view, double *min_width,
123 double *max_width, double *min_height, double *max_height) { 120 double *max_width, double *min_height, double *max_height) {
124 struct wlr_xdg_toplevel_state *state = 121 struct wlr_xdg_toplevel_state *state =
125 &view->wlr_xdg_surface->toplevel->current; 122 &view->wlr_xdg_toplevel->current;
126 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; 123 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
127 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; 124 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
128 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; 125 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
@@ -136,9 +133,9 @@ static const char *get_string_prop(struct sway_view *view,
136 } 133 }
137 switch (prop) { 134 switch (prop) {
138 case VIEW_PROP_TITLE: 135 case VIEW_PROP_TITLE:
139 return view->wlr_xdg_surface->toplevel->title; 136 return view->wlr_xdg_toplevel->title;
140 case VIEW_PROP_APP_ID: 137 case VIEW_PROP_APP_ID:
141 return view->wlr_xdg_surface->toplevel->app_id; 138 return view->wlr_xdg_toplevel->app_id;
142 default: 139 default:
143 return NULL; 140 return NULL;
144 } 141 }
@@ -151,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly,
151 if (xdg_shell_view == NULL) { 148 if (xdg_shell_view == NULL) {
152 return 0; 149 return 0;
153 } 150 }
154 return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); 151 return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel,
152 width, height);
155} 153}
156 154
157static void set_activated(struct sway_view *view, bool activated) { 155static void set_activated(struct sway_view *view, bool activated) {
158 if (xdg_shell_view_from_view(view) == NULL) { 156 if (xdg_shell_view_from_view(view) == NULL) {
159 return; 157 return;
160 } 158 }
161 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 159 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} 160}
166 161
167static void set_tiled(struct sway_view *view, bool tiled) { 162static void set_tiled(struct sway_view *view, bool tiled) {
168 if (xdg_shell_view_from_view(view) == NULL) { 163 if (xdg_shell_view_from_view(view) == NULL) {
169 return; 164 return;
170 } 165 }
171 struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
172 enum wlr_edges edges = WLR_EDGE_NONE; 166 enum wlr_edges edges = WLR_EDGE_NONE;
173 if (tiled) { 167 if (tiled) {
174 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 168 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
175 WLR_EDGE_BOTTOM; 169 WLR_EDGE_BOTTOM;
176 } 170 }
177 wlr_xdg_toplevel_set_tiled(surface, edges); 171 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
178} 172}
179 173
180static void set_fullscreen(struct sway_view *view, bool fullscreen) { 174static void set_fullscreen(struct sway_view *view, bool fullscreen) {
181 if (xdg_shell_view_from_view(view) == NULL) { 175 if (xdg_shell_view_from_view(view) == NULL) {
182 return; 176 return;
183 } 177 }
184 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 178 wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);
185 wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
186} 179}
187 180
188static void set_resizing(struct sway_view *view, bool resizing) { 181static void set_resizing(struct sway_view *view, bool resizing) {
189 if (xdg_shell_view_from_view(view) == NULL) { 182 if (xdg_shell_view_from_view(view) == NULL) {
190 return; 183 return;
191 } 184 }
192 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 185 wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing);
193 wlr_xdg_toplevel_set_resizing(surface, resizing);
194} 186}
195 187
196static bool wants_floating(struct sway_view *view) { 188static bool wants_floating(struct sway_view *view) {
197 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; 189 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
198 struct wlr_xdg_toplevel_state *state = &toplevel->current; 190 struct wlr_xdg_toplevel_state *state = &toplevel->current;
199 return (state->min_width != 0 && state->min_height != 0 191 return (state->min_width != 0 && state->min_height != 0
200 && (state->min_width == state->max_width 192 && (state->min_width == state->max_width
@@ -207,7 +199,7 @@ static void for_each_surface(struct sway_view *view,
207 if (xdg_shell_view_from_view(view) == NULL) { 199 if (xdg_shell_view_from_view(view) == NULL) {
208 return; 200 return;
209 } 201 }
210 wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, 202 wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
211 user_data); 203 user_data);
212} 204}
213 205
@@ -216,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view,
216 if (xdg_shell_view_from_view(view) == NULL) { 208 if (xdg_shell_view_from_view(view) == NULL) {
217 return; 209 return;
218 } 210 }
219 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, 211 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
220 user_data); 212 iterator, user_data);
221} 213}
222 214
223static bool is_transient_for(struct sway_view *child, 215static bool is_transient_for(struct sway_view *child,
@@ -225,12 +217,12 @@ static bool is_transient_for(struct sway_view *child,
225 if (xdg_shell_view_from_view(child) == NULL) { 217 if (xdg_shell_view_from_view(child) == NULL) {
226 return false; 218 return false;
227 } 219 }
228 struct wlr_xdg_surface *surface = child->wlr_xdg_surface; 220 struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel;
229 while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { 221 while (toplevel) {
230 if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { 222 if (toplevel->parent == ancestor->wlr_xdg_toplevel) {
231 return true; 223 return true;
232 } 224 }
233 surface = surface->toplevel->parent; 225 toplevel = toplevel->parent;
234 } 226 }
235 return false; 227 return false;
236} 228}
@@ -239,17 +231,13 @@ static void _close(struct sway_view *view) {
239 if (xdg_shell_view_from_view(view) == NULL) { 231 if (xdg_shell_view_from_view(view) == NULL) {
240 return; 232 return;
241 } 233 }
242 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 234 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} 235}
248 236
249static void close_popups(struct sway_view *view) { 237static void close_popups(struct sway_view *view) {
250 struct wlr_xdg_popup *popup, *tmp; 238 struct wlr_xdg_popup *popup, *tmp;
251 wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { 239 wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) {
252 wlr_xdg_popup_destroy(popup->base); 240 wlr_xdg_popup_destroy(popup);
253 } 241 }
254} 242}
255 243
@@ -283,7 +271,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
283 struct sway_xdg_shell_view *xdg_shell_view = 271 struct sway_xdg_shell_view *xdg_shell_view =
284 wl_container_of(listener, xdg_shell_view, commit); 272 wl_container_of(listener, xdg_shell_view, commit);
285 struct sway_view *view = &xdg_shell_view->view; 273 struct sway_view *view = &xdg_shell_view->view;
286 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 274 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
287 275
288 struct wlr_box new_geo; 276 struct wlr_box new_geo;
289 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 277 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
@@ -293,19 +281,23 @@ static void handle_commit(struct wl_listener *listener, void *data) {
293 new_geo.y != view->geometry.y; 281 new_geo.y != view->geometry.y;
294 282
295 if (new_size) { 283 if (new_size) {
296 // The view has unexpectedly sent a new size 284 // The client changed its surface size in this commit. For floating
285 // containers, we resize the container to match. For tiling containers,
286 // we only recenter the surface.
297 desktop_damage_view(view); 287 desktop_damage_view(view);
298 view_update_size(view, new_geo.width, new_geo.height);
299 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 288 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
289 if (container_is_floating(view->container)) {
290 view_update_size(view);
291 transaction_commit_dirty_client();
292 } else {
293 view_center_surface(view);
294 }
300 desktop_damage_view(view); 295 desktop_damage_view(view);
301 transaction_commit_dirty();
302 } 296 }
303 297
304 if (view->container->node.instruction) { 298 if (view->container->node.instruction) {
305 transaction_notify_view_ready_by_serial(view, 299 transaction_notify_view_ready_by_serial(view,
306 xdg_surface->configure_serial); 300 xdg_surface->current.configure_serial);
307 } else if (new_size) {
308 transaction_notify_view_ready_immediately(view);
309 } 301 }
310 302
311 view_damage_from(view); 303 view_damage_from(view);
@@ -333,28 +325,30 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
333 popup_create(wlr_popup, &xdg_shell_view->view); 325 popup_create(wlr_popup, &xdg_shell_view->view);
334} 326}
335 327
328static void handle_request_maximize(struct wl_listener *listener, void *data) {
329 struct sway_xdg_shell_view *xdg_shell_view =
330 wl_container_of(listener, xdg_shell_view, request_maximize);
331 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
332 wlr_xdg_surface_schedule_configure(toplevel->base);
333}
334
336static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 335static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
337 struct sway_xdg_shell_view *xdg_shell_view = 336 struct sway_xdg_shell_view *xdg_shell_view =
338 wl_container_of(listener, xdg_shell_view, request_fullscreen); 337 wl_container_of(listener, xdg_shell_view, request_fullscreen);
339 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 338 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
340 struct wlr_xdg_surface *xdg_surface =
341 xdg_shell_view->view.wlr_xdg_surface;
342 struct sway_view *view = &xdg_shell_view->view; 339 struct sway_view *view = &xdg_shell_view->view;
343 340
344 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 341 if (!toplevel->base->mapped) {
345 "xdg_shell requested fullscreen of surface with role %i",
346 xdg_surface->role)) {
347 return;
348 }
349 if (!xdg_surface->mapped) {
350 return; 342 return;
351 } 343 }
352 344
353 struct sway_container *container = view->container; 345 struct sway_container *container = view->container;
354 if (e->fullscreen && e->output && e->output->data) { 346 struct wlr_xdg_toplevel_requested *req = &toplevel->requested;
355 struct sway_output *output = e->output->data; 347 if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {
348 struct sway_output *output = req->fullscreen_output->data;
356 struct sway_workspace *ws = output_get_active_workspace(output); 349 struct sway_workspace *ws = output_get_active_workspace(output);
357 if (ws && !container_is_scratchpad_hidden(container)) { 350 if (ws && !container_is_scratchpad_hidden(container) &&
351 container->pending.workspace != ws) {
358 if (container_is_floating(container)) { 352 if (container_is_floating(container)) {
359 workspace_add_floating(ws, container); 353 workspace_add_floating(ws, container);
360 } else { 354 } else {
@@ -363,22 +357,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
363 } 357 }
364 } 358 }
365 359
366 container_set_fullscreen(container, e->fullscreen); 360 container_set_fullscreen(container, req->fullscreen);
367 361
368 arrange_root(); 362 arrange_root();
369 transaction_commit_dirty(); 363 transaction_commit_dirty();
370} 364}
371 365
372static void handle_request_maximize(struct wl_listener *listener, void *data) {
373 struct wlr_xdg_surface *surface = data;
374 wlr_xdg_surface_schedule_configure(surface);
375}
376
377static void handle_request_move(struct wl_listener *listener, void *data) { 366static void handle_request_move(struct wl_listener *listener, void *data) {
378 struct sway_xdg_shell_view *xdg_shell_view = 367 struct sway_xdg_shell_view *xdg_shell_view =
379 wl_container_of(listener, xdg_shell_view, request_move); 368 wl_container_of(listener, xdg_shell_view, request_move);
380 struct sway_view *view = &xdg_shell_view->view; 369 struct sway_view *view = &xdg_shell_view->view;
381 if (!container_is_floating(view->container)) { 370 if (!container_is_floating(view->container) ||
371 view->container->pending.fullscreen_mode) {
382 return; 372 return;
383 } 373 }
384 struct wlr_xdg_toplevel_move_event *e = data; 374 struct wlr_xdg_toplevel_move_event *e = data;
@@ -415,8 +405,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
415 405
416 wl_list_remove(&xdg_shell_view->commit.link); 406 wl_list_remove(&xdg_shell_view->commit.link);
417 wl_list_remove(&xdg_shell_view->new_popup.link); 407 wl_list_remove(&xdg_shell_view->new_popup.link);
418 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
419 wl_list_remove(&xdg_shell_view->request_maximize.link); 408 wl_list_remove(&xdg_shell_view->request_maximize.link);
409 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
420 wl_list_remove(&xdg_shell_view->request_move.link); 410 wl_list_remove(&xdg_shell_view->request_move.link);
421 wl_list_remove(&xdg_shell_view->request_resize.link); 411 wl_list_remove(&xdg_shell_view->request_resize.link);
422 wl_list_remove(&xdg_shell_view->set_title.link); 412 wl_list_remove(&xdg_shell_view->set_title.link);
@@ -427,62 +417,65 @@ static void handle_map(struct wl_listener *listener, void *data) {
427 struct sway_xdg_shell_view *xdg_shell_view = 417 struct sway_xdg_shell_view *xdg_shell_view =
428 wl_container_of(listener, xdg_shell_view, map); 418 wl_container_of(listener, xdg_shell_view, map);
429 struct sway_view *view = &xdg_shell_view->view; 419 struct sway_view *view = &xdg_shell_view->view;
430 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 420 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
431 421
432 view->natural_width = view->wlr_xdg_surface->geometry.width; 422 view->natural_width = toplevel->base->current.geometry.width;
433 view->natural_height = view->wlr_xdg_surface->geometry.height; 423 view->natural_height = toplevel->base->current.geometry.height;
434 if (!view->natural_width && !view->natural_height) { 424 if (!view->natural_width && !view->natural_height) {
435 view->natural_width = view->wlr_xdg_surface->surface->current.width; 425 view->natural_width = toplevel->base->surface->current.width;
436 view->natural_height = view->wlr_xdg_surface->surface->current.height; 426 view->natural_height = toplevel->base->surface->current.height;
437 } 427 }
438 428
439 bool csd = false; 429 bool csd = false;
440 430
441 if (!view->xdg_decoration) { 431 if (view->xdg_decoration) {
432 enum wlr_xdg_toplevel_decoration_v1_mode mode =
433 view->xdg_decoration->wlr_xdg_decoration->requested_mode;
434 csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
435 } else {
442 struct sway_server_decoration *deco = 436 struct sway_server_decoration *deco =
443 decoration_from_surface(xdg_surface->surface); 437 decoration_from_surface(toplevel->base->surface);
444 csd = !deco || deco->wlr_server_decoration->mode == 438 csd = !deco || deco->wlr_server_decoration->mode ==
445 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 439 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
446
447 } 440 }
448 441
449 view_map(view, view->wlr_xdg_surface->surface, 442 view_map(view, toplevel->base->surface,
450 xdg_surface->toplevel->client_pending.fullscreen, 443 toplevel->requested.fullscreen,
451 xdg_surface->toplevel->client_pending.fullscreen_output, 444 toplevel->requested.fullscreen_output,
452 csd); 445 csd);
453 446
454 transaction_commit_dirty(); 447 transaction_commit_dirty();
455 448
456 xdg_shell_view->commit.notify = handle_commit; 449 xdg_shell_view->commit.notify = handle_commit;
457 wl_signal_add(&xdg_surface->surface->events.commit, 450 wl_signal_add(&toplevel->base->surface->events.commit,
458 &xdg_shell_view->commit); 451 &xdg_shell_view->commit);
459 452
460 xdg_shell_view->new_popup.notify = handle_new_popup; 453 xdg_shell_view->new_popup.notify = handle_new_popup;
461 wl_signal_add(&xdg_surface->events.new_popup, 454 wl_signal_add(&toplevel->base->events.new_popup,
462 &xdg_shell_view->new_popup); 455 &xdg_shell_view->new_popup);
463 456
464 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
465 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
466 &xdg_shell_view->request_fullscreen);
467
468 xdg_shell_view->request_maximize.notify = handle_request_maximize; 457 xdg_shell_view->request_maximize.notify = handle_request_maximize;
469 wl_signal_add(&xdg_surface->toplevel->events.request_maximize, 458 wl_signal_add(&toplevel->events.request_maximize,
470 &xdg_shell_view->request_maximize); 459 &xdg_shell_view->request_maximize);
471 460
461 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
462 wl_signal_add(&toplevel->events.request_fullscreen,
463 &xdg_shell_view->request_fullscreen);
464
472 xdg_shell_view->request_move.notify = handle_request_move; 465 xdg_shell_view->request_move.notify = handle_request_move;
473 wl_signal_add(&xdg_surface->toplevel->events.request_move, 466 wl_signal_add(&toplevel->events.request_move,
474 &xdg_shell_view->request_move); 467 &xdg_shell_view->request_move);
475 468
476 xdg_shell_view->request_resize.notify = handle_request_resize; 469 xdg_shell_view->request_resize.notify = handle_request_resize;
477 wl_signal_add(&xdg_surface->toplevel->events.request_resize, 470 wl_signal_add(&toplevel->events.request_resize,
478 &xdg_shell_view->request_resize); 471 &xdg_shell_view->request_resize);
479 472
480 xdg_shell_view->set_title.notify = handle_set_title; 473 xdg_shell_view->set_title.notify = handle_set_title;
481 wl_signal_add(&xdg_surface->toplevel->events.set_title, 474 wl_signal_add(&toplevel->events.set_title,
482 &xdg_shell_view->set_title); 475 &xdg_shell_view->set_title);
483 476
484 xdg_shell_view->set_app_id.notify = handle_set_app_id; 477 xdg_shell_view->set_app_id.notify = handle_set_app_id;
485 wl_signal_add(&xdg_surface->toplevel->events.set_app_id, 478 wl_signal_add(&toplevel->events.set_app_id,
486 &xdg_shell_view->set_app_id); 479 &xdg_shell_view->set_app_id);
487} 480}
488 481
@@ -496,7 +489,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
496 wl_list_remove(&xdg_shell_view->destroy.link); 489 wl_list_remove(&xdg_shell_view->destroy.link);
497 wl_list_remove(&xdg_shell_view->map.link); 490 wl_list_remove(&xdg_shell_view->map.link);
498 wl_list_remove(&xdg_shell_view->unmap.link); 491 wl_list_remove(&xdg_shell_view->unmap.link);
499 view->wlr_xdg_surface = NULL; 492 view->wlr_xdg_toplevel = NULL;
500 if (view->xdg_decoration) { 493 if (view->xdg_decoration) {
501 view->xdg_decoration->view = NULL; 494 view->xdg_decoration->view = NULL;
502 } 495 }
@@ -527,7 +520,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
527 } 520 }
528 521
529 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 522 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
530 xdg_shell_view->view.wlr_xdg_surface = xdg_surface; 523 xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel;
531 524
532 xdg_shell_view->map.notify = handle_map; 525 xdg_shell_view->map.notify = handle_map;
533 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 526 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index e1a2e463..7c5dde53 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -6,6 +6,7 @@
6#include <wlr/types/wlr_output_layout.h> 6#include <wlr/types/wlr_output_layout.h>
7#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/xwayland.h> 8#include <wlr/xwayland.h>
9#include <xcb/xcb_icccm.h>
9#include "log.h" 10#include "log.h"
10#include "sway/desktop.h" 11#include "sway/desktop.h"
11#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
@@ -105,14 +106,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 106 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
106 // This simply returns focus to the parent surface if there's one available. 107 // This simply returns focus to the parent surface if there's one available.
107 // This seems to handle JetBrains issues. 108 // This seems to handle JetBrains issues.
108 if (xsurface->parent && xsurface->parent->surface && 109 if (xsurface->parent && xsurface->parent->surface
109 wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { 110 && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) {
110 struct wlr_xwayland_surface *next_surface = 111 seat_set_focus_surface(seat, xsurface->parent->surface, false);
111 wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); 112 return;
112 if (wlr_xwayland_or_surface_wants_focus(next_surface)) {
113 seat_set_focus_surface(seat, xsurface->parent->surface, false);
114 return;
115 }
116 } 113 }
117 114
118 // Restore focus 115 // Restore focus
@@ -125,6 +122,20 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
125 } 122 }
126} 123}
127 124
125static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
126 struct wlr_xwayland_surface *xsurface = data;
127 if (!xsurface->mapped) {
128 return;
129 }
130 struct sway_seat *seat = input_manager_current_seat();
131 struct sway_container *focus = seat_get_focused_container(seat);
132 if (focus && focus->view && focus->view->pid != xsurface->pid) {
133 return;
134 }
135
136 seat_set_focus_surface(seat, xsurface->surface, false);
137}
138
128static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 139static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
129 struct sway_xwayland_unmanaged *surface = 140 struct sway_xwayland_unmanaged *surface =
130 wl_container_of(listener, surface, destroy); 141 wl_container_of(listener, surface, destroy);
@@ -133,6 +144,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
133 wl_list_remove(&surface->unmap.link); 144 wl_list_remove(&surface->unmap.link);
134 wl_list_remove(&surface->destroy.link); 145 wl_list_remove(&surface->destroy.link);
135 wl_list_remove(&surface->override_redirect.link); 146 wl_list_remove(&surface->override_redirect.link);
147 wl_list_remove(&surface->request_activate.link);
136 free(surface); 148 free(surface);
137} 149}
138 150
@@ -180,6 +192,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
180 surface->destroy.notify = unmanaged_handle_destroy; 192 surface->destroy.notify = unmanaged_handle_destroy;
181 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); 193 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
182 surface->override_redirect.notify = unmanaged_handle_override_redirect; 194 surface->override_redirect.notify = unmanaged_handle_override_redirect;
195 wl_signal_add(&xsurface->events.request_activate, &surface->request_activate);
196 surface->request_activate.notify = unmanaged_handle_request_activate;
183 197
184 return surface; 198 return surface;
185} 199}
@@ -258,6 +272,7 @@ static void set_activated(struct sway_view *view, bool activated) {
258 } 272 }
259 273
260 wlr_xwayland_surface_activate(surface, activated); 274 wlr_xwayland_surface_activate(surface, activated);
275 wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE);
261} 276}
262 277
263static void set_tiled(struct sway_view *view, bool tiled) { 278static void set_tiled(struct sway_view *view, bool tiled) {
@@ -297,7 +312,7 @@ static bool wants_floating(struct sway_view *view) {
297 } 312 }
298 } 313 }
299 314
300 struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; 315 xcb_size_hints_t *size_hints = surface->size_hints;
301 if (size_hints != NULL && 316 if (size_hints != NULL &&
302 size_hints->min_width > 0 && size_hints->min_height > 0 && 317 size_hints->min_width > 0 && size_hints->min_height > 0 &&
303 (size_hints->max_width == size_hints->min_width || 318 (size_hints->max_width == size_hints->min_width ||
@@ -351,7 +366,7 @@ static void destroy(struct sway_view *view) {
351static void get_constraints(struct sway_view *view, double *min_width, 366static void get_constraints(struct sway_view *view, double *min_width,
352 double *max_width, double *min_height, double *max_height) { 367 double *max_width, double *min_height, double *max_height) {
353 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 368 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
354 struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; 369 xcb_size_hints_t *size_hints = surface->size_hints;
355 370
356 if (size_hints == NULL) { 371 if (size_hints == NULL) {
357 *min_width = DBL_MIN; 372 *min_width = DBL_MIN;
@@ -399,30 +414,31 @@ static void handle_commit(struct wl_listener *listener, void *data) {
399 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 414 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
400 struct wlr_surface_state *state = &xsurface->surface->current; 415 struct wlr_surface_state *state = &xsurface->surface->current;
401 416
417 struct wlr_box new_geo;
418 get_geometry(view, &new_geo);
419 bool new_size = new_geo.width != view->geometry.width ||
420 new_geo.height != view->geometry.height ||
421 new_geo.x != view->geometry.x ||
422 new_geo.y != view->geometry.y;
423
424 if (new_size) {
425 // The client changed its surface size in this commit. For floating
426 // containers, we resize the container to match. For tiling containers,
427 // we only recenter the surface.
428 desktop_damage_view(view);
429 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
430 if (container_is_floating(view->container)) {
431 view_update_size(view);
432 transaction_commit_dirty_client();
433 } else {
434 view_center_surface(view);
435 }
436 desktop_damage_view(view);
437 }
438
402 if (view->container->node.instruction) { 439 if (view->container->node.instruction) {
403 get_geometry(view, &view->geometry);
404 transaction_notify_view_ready_by_geometry(view, 440 transaction_notify_view_ready_by_geometry(view,
405 xsurface->x, xsurface->y, state->width, state->height); 441 xsurface->x, xsurface->y, state->width, state->height);
406 } else {
407 struct wlr_box new_geo;
408 get_geometry(view, &new_geo);
409
410 if ((new_geo.width != view->geometry.width ||
411 new_geo.height != view->geometry.height ||
412 new_geo.x != view->geometry.x ||
413 new_geo.y != view->geometry.y)) {
414 // The view has unexpectedly sent a new size
415 // eg. The Firefox "Save As" dialog when downloading a file
416 desktop_damage_view(view);
417 view_update_size(view, new_geo.width, new_geo.height);
418 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
419 desktop_damage_view(view);
420 transaction_commit_dirty();
421 transaction_notify_view_ready_by_geometry(view,
422 xsurface->x, xsurface->y, new_geo.width, new_geo.height);
423 } else {
424 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
425 }
426 } 442 }
427 443
428 view_damage_from(view); 444 view_damage_from(view);
@@ -438,6 +454,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
438 wl_list_remove(&xwayland_view->commit.link); 454 wl_list_remove(&xwayland_view->commit.link);
439 } 455 }
440 456
457 xwayland_view->view.wlr_xwayland_surface = NULL;
458
441 wl_list_remove(&xwayland_view->destroy.link); 459 wl_list_remove(&xwayland_view->destroy.link);
442 wl_list_remove(&xwayland_view->request_configure.link); 460 wl_list_remove(&xwayland_view->request_configure.link);
443 wl_list_remove(&xwayland_view->request_fullscreen.link); 461 wl_list_remove(&xwayland_view->request_fullscreen.link);
@@ -527,10 +545,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
527 view->natural_height = ev->height; 545 view->natural_height = ev->height;
528 container_floating_resize_and_center(view->container); 546 container_floating_resize_and_center(view->container);
529 547
530 configure(view, view->container->content_x, 548 configure(view, view->container->pending.content_x,
531 view->container->content_y, 549 view->container->pending.content_y,
532 view->container->content_width, 550 view->container->pending.content_width,
533 view->container->content_height); 551 view->container->pending.content_height);
534 node_set_dirty(&view->container->node); 552 node_set_dirty(&view->container->node);
535 } else { 553 } else {
536 configure(view, view->container->current.content_x, 554 configure(view, view->container->current.content_x,
@@ -577,7 +595,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
577 if (!xsurface->mapped) { 595 if (!xsurface->mapped) {
578 return; 596 return;
579 } 597 }
580 if (!container_is_floating(view->container)) { 598 if (!container_is_floating(view->container) ||
599 view->container->pending.fullscreen_mode) {
581 return; 600 return;
582 } 601 }
583 struct sway_seat *seat = input_manager_current_seat(); 602 struct sway_seat *seat = input_manager_current_seat();
@@ -666,14 +685,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
666 if (!xsurface->mapped) { 685 if (!xsurface->mapped) {
667 return; 686 return;
668 } 687 }
669 if (!xsurface->hints_urgency && view->urgent_timer) { 688 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
689 if (!hints_urgency && view->urgent_timer) {
670 // The view is in the timeout period. We'll ignore the request to 690 // The view is in the timeout period. We'll ignore the request to
671 // unset urgency so that the view remains urgent until the timer clears 691 // unset urgency so that the view remains urgent until the timer clears
672 // it. 692 // it.
673 return; 693 return;
674 } 694 }
675 if (view->allow_request_urgent) { 695 if (view->allow_request_urgent) {
676 view_set_urgent(view, (bool)xsurface->hints_urgency); 696 view_set_urgent(view, hints_urgency);
677 } 697 }
678} 698}
679 699
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index fa604426..2ee63124 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -4,8 +4,8 @@
4#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
5#include <linux/input-event-codes.h> 5#include <linux/input-event-codes.h>
6#include <errno.h> 6#include <errno.h>
7#include <time.h>
7#include <strings.h> 8#include <strings.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_idle.h> 10#include <wlr/types/wlr_idle.h>
11#include <wlr/types/wlr_pointer.h> 11#include <wlr/types/wlr_pointer.h>
@@ -20,7 +20,6 @@
20#include "util.h" 20#include "util.h"
21#include "sway/commands.h" 21#include "sway/commands.h"
22#include "sway/desktop.h" 22#include "sway/desktop.h"
23#include "sway/desktop/transaction.h"
24#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
25#include "sway/input/keyboard.h" 24#include "sway/input/keyboard.h"
26#include "sway/input/tablet.h" 25#include "sway/input/tablet.h"
@@ -32,6 +31,12 @@
32#include "sway/tree/workspace.h" 31#include "sway/tree/workspace.h"
33#include "wlr-layer-shell-unstable-v1-protocol.h" 32#include "wlr-layer-shell-unstable-v1-protocol.h"
34 33
34static uint32_t get_current_time_msec(void) {
35 struct timespec now;
36 clock_gettime(CLOCK_MONOTONIC, &now);
37 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
38}
39
35static struct wlr_surface *layer_surface_at(struct sway_output *output, 40static struct wlr_surface *layer_surface_at(struct sway_output *output,
36 struct wl_list *layer, double ox, double oy, double *sx, double *sy) { 41 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
37 struct sway_layer_surface *sway_layer; 42 struct sway_layer_surface *sway_layer;
@@ -78,7 +83,28 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
78struct sway_node *node_at_coords( 83struct sway_node *node_at_coords(
79 struct sway_seat *seat, double lx, double ly, 84 struct sway_seat *seat, double lx, double ly,
80 struct wlr_surface **surface, double *sx, double *sy) { 85 struct wlr_surface **surface, double *sx, double *sy) {
81 // check for unmanaged views first 86 // find the output the cursor is on
87 struct wlr_output *wlr_output = wlr_output_layout_output_at(
88 root->output_layout, lx, ly);
89 if (wlr_output == NULL) {
90 return NULL;
91 }
92 struct sway_output *output = wlr_output->data;
93 if (!output || !output->enabled) {
94 // output is being destroyed or is being enabled
95 return NULL;
96 }
97 double ox = lx, oy = ly;
98 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
99
100 // layer surfaces on the overlay layer are rendered on top
101 if ((*surface = layer_surface_at(output,
102 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
103 ox, oy, sx, sy))) {
104 return NULL;
105 }
106
107 // check for unmanaged views
82#if HAVE_XWAYLAND 108#if HAVE_XWAYLAND
83 struct wl_list *unmanaged = &root->xwayland_unmanaged; 109 struct wl_list *unmanaged = &root->xwayland_unmanaged;
84 struct sway_xwayland_unmanaged *unmanaged_surface; 110 struct sway_xwayland_unmanaged *unmanaged_surface;
@@ -96,19 +122,6 @@ struct sway_node *node_at_coords(
96 } 122 }
97 } 123 }
98#endif 124#endif
99 // find the output the cursor is on
100 struct wlr_output *wlr_output = wlr_output_layout_output_at(
101 root->output_layout, lx, ly);
102 if (wlr_output == NULL) {
103 return NULL;
104 }
105 struct sway_output *output = wlr_output->data;
106 if (!output || !output->enabled) {
107 // output is being destroyed or is being enabled
108 return NULL;
109 }
110 double ox = lx, oy = ly;
111 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
112 125
113 if (root->fullscreen_global) { 126 if (root->fullscreen_global) {
114 // Try fullscreen container 127 // Try fullscreen container
@@ -126,11 +139,6 @@ struct sway_node *node_at_coords(
126 return NULL; 139 return NULL;
127 } 140 }
128 141
129 if ((*surface = layer_surface_at(output,
130 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
131 ox, oy, sx, sy))) {
132 return NULL;
133 }
134 if (ws->fullscreen) { 142 if (ws->fullscreen) {
135 // Try transient containers 143 // Try transient containers
136 for (int i = 0; i < ws->floating->length; ++i) { 144 for (int i = 0; i < ws->floating->length; ++i) {
@@ -378,30 +386,29 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
378static void handle_pointer_motion_relative( 386static void handle_pointer_motion_relative(
379 struct wl_listener *listener, void *data) { 387 struct wl_listener *listener, void *data) {
380 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 388 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
381 struct wlr_event_pointer_motion *e = data; 389 struct wlr_pointer_motion_event *e = data;
382 cursor_handle_activity_from_device(cursor, e->device); 390 cursor_handle_activity_from_device(cursor, &e->pointer->base);
383 391
384 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, 392 pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x,
385 e->unaccel_dx, e->unaccel_dy); 393 e->delta_y, e->unaccel_dx, e->unaccel_dy);
386 transaction_commit_dirty();
387} 394}
388 395
389static void handle_pointer_motion_absolute( 396static void handle_pointer_motion_absolute(
390 struct wl_listener *listener, void *data) { 397 struct wl_listener *listener, void *data) {
391 struct sway_cursor *cursor = 398 struct sway_cursor *cursor =
392 wl_container_of(listener, cursor, motion_absolute); 399 wl_container_of(listener, cursor, motion_absolute);
393 struct wlr_event_pointer_motion_absolute *event = data; 400 struct wlr_pointer_motion_absolute_event *event = data;
394 cursor_handle_activity_from_device(cursor, event->device); 401 cursor_handle_activity_from_device(cursor, &event->pointer->base);
395 402
396 double lx, ly; 403 double lx, ly;
397 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 404 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base,
398 event->x, event->y, &lx, &ly); 405 event->x, event->y, &lx, &ly);
399 406
400 double dx = lx - cursor->cursor->x; 407 double dx = lx - cursor->cursor->x;
401 double dy = ly - cursor->cursor->y; 408 double dy = ly - cursor->cursor->y;
402 409
403 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 410 pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy,
404 transaction_commit_dirty(); 411 dx, dy);
405} 412}
406 413
407void dispatch_cursor_button(struct sway_cursor *cursor, 414void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -416,7 +423,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
416 423
417static void handle_pointer_button(struct wl_listener *listener, void *data) { 424static void handle_pointer_button(struct wl_listener *listener, void *data) {
418 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 425 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
419 struct wlr_event_pointer_button *event = data; 426 struct wlr_pointer_button_event *event = data;
420 427
421 if (event->state == WLR_BUTTON_PRESSED) { 428 if (event->state == WLR_BUTTON_PRESSED) {
422 cursor->pressed_button_count++; 429 cursor->pressed_button_count++;
@@ -428,23 +435,21 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
428 } 435 }
429 } 436 }
430 437
431 cursor_handle_activity_from_device(cursor, event->device); 438 cursor_handle_activity_from_device(cursor, &event->pointer->base);
432 dispatch_cursor_button(cursor, event->device, 439 dispatch_cursor_button(cursor, &event->pointer->base,
433 event->time_msec, event->button, event->state); 440 event->time_msec, event->button, event->state);
434 transaction_commit_dirty();
435} 441}
436 442
437void dispatch_cursor_axis(struct sway_cursor *cursor, 443void dispatch_cursor_axis(struct sway_cursor *cursor,
438 struct wlr_event_pointer_axis *event) { 444 struct wlr_pointer_axis_event *event) {
439 seatop_pointer_axis(cursor->seat, event); 445 seatop_pointer_axis(cursor->seat, event);
440} 446}
441 447
442static void handle_pointer_axis(struct wl_listener *listener, void *data) { 448static void handle_pointer_axis(struct wl_listener *listener, void *data) {
443 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 449 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
444 struct wlr_event_pointer_axis *event = data; 450 struct wlr_pointer_axis_event *event = data;
445 cursor_handle_activity_from_device(cursor, event->device); 451 cursor_handle_activity_from_device(cursor, &event->pointer->base);
446 dispatch_cursor_axis(cursor, event); 452 dispatch_cursor_axis(cursor, event);
447 transaction_commit_dirty();
448} 453}
449 454
450static void handle_pointer_frame(struct wl_listener *listener, void *data) { 455static void handle_pointer_frame(struct wl_listener *listener, void *data) {
@@ -454,8 +459,8 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) {
454 459
455static void handle_touch_down(struct wl_listener *listener, void *data) { 460static void handle_touch_down(struct wl_listener *listener, void *data) {
456 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); 461 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
457 struct wlr_event_touch_down *event = data; 462 struct wlr_touch_down_event *event = data;
458 cursor_handle_activity_from_device(cursor, event->device); 463 cursor_handle_activity_from_device(cursor, &event->touch->base);
459 cursor_hide(cursor); 464 cursor_hide(cursor);
460 465
461 struct sway_seat *seat = cursor->seat; 466 struct sway_seat *seat = cursor->seat;
@@ -463,7 +468,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
463 struct wlr_surface *surface = NULL; 468 struct wlr_surface *surface = NULL;
464 469
465 double lx, ly; 470 double lx, ly;
466 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 471 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
467 event->x, event->y, &lx, &ly); 472 event->x, event->y, &lx, &ly);
468 double sx, sy; 473 double sx, sy;
469 struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); 474 struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
@@ -491,28 +496,25 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
491 double dx, dy; 496 double dx, dy;
492 dx = lx - cursor->cursor->x; 497 dx = lx - cursor->cursor->x;
493 dy = ly - cursor->cursor->y; 498 dy = ly - cursor->cursor->y;
494 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 499 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
495 dispatch_cursor_button(cursor, event->device, event->time_msec, 500 dx, dy);
501 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
496 BTN_LEFT, WLR_BUTTON_PRESSED); 502 BTN_LEFT, WLR_BUTTON_PRESSED);
497 wlr_seat_pointer_notify_frame(wlr_seat);
498 transaction_commit_dirty();
499 } 503 }
500} 504}
501 505
502static void handle_touch_up(struct wl_listener *listener, void *data) { 506static void handle_touch_up(struct wl_listener *listener, void *data) {
503 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); 507 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
504 struct wlr_event_touch_up *event = data; 508 struct wlr_touch_up_event *event = data;
505 cursor_handle_activity_from_device(cursor, event->device); 509 cursor_handle_activity_from_device(cursor, &event->touch->base);
506 510
507 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; 511 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
508 512
509 if (cursor->simulating_pointer_from_touch) { 513 if (cursor->simulating_pointer_from_touch) {
510 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 514 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
511 cursor->simulating_pointer_from_touch = false; 515 cursor->pointer_touch_up = true;
512 dispatch_cursor_button(cursor, event->device, event->time_msec, 516 dispatch_cursor_button(cursor, &event->touch->base,
513 BTN_LEFT, WLR_BUTTON_RELEASED); 517 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED);
514 wlr_seat_pointer_notify_frame(wlr_seat);
515 transaction_commit_dirty();
516 } 518 }
517 } else { 519 } else {
518 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 520 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id);
@@ -522,15 +524,15 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
522static void handle_touch_motion(struct wl_listener *listener, void *data) { 524static void handle_touch_motion(struct wl_listener *listener, void *data) {
523 struct sway_cursor *cursor = 525 struct sway_cursor *cursor =
524 wl_container_of(listener, cursor, touch_motion); 526 wl_container_of(listener, cursor, touch_motion);
525 struct wlr_event_touch_motion *event = data; 527 struct wlr_touch_motion_event *event = data;
526 cursor_handle_activity_from_device(cursor, event->device); 528 cursor_handle_activity_from_device(cursor, &event->touch->base);
527 529
528 struct sway_seat *seat = cursor->seat; 530 struct sway_seat *seat = cursor->seat;
529 struct wlr_seat *wlr_seat = seat->wlr_seat; 531 struct wlr_seat *wlr_seat = seat->wlr_seat;
530 struct wlr_surface *surface = NULL; 532 struct wlr_surface *surface = NULL;
531 533
532 double lx, ly; 534 double lx, ly;
533 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 535 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
534 event->x, event->y, &lx, &ly); 536 event->x, event->y, &lx, &ly);
535 double sx, sy; 537 double sx, sy;
536 node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); 538 node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
@@ -552,8 +554,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
552 double dx, dy; 554 double dx, dy;
553 dx = lx - cursor->cursor->x; 555 dx = lx - cursor->cursor->x;
554 dy = ly - cursor->cursor->y; 556 dy = ly - cursor->cursor->y;
555 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 557 pointer_motion(cursor, event->time_msec, &event->touch->base,
556 transaction_commit_dirty(); 558 dx, dy, dx, dy);
557 } 559 }
558 } else if (surface) { 560 } else if (surface) {
559 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 561 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec,
@@ -561,6 +563,24 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
561 } 563 }
562} 564}
563 565
566static void handle_touch_frame(struct wl_listener *listener, void *data) {
567 struct sway_cursor *cursor =
568 wl_container_of(listener, cursor, touch_frame);
569
570 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
571
572 if (cursor->simulating_pointer_from_touch) {
573 wlr_seat_pointer_notify_frame(wlr_seat);
574
575 if (cursor->pointer_touch_up) {
576 cursor->pointer_touch_up = false;
577 cursor->simulating_pointer_from_touch = false;
578 }
579 } else {
580 wlr_seat_touch_notify_frame(wlr_seat);
581 }
582}
583
564static double apply_mapping_from_coord(double low, double high, double value) { 584static double apply_mapping_from_coord(double low, double high, double value) {
565 if (isnan(value)) { 585 if (isnan(value)) {
566 return value; 586 return value;
@@ -574,14 +594,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
574 double x1 = region->x1, x2 = region->x2; 594 double x1 = region->x1, x2 = region->x2;
575 double y1 = region->y1, y2 = region->y2; 595 double y1 = region->y1, y2 = region->y2;
576 596
577 if (region->mm) { 597 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
578 if (device->width_mm == 0 || device->height_mm == 0) { 598 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
599 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
579 return; 600 return;
580 } 601 }
581 x1 /= device->width_mm; 602 x1 /= tablet->width_mm;
582 x2 /= device->width_mm; 603 x2 /= tablet->width_mm;
583 y1 /= device->height_mm; 604 y1 /= tablet->height_mm;
584 y2 /= device->height_mm; 605 y2 /= tablet->height_mm;
585 } 606 }
586 607
587 *x = apply_mapping_from_coord(x1, x2, *x); 608 *x = apply_mapping_from_coord(x1, x2, *x);
@@ -639,14 +660,12 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
639 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 660 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);
640 pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); 661 pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy);
641 } 662 }
642
643 transaction_commit_dirty();
644} 663}
645 664
646static void handle_tool_axis(struct wl_listener *listener, void *data) { 665static void handle_tool_axis(struct wl_listener *listener, void *data) {
647 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); 666 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
648 struct wlr_event_tablet_tool_axis *event = data; 667 struct wlr_tablet_tool_axis_event *event = data;
649 cursor_handle_activity_from_device(cursor, event->device); 668 cursor_handle_activity_from_device(cursor, &event->tablet->base);
650 669
651 struct sway_tablet_tool *sway_tool = event->tool->data; 670 struct sway_tablet_tool *sway_tool = event->tool->data;
652 if (!sway_tool) { 671 if (!sway_tool) {
@@ -701,8 +720,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
701 720
702static void handle_tool_tip(struct wl_listener *listener, void *data) { 721static void handle_tool_tip(struct wl_listener *listener, void *data) {
703 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); 722 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
704 struct wlr_event_tablet_tool_tip *event = data; 723 struct wlr_tablet_tool_tip_event *event = data;
705 cursor_handle_activity_from_device(cursor, event->device); 724 cursor_handle_activity_from_device(cursor, &event->tablet->base);
706 725
707 struct sway_tablet_tool *sway_tool = event->tool->data; 726 struct sway_tablet_tool *sway_tool = event->tool->data;
708 struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; 727 struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;
@@ -717,10 +736,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
717 if (cursor->simulating_pointer_from_tool_tip && 736 if (cursor->simulating_pointer_from_tool_tip &&
718 event->state == WLR_TABLET_TOOL_TIP_UP) { 737 event->state == WLR_TABLET_TOOL_TIP_UP) {
719 cursor->simulating_pointer_from_tool_tip = false; 738 cursor->simulating_pointer_from_tool_tip = false;
720 dispatch_cursor_button(cursor, event->device, event->time_msec, 739 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
721 BTN_LEFT, WLR_BUTTON_RELEASED); 740 BTN_LEFT, WLR_BUTTON_RELEASED);
722 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 741 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
723 transaction_commit_dirty();
724 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 742 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
725 // If we started holding the tool tip down on a surface that accepts 743 // If we started holding the tool tip down on a surface that accepts
726 // tablet v2, we should notify that surface if it gets released over a 744 // tablet v2, we should notify that surface if it gets released over a
@@ -730,10 +748,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
730 WLR_TABLET_TOOL_TIP_UP); 748 WLR_TABLET_TOOL_TIP_UP);
731 } else { 749 } else {
732 cursor->simulating_pointer_from_tool_tip = true; 750 cursor->simulating_pointer_from_tool_tip = true;
733 dispatch_cursor_button(cursor, event->device, event->time_msec, 751 dispatch_cursor_button(cursor, &event->tablet->base,
734 BTN_LEFT, WLR_BUTTON_PRESSED); 752 event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED);
735 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 753 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
736 transaction_commit_dirty();
737 } 754 }
738 } else { 755 } else {
739 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); 756 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state);
@@ -754,12 +771,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor,
754static void handle_tool_proximity(struct wl_listener *listener, void *data) { 771static void handle_tool_proximity(struct wl_listener *listener, void *data) {
755 struct sway_cursor *cursor = 772 struct sway_cursor *cursor =
756 wl_container_of(listener, cursor, tool_proximity); 773 wl_container_of(listener, cursor, tool_proximity);
757 struct wlr_event_tablet_tool_proximity *event = data; 774 struct wlr_tablet_tool_proximity_event *event = data;
758 cursor_handle_activity_from_device(cursor, event->device); 775 cursor_handle_activity_from_device(cursor, &event->tablet->base);
759 776
760 struct wlr_tablet_tool *tool = event->tool; 777 struct wlr_tablet_tool *tool = event->tool;
761 if (!tool->data) { 778 if (!tool->data) {
762 struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); 779 struct sway_tablet *tablet = get_tablet_for_device(cursor,
780 &event->tablet->base);
763 if (!tablet) { 781 if (!tablet) {
764 sway_log(SWAY_ERROR, "no tablet for tablet tool"); 782 sway_log(SWAY_ERROR, "no tablet for tablet tool");
765 return; 783 return;
@@ -784,8 +802,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
784 802
785static void handle_tool_button(struct wl_listener *listener, void *data) { 803static void handle_tool_button(struct wl_listener *listener, void *data) {
786 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); 804 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
787 struct wlr_event_tablet_tool_button *event = data; 805 struct wlr_tablet_tool_button_event *event = data;
788 cursor_handle_activity_from_device(cursor, event->device); 806 cursor_handle_activity_from_device(cursor, &event->tablet->base);
789 807
790 struct sway_tablet_tool *sway_tool = event->tool->data; 808 struct sway_tablet_tool *sway_tool = event->tool->data;
791 if (!sway_tool) { 809 if (!sway_tool) {
@@ -806,21 +824,20 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
806 switch (event->state) { 824 switch (event->state) {
807 case WLR_BUTTON_PRESSED: 825 case WLR_BUTTON_PRESSED:
808 if (cursor->tool_buttons == 0) { 826 if (cursor->tool_buttons == 0) {
809 dispatch_cursor_button(cursor, event->device, 827 dispatch_cursor_button(cursor, &event->tablet->base,
810 event->time_msec, BTN_RIGHT, event->state); 828 event->time_msec, BTN_RIGHT, event->state);
811 } 829 }
812 cursor->tool_buttons++; 830 cursor->tool_buttons++;
813 break; 831 break;
814 case WLR_BUTTON_RELEASED: 832 case WLR_BUTTON_RELEASED:
815 if (cursor->tool_buttons == 1) { 833 if (cursor->tool_buttons == 1) {
816 dispatch_cursor_button(cursor, event->device, 834 dispatch_cursor_button(cursor, &event->tablet->base,
817 event->time_msec, BTN_RIGHT, event->state); 835 event->time_msec, BTN_RIGHT, event->state);
818 } 836 }
819 cursor->tool_buttons--; 837 cursor->tool_buttons--;
820 break; 838 break;
821 } 839 }
822 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 840 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
823 transaction_commit_dirty();
824 return; 841 return;
825 } 842 }
826 843
@@ -837,8 +854,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
837 854
838 struct sway_container *con = view->container; 855 struct sway_container *con = view->container;
839 856
840 double sx = cursor->cursor->x - con->content_x + view->geometry.x; 857 double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x;
841 double sy = cursor->cursor->y - con->content_y + view->geometry.y; 858 double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y;
842 859
843 if (!pixman_region32_contains_point(region, 860 if (!pixman_region32_contains_point(region,
844 floor(sx), floor(sy), NULL)) { 861 floor(sx), floor(sy), NULL)) {
@@ -849,8 +866,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
849 double sy = (boxes[0].y1 + boxes[0].y2) / 2.; 866 double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
850 867
851 wlr_cursor_warp_closest(cursor->cursor, NULL, 868 wlr_cursor_warp_closest(cursor->cursor, NULL,
852 sx + con->content_x - view->geometry.x, 869 sx + con->pending.content_x - view->geometry.x,
853 sy + con->content_y - view->geometry.y); 870 sy + con->pending.content_y - view->geometry.y);
854 871
855 cursor_rebase(cursor); 872 cursor_rebase(cursor);
856 } 873 }
@@ -911,59 +928,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
911 event->hotspot_y, focused_client); 928 event->hotspot_y, focused_client);
912} 929}
913 930
931static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
932 struct sway_cursor *cursor = wl_container_of(
933 listener, cursor, hold_begin);
934 struct wlr_pointer_hold_begin_event *event = data;
935 cursor_handle_activity_from_device(cursor, &event->pointer->base);
936 seatop_hold_begin(cursor->seat, event);
937}
938
939static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
940 struct sway_cursor *cursor = wl_container_of(
941 listener, cursor, hold_end);
942 struct wlr_pointer_hold_end_event *event = data;
943 cursor_handle_activity_from_device(cursor, &event->pointer->base);
944 seatop_hold_end(cursor->seat, event);
945}
946
914static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { 947static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
915 struct sway_cursor *cursor = wl_container_of( 948 struct sway_cursor *cursor = wl_container_of(
916 listener, cursor, pinch_begin); 949 listener, cursor, pinch_begin);
917 struct wlr_event_pointer_pinch_begin *event = data; 950 struct wlr_pointer_pinch_begin_event *event = data;
918 wlr_pointer_gestures_v1_send_pinch_begin( 951 cursor_handle_activity_from_device(cursor, &event->pointer->base);
919 cursor->pointer_gestures, cursor->seat->wlr_seat, 952 seatop_pinch_begin(cursor->seat, event);
920 event->time_msec, event->fingers);
921} 953}
922 954
923static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { 955static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
924 struct sway_cursor *cursor = wl_container_of( 956 struct sway_cursor *cursor = wl_container_of(
925 listener, cursor, pinch_update); 957 listener, cursor, pinch_update);
926 struct wlr_event_pointer_pinch_update *event = data; 958 struct wlr_pointer_pinch_update_event *event = data;
927 wlr_pointer_gestures_v1_send_pinch_update( 959 cursor_handle_activity_from_device(cursor, &event->pointer->base);
928 cursor->pointer_gestures, cursor->seat->wlr_seat, 960 seatop_pinch_update(cursor->seat, event);
929 event->time_msec, event->dx, event->dy,
930 event->scale, event->rotation);
931} 961}
932 962
933static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { 963static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
934 struct sway_cursor *cursor = wl_container_of( 964 struct sway_cursor *cursor = wl_container_of(
935 listener, cursor, pinch_end); 965 listener, cursor, pinch_end);
936 struct wlr_event_pointer_pinch_end *event = data; 966 struct wlr_pointer_pinch_end_event *event = data;
937 wlr_pointer_gestures_v1_send_pinch_end( 967 cursor_handle_activity_from_device(cursor, &event->pointer->base);
938 cursor->pointer_gestures, cursor->seat->wlr_seat, 968 seatop_pinch_end(cursor->seat, event);
939 event->time_msec, event->cancelled);
940} 969}
941 970
942static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { 971static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
943 struct sway_cursor *cursor = wl_container_of( 972 struct sway_cursor *cursor = wl_container_of(
944 listener, cursor, swipe_begin); 973 listener, cursor, swipe_begin);
945 struct wlr_event_pointer_swipe_begin *event = data; 974 struct wlr_pointer_swipe_begin_event *event = data;
946 wlr_pointer_gestures_v1_send_swipe_begin( 975 cursor_handle_activity_from_device(cursor, &event->pointer->base);
947 cursor->pointer_gestures, cursor->seat->wlr_seat, 976 seatop_swipe_begin(cursor->seat, event);
948 event->time_msec, event->fingers);
949} 977}
950 978
951static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { 979static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
952 struct sway_cursor *cursor = wl_container_of( 980 struct sway_cursor *cursor = wl_container_of(
953 listener, cursor, swipe_update); 981 listener, cursor, swipe_update);
954 struct wlr_event_pointer_swipe_update *event = data; 982 struct wlr_pointer_swipe_update_event *event = data;
955 wlr_pointer_gestures_v1_send_swipe_update( 983 cursor_handle_activity_from_device(cursor, &event->pointer->base);
956 cursor->pointer_gestures, cursor->seat->wlr_seat, 984 seatop_swipe_update(cursor->seat, event);
957 event->time_msec, event->dx, event->dy);
958} 985}
959 986
960static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { 987static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
961 struct sway_cursor *cursor = wl_container_of( 988 struct sway_cursor *cursor = wl_container_of(
962 listener, cursor, swipe_end); 989 listener, cursor, swipe_end);
963 struct wlr_event_pointer_swipe_end *event = data; 990 struct wlr_pointer_swipe_end_event *event = data;
964 wlr_pointer_gestures_v1_send_swipe_end( 991 cursor_handle_activity_from_device(cursor, &event->pointer->base);
965 cursor->pointer_gestures, cursor->seat->wlr_seat, 992 seatop_swipe_end(cursor->seat, event);
966 event->time_msec, event->cancelled);
967} 993}
968 994
969static void handle_image_surface_destroy(struct wl_listener *listener, 995static void handle_image_surface_destroy(struct wl_listener *listener,
@@ -1037,6 +1063,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1037 wl_event_source_remove(cursor->hide_source); 1063 wl_event_source_remove(cursor->hide_source);
1038 1064
1039 wl_list_remove(&cursor->image_surface_destroy.link); 1065 wl_list_remove(&cursor->image_surface_destroy.link);
1066 wl_list_remove(&cursor->hold_begin.link);
1067 wl_list_remove(&cursor->hold_end.link);
1040 wl_list_remove(&cursor->pinch_begin.link); 1068 wl_list_remove(&cursor->pinch_begin.link);
1041 wl_list_remove(&cursor->pinch_update.link); 1069 wl_list_remove(&cursor->pinch_update.link);
1042 wl_list_remove(&cursor->pinch_end.link); 1070 wl_list_remove(&cursor->pinch_end.link);
@@ -1051,6 +1079,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1051 wl_list_remove(&cursor->touch_down.link); 1079 wl_list_remove(&cursor->touch_down.link);
1052 wl_list_remove(&cursor->touch_up.link); 1080 wl_list_remove(&cursor->touch_up.link);
1053 wl_list_remove(&cursor->touch_motion.link); 1081 wl_list_remove(&cursor->touch_motion.link);
1082 wl_list_remove(&cursor->touch_frame.link);
1054 wl_list_remove(&cursor->tool_axis.link); 1083 wl_list_remove(&cursor->tool_axis.link);
1055 wl_list_remove(&cursor->tool_tip.link); 1084 wl_list_remove(&cursor->tool_tip.link);
1056 wl_list_remove(&cursor->tool_button.link); 1085 wl_list_remove(&cursor->tool_button.link);
@@ -1085,19 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1085 wl_list_init(&cursor->image_surface_destroy.link); 1114 wl_list_init(&cursor->image_surface_destroy.link);
1086 cursor->image_surface_destroy.notify = handle_image_surface_destroy; 1115 cursor->image_surface_destroy.notify = handle_image_surface_destroy;
1087 1116
1117 // gesture events
1088 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); 1118 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
1089 cursor->pinch_begin.notify = handle_pointer_pinch_begin; 1119
1120 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1121 cursor->hold_begin.notify = handle_pointer_hold_begin;
1122 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1123 cursor->hold_end.notify = handle_pointer_hold_end;
1124
1090 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); 1125 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
1091 cursor->pinch_update.notify = handle_pointer_pinch_update; 1126 cursor->pinch_begin.notify = handle_pointer_pinch_begin;
1092 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); 1127 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
1093 cursor->pinch_end.notify = handle_pointer_pinch_end; 1128 cursor->pinch_update.notify = handle_pointer_pinch_update;
1094 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); 1129 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
1095 cursor->swipe_begin.notify = handle_pointer_swipe_begin; 1130 cursor->pinch_end.notify = handle_pointer_pinch_end;
1131
1096 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); 1132 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
1097 cursor->swipe_update.notify = handle_pointer_swipe_update; 1133 cursor->swipe_begin.notify = handle_pointer_swipe_begin;
1098 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); 1134 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
1099 cursor->swipe_end.notify = handle_pointer_swipe_end; 1135 cursor->swipe_update.notify = handle_pointer_swipe_update;
1100 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); 1136 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
1137 cursor->swipe_end.notify = handle_pointer_swipe_end;
1101 1138
1102 // input events 1139 // input events
1103 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1140 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@@ -1126,6 +1163,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1126 &cursor->touch_motion); 1163 &cursor->touch_motion);
1127 cursor->touch_motion.notify = handle_touch_motion; 1164 cursor->touch_motion.notify = handle_touch_motion;
1128 1165
1166 wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame);
1167 cursor->touch_frame.notify = handle_touch_frame;
1168
1129 wl_signal_add(&wlr_cursor->events.tablet_tool_axis, 1169 wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
1130 &cursor->tool_axis); 1170 &cursor->tool_axis);
1131 cursor->tool_axis.notify = handle_tool_axis; 1171 cursor->tool_axis.notify = handle_tool_axis;
@@ -1170,8 +1210,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
1170 return; 1210 return;
1171 } 1211 }
1172 1212
1173 double x = container->x + container->width / 2.0; 1213 double x = container->pending.x + container->pending.width / 2.0;
1174 double y = container->y + container->height / 2.0; 1214 double y = container->pending.y + container->pending.height / 2.0;
1175 1215
1176 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1216 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1177 cursor_unhide(cursor); 1217 cursor_unhide(cursor);
@@ -1284,8 +1324,8 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1284 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1324 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1285 struct sway_container *con = view->container; 1325 struct sway_container *con = view->container;
1286 1326
1287 double lx = sx + con->content_x - view->geometry.x; 1327 double lx = sx + con->pending.content_x - view->geometry.x;
1288 double ly = sy + con->content_y - view->geometry.y; 1328 double ly = sy + con->pending.content_y - view->geometry.y;
1289 1329
1290 wlr_cursor_warp(cursor->cursor, NULL, lx, ly); 1330 wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
1291 1331
@@ -1333,7 +1373,7 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1333 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1373 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1334 1374
1335 struct sway_node *focus = seat_get_focus(seat); 1375 struct sway_node *focus = seat_get_focus(seat);
1336 if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { 1376 if (focus && node_is_view(focus)) {
1337 struct wlr_surface *surface = focus->sway_container->view->surface; 1377 struct wlr_surface *surface = focus->sway_container->view->surface;
1338 if (surface == constraint->surface) { 1378 if (surface == constraint->surface) {
1339 sway_cursor_constrain(seat->cursor, constraint); 1379 sway_cursor_constrain(seat->cursor, constraint);
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index f04a8ce0..4a0bce0e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -289,6 +289,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
289 struct sway_input_manager *input_manager = wl_container_of( 289 struct sway_input_manager *input_manager = wl_container_of(
290 listener, input_manager, inhibit_deactivate); 290 listener, input_manager, inhibit_deactivate);
291 struct sway_seat *seat; 291 struct sway_seat *seat;
292 if (server.session_lock.locked) {
293 // Don't deactivate the grab of a screenlocker
294 return;
295 }
292 wl_list_for_each(seat, &input_manager->seats, link) { 296 wl_list_for_each(seat, &input_manager->seats, link) {
293 seat_set_exclusive_client(seat, NULL); 297 seat_set_exclusive_client(seat, NULL);
294 struct sway_node *previous = seat_get_focus(seat); 298 struct sway_node *previous = seat_get_focus(seat);
@@ -377,7 +381,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
377 struct sway_input_manager *input_manager = 381 struct sway_input_manager *input_manager =
378 wl_container_of(listener, input_manager, virtual_keyboard_new); 382 wl_container_of(listener, input_manager, virtual_keyboard_new);
379 struct wlr_virtual_keyboard_v1 *keyboard = data; 383 struct wlr_virtual_keyboard_v1 *keyboard = data;
380 struct wlr_input_device *device = &keyboard->input_device; 384 struct wlr_input_device *device = &keyboard->keyboard.base;
381 385
382 // TODO: Amend protocol to allow NULL seat 386 // TODO: Amend protocol to allow NULL seat
383 struct sway_seat *seat = keyboard->seat ? 387 struct sway_seat *seat = keyboard->seat ?
@@ -410,7 +414,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
410 wl_container_of(listener, input_manager, virtual_pointer_new); 414 wl_container_of(listener, input_manager, virtual_pointer_new);
411 struct wlr_virtual_pointer_v1_new_pointer_event *event = data; 415 struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
412 struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; 416 struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
413 struct wlr_input_device *device = &pointer->input_device; 417 struct wlr_input_device *device = &pointer->pointer.base;
414 418
415 struct sway_seat *seat = event->suggested_seat ? 419 struct sway_seat *seat = event->suggested_seat ?
416 input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : 420 input_manager_sway_seat_from_wlr_seat(event->suggested_seat) :
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index ce259eb2..5e5692f1 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -9,7 +9,6 @@
9#include <wlr/types/wlr_keyboard_group.h> 9#include <wlr/types/wlr_keyboard_group.h>
10#include <xkbcommon/xkbcommon-names.h> 10#include <xkbcommon/xkbcommon-names.h>
11#include "sway/commands.h" 11#include "sway/commands.h"
12#include "sway/desktop/transaction.h"
13#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
14#include "sway/input/keyboard.h" 13#include "sway/input/keyboard.h"
15#include "sway/input/seat.h" 14#include "sway/input/seat.h"
@@ -292,14 +291,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
292static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, 291static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
293 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 292 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
294 uint32_t *modifiers) { 293 uint32_t *modifiers) {
295 struct wlr_input_device *device = 294 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
296 keyboard->seat_device->input_device->wlr_device;
297 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
298 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( 295 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
299 device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); 296 keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
300 *modifiers = *modifiers & ~consumed; 297 *modifiers = *modifiers & ~consumed;
301 298
302 return xkb_state_key_get_syms(device->keyboard->xkb_state, 299 return xkb_state_key_get_syms(keyboard->wlr->xkb_state,
303 keycode, keysyms); 300 keycode, keysyms);
304} 301}
305 302
@@ -315,13 +312,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
315static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, 312static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
316 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 313 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
317 uint32_t *modifiers) { 314 uint32_t *modifiers) {
318 struct wlr_input_device *device = 315 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
319 keyboard->seat_device->input_device->wlr_device;
320 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
321 316
322 xkb_layout_index_t layout_index = xkb_state_key_get_layout( 317 xkb_layout_index_t layout_index = xkb_state_key_get_layout(
323 device->keyboard->xkb_state, keycode); 318 keyboard->wlr->xkb_state, keycode);
324 return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, 319 return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,
325 keycode, layout_index, 0, keysyms); 320 keycode, layout_index, 0, keysyms);
326} 321}
327 322
@@ -361,8 +356,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
361 keyinfo->keycode, &keyinfo->translated_keysyms, 356 keyinfo->keycode, &keyinfo->translated_keysyms,
362 &keyinfo->translated_modifiers); 357 &keyinfo->translated_modifiers);
363 358
364 keyinfo->code_modifiers = wlr_keyboard_get_modifiers( 359 keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
365 keyboard->seat_device->input_device->wlr_device->keyboard);
366 360
367 // Update shortcut model keyinfo 361 // Update shortcut model keyinfo
368 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, 362 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,
@@ -379,16 +373,39 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
379 } 373 }
380} 374}
381 375
376/**
377 * Get keyboard grab of the seat from sway_keyboard if we should forward events
378 * to it.
379 *
380 * Returns NULL if the keyboard is not grabbed by an input method,
381 * or if event is from virtual keyboard of the same client as grab.
382 * TODO: see swaywm/wlroots#2322
383 */
384static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(
385 struct sway_keyboard *keyboard) {
386 struct wlr_input_method_v2 *input_method = keyboard->seat_device->
387 sway_seat->im_relay.input_method;
388 struct wlr_virtual_keyboard_v1 *virtual_keyboard =
389 wlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device);
390 if (!input_method || !input_method->keyboard_grab || (virtual_keyboard &&
391 wl_resource_get_client(virtual_keyboard->resource) ==
392 wl_resource_get_client(input_method->keyboard_grab->resource))) {
393 return NULL;
394 }
395 return input_method->keyboard_grab;
396}
397
382static void handle_key_event(struct sway_keyboard *keyboard, 398static void handle_key_event(struct sway_keyboard *keyboard,
383 struct wlr_event_keyboard_key *event) { 399 struct wlr_keyboard_key_event *event) {
384 struct sway_seat *seat = keyboard->seat_device->sway_seat; 400 struct sway_seat *seat = keyboard->seat_device->sway_seat;
385 struct wlr_seat *wlr_seat = seat->wlr_seat; 401 struct wlr_seat *wlr_seat = seat->wlr_seat;
386 struct wlr_input_device *wlr_device = 402 struct wlr_input_device *wlr_device =
387 keyboard->seat_device->input_device->wlr_device; 403 keyboard->seat_device->input_device->wlr_device;
388 char *device_identifier = input_device_get_identifier(wlr_device); 404 char *device_identifier = input_device_get_identifier(wlr_device);
389 bool exact_identifier = wlr_device->keyboard->group != NULL; 405 bool exact_identifier = keyboard->wlr->group != NULL;
390 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); 406 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
391 bool input_inhibited = seat->exclusive_client != NULL; 407 bool input_inhibited = seat->exclusive_client != NULL ||
408 server.session_lock.locked;
392 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = 409 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
393 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 410 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
394 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; 411 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@@ -456,10 +473,10 @@ static void handle_key_event(struct sway_keyboard *keyboard,
456 // Set up (or clear) keyboard repeat for a pressed binding. Since the 473 // Set up (or clear) keyboard repeat for a pressed binding. Since the
457 // binding may remove the keyboard, the timer needs to be updated first 474 // binding may remove the keyboard, the timer needs to be updated first
458 if (binding && !(binding->flags & BINDING_NOREPEAT) && 475 if (binding && !(binding->flags & BINDING_NOREPEAT) &&
459 wlr_device->keyboard->repeat_info.delay > 0) { 476 keyboard->wlr->repeat_info.delay > 0) {
460 keyboard->repeat_binding = binding; 477 keyboard->repeat_binding = binding;
461 if (wl_event_source_timer_update(keyboard->key_repeat_source, 478 if (wl_event_source_timer_update(keyboard->key_repeat_source,
462 wlr_device->keyboard->repeat_info.delay) < 0) { 479 keyboard->wlr->repeat_info.delay) < 0) {
463 sway_log(SWAY_DEBUG, "failed to set key repeat timer"); 480 sway_log(SWAY_DEBUG, "failed to set key repeat timer");
464 } 481 }
465 } else if (keyboard->repeat_binding) { 482 } else if (keyboard->repeat_binding) {
@@ -471,7 +488,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
471 handled = true; 488 handled = true;
472 } 489 }
473 490
474 if (!handled && wlr_device->keyboard->group) { 491 if (!handled && keyboard->wlr->group) {
475 // Only handle device specific bindings for keyboards in a group 492 // Only handle device specific bindings for keyboards in a group
476 free(device_identifier); 493 free(device_identifier);
477 return; 494 return;
@@ -489,18 +506,41 @@ static void handle_key_event(struct sway_keyboard *keyboard,
489 keyinfo.raw_keysyms_len); 506 keyinfo.raw_keysyms_len);
490 } 507 }
491 508
492 if (!handled || event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { 509 if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
510 // If the pressed event was sent to a client, also send the released
511 // event. In particular, don't send the released event to the IM grab.
493 bool pressed_sent = update_shortcut_state( 512 bool pressed_sent = update_shortcut_state(
494 &keyboard->state_pressed_sent, event->keycode, event->state, 513 &keyboard->state_pressed_sent, event->keycode,
495 keyinfo.keycode, 0); 514 event->state, keyinfo.keycode, 0);
496 if (pressed_sent || event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 515 if (pressed_sent) {
497 wlr_seat_set_keyboard(wlr_seat, wlr_device); 516 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
498 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 517 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
499 event->keycode, event->state); 518 event->keycode, event->state);
519 handled = true;
500 } 520 }
501 } 521 }
502 522
503 transaction_commit_dirty(); 523 if (!handled) {
524 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
525
526 if (kb_grab) {
527 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
528 wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
529 event->time_msec, event->keycode, event->state);
530 handled = true;
531 }
532 }
533
534 if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) {
535 // If a released event failed pressed sent test, and not in sent to
536 // keyboard grab, it is still not handled. Don't handle released here.
537 update_shortcut_state(
538 &keyboard->state_pressed_sent, event->keycode, event->state,
539 keyinfo.keycode, 0);
540 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
541 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
542 event->keycode, event->state);
543 }
504 544
505 free(device_identifier); 545 free(device_identifier);
506} 546}
@@ -573,21 +613,18 @@ static void handle_keyboard_group_leave(struct wl_listener *listener,
573} 613}
574 614
575static int handle_keyboard_repeat(void *data) { 615static int handle_keyboard_repeat(void *data) {
576 struct sway_keyboard *keyboard = (struct sway_keyboard *)data; 616 struct sway_keyboard *keyboard = data;
577 struct wlr_keyboard *wlr_device =
578 keyboard->seat_device->input_device->wlr_device->keyboard;
579 if (keyboard->repeat_binding) { 617 if (keyboard->repeat_binding) {
580 if (wlr_device->repeat_info.rate > 0) { 618 if (keyboard->wlr->repeat_info.rate > 0) {
581 // We queue the next event first, as the command might cancel it 619 // We queue the next event first, as the command might cancel it
582 if (wl_event_source_timer_update(keyboard->key_repeat_source, 620 if (wl_event_source_timer_update(keyboard->key_repeat_source,
583 1000 / wlr_device->repeat_info.rate) < 0) { 621 1000 / keyboard->wlr->repeat_info.rate) < 0) {
584 sway_log(SWAY_DEBUG, "failed to update key repeat timer"); 622 sway_log(SWAY_DEBUG, "failed to update key repeat timer");
585 } 623 }
586 } 624 }
587 625
588 seat_execute_command(keyboard->seat_device->sway_seat, 626 seat_execute_command(keyboard->seat_device->sway_seat,
589 keyboard->repeat_binding); 627 keyboard->repeat_binding);
590 transaction_commit_dirty();
591 } 628 }
592 return 0; 629 return 0;
593} 630}
@@ -614,22 +651,28 @@ static void determine_bar_visibility(uint32_t modifiers) {
614} 651}
615 652
616static void handle_modifier_event(struct sway_keyboard *keyboard) { 653static void handle_modifier_event(struct sway_keyboard *keyboard) {
617 struct wlr_input_device *wlr_device = 654 if (!keyboard->wlr->group) {
618 keyboard->seat_device->input_device->wlr_device; 655 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
619 if (!wlr_device->keyboard->group) {
620 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
621 wlr_seat_set_keyboard(wlr_seat, wlr_device);
622 wlr_seat_keyboard_notify_modifiers(wlr_seat,
623 &wlr_device->keyboard->modifiers);
624 656
625 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); 657 if (kb_grab) {
658 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
659 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
660 &keyboard->wlr->modifiers);
661 } else {
662 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
663 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
664 wlr_seat_keyboard_notify_modifiers(wlr_seat,
665 &keyboard->wlr->modifiers);
666 }
667
668 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
626 determine_bar_visibility(modifiers); 669 determine_bar_visibility(modifiers);
627 } 670 }
628 671
629 if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { 672 if (keyboard->wlr->modifiers.group != keyboard->effective_layout) {
630 keyboard->effective_layout = wlr_device->keyboard->modifiers.group; 673 keyboard->effective_layout = keyboard->wlr->modifiers.group;
631 674
632 if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { 675 if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {
633 ipc_event_input("xkb_layout", keyboard->seat_device->input_device); 676 ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
634 } 677 }
635 } 678 }
@@ -658,6 +701,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
658 } 701 }
659 702
660 keyboard->seat_device = device; 703 keyboard->seat_device = device;
704 keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);
661 device->keyboard = keyboard; 705 device->keyboard = keyboard;
662 706
663 wl_list_init(&keyboard->keyboard_key.link); 707 wl_list_init(&keyboard->keyboard_key.link);
@@ -766,13 +810,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) {
766 810
767static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { 811static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
768 struct sway_input_device *device = keyboard->seat_device->input_device; 812 struct sway_input_device *device = keyboard->seat_device->input_device;
769 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; 813 struct wlr_keyboard_group *wlr_group = keyboard->wlr->group;
770 struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
771 814
772 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", 815 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
773 device->identifier, wlr_group); 816 device->identifier, wlr_group);
774 817
775 wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); 818 wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);
776 819
777 if (wl_list_empty(&wlr_group->devices)) { 820 if (wl_list_empty(&wlr_group->devices)) {
778 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", 821 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
@@ -797,9 +840,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
797} 840}
798 841
799static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { 842static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
800 struct sway_input_device *device = keyboard->seat_device->input_device; 843 if (!keyboard->wlr->group) {
801 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
802 if (!wlr_keyboard->group) {
803 return; 844 return;
804 } 845 }
805 846
@@ -815,7 +856,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
815 break; 856 break;
816 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ 857 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
817 case KEYBOARD_GROUP_SMART:; 858 case KEYBOARD_GROUP_SMART:;
818 struct wlr_keyboard_group *group = wlr_keyboard->group; 859 struct wlr_keyboard_group *group = keyboard->wlr->group;
819 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || 860 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||
820 !repeat_info_match(keyboard, &group->keyboard)) { 861 !repeat_info_match(keyboard, &group->keyboard)) {
821 sway_keyboard_group_remove(keyboard); 862 sway_keyboard_group_remove(keyboard);
@@ -826,7 +867,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
826 867
827static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { 868static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
828 struct sway_input_device *device = keyboard->seat_device->input_device; 869 struct sway_input_device *device = keyboard->seat_device->input_device;
829 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
830 struct sway_seat *seat = keyboard->seat_device->sway_seat; 870 struct sway_seat *seat = keyboard->seat_device->sway_seat;
831 struct seat_config *sc = seat_get_config(seat); 871 struct seat_config *sc = seat_get_config(seat);
832 872
@@ -858,7 +898,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
858 repeat_info_match(keyboard, &wlr_group->keyboard)) { 898 repeat_info_match(keyboard, &wlr_group->keyboard)) {
859 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 899 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
860 device->identifier, wlr_group); 900 device->identifier, wlr_group);
861 wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); 901 wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);
862 return; 902 return;
863 } 903 }
864 break; 904 break;
@@ -897,7 +937,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
897 goto cleanup; 937 goto cleanup;
898 } 938 }
899 sway_group->seat_device->input_device->wlr_device = 939 sway_group->seat_device->input_device->wlr_device =
900 sway_group->wlr_group->input_device; 940 &sway_group->wlr_group->keyboard.base;
901 941
902 if (!sway_keyboard_create(seat, sway_group->seat_device)) { 942 if (!sway_keyboard_create(seat, sway_group->seat_device)) {
903 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); 943 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
@@ -906,7 +946,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
906 946
907 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 947 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
908 device->identifier, sway_group->wlr_group); 948 device->identifier, sway_group->wlr_group);
909 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); 949 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);
910 950
911 wl_list_insert(&seat->keyboard_groups, &sway_group->link); 951 wl_list_insert(&seat->keyboard_groups, &sway_group->link);
912 952
@@ -938,10 +978,8 @@ cleanup:
938void sway_keyboard_configure(struct sway_keyboard *keyboard) { 978void sway_keyboard_configure(struct sway_keyboard *keyboard) {
939 struct input_config *input_config = 979 struct input_config *input_config =
940 input_device_get_config(keyboard->seat_device->input_device); 980 input_device_get_config(keyboard->seat_device->input_device);
941 struct wlr_input_device *wlr_device =
942 keyboard->seat_device->input_device->wlr_device;
943 981
944 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), 982 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),
945 "sway_keyboard_configure should not be called with a " 983 "sway_keyboard_configure should not be called with a "
946 "keyboard group's keyboard")) { 984 "keyboard group's keyboard")) {
947 return; 985 return;
@@ -983,11 +1021,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
983 1021
984 sway_keyboard_group_remove_invalid(keyboard); 1022 sway_keyboard_group_remove_invalid(keyboard);
985 1023
986 wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); 1024 wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);
987 wlr_keyboard_set_repeat_info(wlr_device->keyboard, 1025 wlr_keyboard_set_repeat_info(keyboard->wlr,
988 keyboard->repeat_rate, keyboard->repeat_delay); 1026 keyboard->repeat_rate, keyboard->repeat_delay);
989 1027
990 if (!wlr_device->keyboard->group) { 1028 if (!keyboard->wlr->group) {
991 sway_keyboard_group_add(keyboard); 1029 sway_keyboard_group_add(keyboard);
992 } 1030 }
993 1031
@@ -1007,40 +1045,38 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1007 } 1045 }
1008 } 1046 }
1009 if (locked_mods) { 1047 if (locked_mods) {
1010 wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, 1048 wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,
1011 locked_mods, 0); 1049 locked_mods, 0);
1012 uint32_t leds = 0; 1050 uint32_t leds = 0;
1013 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { 1051 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
1014 if (xkb_state_led_index_is_active( 1052 if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,
1015 wlr_device->keyboard->xkb_state, 1053 keyboard->wlr->led_indexes[i])) {
1016 wlr_device->keyboard->led_indexes[i])) {
1017 leds |= (1 << i); 1054 leds |= (1 << i);
1018 } 1055 }
1019 } 1056 }
1020 if (wlr_device->keyboard->group) { 1057 if (keyboard->wlr->group) {
1021 wlr_keyboard_led_update( 1058 wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);
1022 &wlr_device->keyboard->group->keyboard, leds);
1023 } else { 1059 } else {
1024 wlr_keyboard_led_update(wlr_device->keyboard, leds); 1060 wlr_keyboard_led_update(keyboard->wlr, leds);
1025 } 1061 }
1026 } 1062 }
1027 } else { 1063 } else {
1028 xkb_keymap_unref(keymap); 1064 xkb_keymap_unref(keymap);
1029 sway_keyboard_group_remove_invalid(keyboard); 1065 sway_keyboard_group_remove_invalid(keyboard);
1030 if (!wlr_device->keyboard->group) { 1066 if (!keyboard->wlr->group) {
1031 sway_keyboard_group_add(keyboard); 1067 sway_keyboard_group_add(keyboard);
1032 } 1068 }
1033 } 1069 }
1034 1070
1035 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; 1071 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
1036 wlr_seat_set_keyboard(seat, wlr_device); 1072 wlr_seat_set_keyboard(seat, keyboard->wlr);
1037 1073
1038 wl_list_remove(&keyboard->keyboard_key.link); 1074 wl_list_remove(&keyboard->keyboard_key.link);
1039 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); 1075 wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
1040 keyboard->keyboard_key.notify = handle_keyboard_key; 1076 keyboard->keyboard_key.notify = handle_keyboard_key;
1041 1077
1042 wl_list_remove(&keyboard->keyboard_modifiers.link); 1078 wl_list_remove(&keyboard->keyboard_modifiers.link);
1043 wl_signal_add(&wlr_device->keyboard->events.modifiers, 1079 wl_signal_add(&keyboard->wlr->events.modifiers,
1044 &keyboard->keyboard_modifiers); 1080 &keyboard->keyboard_modifiers);
1045 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; 1081 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
1046 1082
@@ -1057,12 +1093,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
1057 if (!keyboard) { 1093 if (!keyboard) {
1058 return; 1094 return;
1059 } 1095 }
1060 if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { 1096 if (keyboard->wlr->group) {
1061 sway_keyboard_group_remove(keyboard); 1097 sway_keyboard_group_remove(keyboard);
1062 } 1098 }
1063 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 1099 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
1064 struct sway_input_device *device = keyboard->seat_device->input_device; 1100 if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {
1065 if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) {
1066 wlr_seat_set_keyboard(wlr_seat, NULL); 1101 wlr_seat_set_keyboard(wlr_seat, NULL);
1067 } 1102 }
1068 if (keyboard->keymap) { 1103 if (keyboard->keymap) {
diff --git a/sway/input/libinput.c b/sway/input/libinput.c
index 54520f9e..3c0f359d 100644
--- a/sway/input/libinput.c
+++ b/sway/input/libinput.c
@@ -1,5 +1,6 @@
1#include <float.h> 1#include <float.h>
2#include <libinput.h> 2#include <libinput.h>
3#include <libudev.h>
3#include <limits.h> 4#include <limits.h>
4#include <wlr/backend/libinput.h> 5#include <wlr/backend/libinput.h>
5#include "log.h" 6#include "log.h"
@@ -312,3 +313,32 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
312 ipc_event_input("libinput_config", input_device); 313 ipc_event_input("libinput_config", input_device);
313 } 314 }
314} 315}
316
317bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) {
318 if (!wlr_input_device_is_libinput(sway_device->wlr_device)) {
319 return false;
320 }
321
322 struct libinput_device *device =
323 wlr_libinput_get_device_handle(sway_device->wlr_device);
324 struct udev_device *udev_device =
325 libinput_device_get_udev_device(device);
326 if (!udev_device) {
327 return false;
328 }
329
330 const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH");
331 if (!id_path) {
332 return false;
333 }
334
335 const char prefix_platform[] = "platform-";
336 if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) {
337 return false;
338 }
339
340 const char prefix_pci[] = "pci-";
341 const char infix_platform[] = "-platform-";
342 return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) &&
343 strstr(id_path, infix_platform);
344}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1f5865ee..b21e1b86 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -11,6 +11,7 @@
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"
@@ -20,6 +21,7 @@
20#include "sway/input/cursor.h" 21#include "sway/input/cursor.h"
21#include "sway/input/input-manager.h" 22#include "sway/input/input-manager.h"
22#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
24#include "sway/input/libinput.h"
23#include "sway/input/seat.h" 25#include "sway/input/seat.h"
24#include "sway/input/switch.h" 26#include "sway/input/switch.h"
25#include "sway/input/tablet.h" 27#include "sway/input/tablet.h"
@@ -41,6 +43,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
41 sway_keyboard_destroy(seat_device->keyboard); 43 sway_keyboard_destroy(seat_device->keyboard);
42 sway_tablet_destroy(seat_device->tablet); 44 sway_tablet_destroy(seat_device->tablet);
43 sway_tablet_pad_destroy(seat_device->tablet_pad); 45 sway_tablet_pad_destroy(seat_device->tablet_pad);
46 sway_switch_destroy(seat_device->switch_device);
44 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, 47 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
45 seat_device->input_device->wlr_device); 48 seat_device->input_device->wlr_device);
46 wl_list_remove(&seat_device->link); 49 wl_list_remove(&seat_device->link);
@@ -50,6 +53,16 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
50static void seat_node_destroy(struct sway_seat_node *seat_node) { 53static void seat_node_destroy(struct sway_seat_node *seat_node) {
51 wl_list_remove(&seat_node->destroy.link); 54 wl_list_remove(&seat_node->destroy.link);
52 wl_list_remove(&seat_node->link); 55 wl_list_remove(&seat_node->link);
56
57 /*
58 * This is the only time we remove items from the focus stack without
59 * immediately re-adding them. If we just removed the last thing,
60 * mark that nothing has focus anymore.
61 */
62 if (wl_list_empty(&seat_node->seat->focus_stack)) {
63 seat_node->seat->has_focus = false;
64 }
65
53 free(seat_node); 66 free(seat_node);
54} 67}
55 68
@@ -129,7 +142,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
129 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 142 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
130 continue; 143 continue;
131 } 144 }
132 if (input_device->wlr_device->keyboard == wlr_keyboard) { 145 if (input_device->wlr_device == &wlr_keyboard->base) {
133 return seat_device->keyboard; 146 return seat_device->keyboard;
134 } 147 }
135 } 148 }
@@ -137,7 +150,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
137 wl_list_for_each(group, &seat->keyboard_groups, link) { 150 wl_list_for_each(group, &seat->keyboard_groups, link) {
138 struct sway_input_device *input_device = 151 struct sway_input_device *input_device =
139 group->seat_device->input_device; 152 group->seat_device->input_device;
140 if (input_device->wlr_device->keyboard == wlr_keyboard) { 153 if (input_device->wlr_device == &wlr_keyboard->base) {
141 return group->seat_device->keyboard; 154 return group->seat_device->keyboard;
142 } 155 }
143 } 156 }
@@ -199,6 +212,15 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
199 } 212 }
200} 213}
201 214
215void sway_force_focus(struct wlr_surface *surface) {
216 struct sway_seat *seat;
217 wl_list_for_each(seat, &server.input->seats, link) {
218 seat_keyboard_notify_enter(seat, surface);
219 seat_tablet_pads_notify_enter(seat, surface);
220 sway_input_method_relay_set_focus(&seat->im_relay, surface);
221 }
222}
223
202void seat_for_each_node(struct sway_seat *seat, 224void seat_for_each_node(struct sway_seat *seat,
203 void (*f)(struct sway_node *node, void *data), void *data) { 225 void (*f)(struct sway_node *node, void *data), void *data) {
204 struct sway_seat_node *current = NULL; 226 struct sway_seat_node *current = NULL;
@@ -209,14 +231,13 @@ void seat_for_each_node(struct sway_seat *seat,
209 231
210struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 232struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
211 struct sway_node *ancestor) { 233 struct sway_node *ancestor) {
212 if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { 234 if (node_is_view(ancestor)) {
213 return ancestor->sway_container; 235 return ancestor->sway_container;
214 } 236 }
215 struct sway_seat_node *current; 237 struct sway_seat_node *current;
216 wl_list_for_each(current, &seat->focus_stack, link) { 238 wl_list_for_each(current, &seat->focus_stack, link) {
217 struct sway_node *node = current->node; 239 struct sway_node *node = current->node;
218 if (node->type == N_CONTAINER && node->sway_container->view && 240 if (node_is_view(node) && node_has_ancestor(node, ancestor)) {
219 node_has_ancestor(node, ancestor)) {
220 return node->sway_container; 241 return node->sway_container;
221 } 242 }
222 } 243 }
@@ -235,7 +256,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
235 seat_node_destroy(seat_node); 256 seat_node_destroy(seat_node);
236 // If an unmanaged or layer surface is focused when an output gets 257 // If an unmanaged or layer surface is focused when an output gets
237 // disabled and an empty workspace on the output was focused by the 258 // disabled and an empty workspace on the output was focused by the
238 // seat, the seat needs to refocus it's focus inactive to update the 259 // seat, the seat needs to refocus its focus inactive to update the
239 // value of seat->workspace. 260 // value of seat->workspace.
240 if (seat->workspace == node->sway_workspace) { 261 if (seat->workspace == node->sway_workspace) {
241 struct sway_node *node = seat_get_focus_inactive(seat, &root->node); 262 struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
@@ -309,8 +330,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
309 // Setting focus_inactive 330 // Setting focus_inactive
310 focus = seat_get_focus_inactive(seat, &root->node); 331 focus = seat_get_focus_inactive(seat, &root->node);
311 seat_set_raw_focus(seat, next_focus); 332 seat_set_raw_focus(seat, next_focus);
312 if (focus->type == N_CONTAINER && focus->sway_container->workspace) { 333 if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) {
313 seat_set_raw_focus(seat, &focus->sway_container->workspace->node); 334 seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node);
314 } 335 }
315 seat_set_raw_focus(seat, focus); 336 seat_set_raw_focus(seat, focus);
316 } 337 }
@@ -368,8 +389,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
368 case WLR_DRAG_GRAB_KEYBOARD: 389 case WLR_DRAG_GRAB_KEYBOARD:
369 return; 390 return;
370 case WLR_DRAG_GRAB_KEYBOARD_POINTER: 391 case WLR_DRAG_GRAB_KEYBOARD_POINTER:
371 icon->x = cursor->x; 392 icon->x = cursor->x + wlr_icon->surface->sx;
372 icon->y = cursor->y; 393 icon->y = cursor->y + wlr_icon->surface->sy;
373 break; 394 break;
374 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; 395 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
375 struct wlr_touch_point *point = 396 struct wlr_touch_point *point =
@@ -377,8 +398,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
377 if (point == NULL) { 398 if (point == NULL) {
378 return; 399 return;
379 } 400 }
380 icon->x = seat->touch_x; 401 icon->x = seat->touch_x + wlr_icon->surface->sx;
381 icon->y = seat->touch_y; 402 icon->y = seat->touch_y + wlr_icon->surface->sy;
382 } 403 }
383 404
384 drag_icon_damage_whole(icon); 405 drag_icon_damage_whole(icon);
@@ -666,6 +687,40 @@ static void seat_reset_input_config(struct sway_seat *seat,
666 sway_device->input_device->wlr_device, NULL); 687 sway_device->input_device->wlr_device, NULL);
667} 688}
668 689
690static bool has_prefix(const char *str, const char *prefix) {
691 return strncmp(str, prefix, strlen(prefix)) == 0;
692}
693
694/**
695 * Get the name of the built-in output, if any. Returns NULL if there isn't
696 * exactly one built-in output.
697 */
698static const char *get_builtin_output_name(void) {
699 const char *match = NULL;
700 for (int i = 0; i < root->outputs->length; ++i) {
701 struct sway_output *output = root->outputs->items[i];
702 const char *name = output->wlr_output->name;
703 if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") ||
704 has_prefix(name, "DSI-")) {
705 if (match != NULL) {
706 return NULL;
707 }
708 match = name;
709 }
710 }
711 return match;
712}
713
714static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {
715 switch (seat_device->input_device->wlr_device->type) {
716 case WLR_INPUT_DEVICE_TOUCH:
717 case WLR_INPUT_DEVICE_TABLET_TOOL:
718 return true;
719 default:
720 return false;
721 }
722}
723
669static void seat_apply_input_config(struct sway_seat *seat, 724static void seat_apply_input_config(struct sway_seat *seat,
670 struct sway_seat_device *sway_device) { 725 struct sway_seat_device *sway_device) {
671 struct input_config *ic = 726 struct input_config *ic =
@@ -680,8 +735,33 @@ static void seat_apply_input_config(struct sway_seat *seat,
680 ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; 735 ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to;
681 736
682 switch (mapped_to) { 737 switch (mapped_to) {
683 case MAPPED_TO_DEFAULT: 738 case MAPPED_TO_DEFAULT:;
684 mapped_to_output = sway_device->input_device->wlr_device->output_name; 739 /*
740 * If the wlroots backend provides an output name, use that.
741 *
742 * Otherwise, try to map built-in touch and pointer devices to the
743 * built-in output.
744 */
745 struct wlr_input_device *dev = sway_device->input_device->wlr_device;
746 switch (dev->type) {
747 case WLR_INPUT_DEVICE_POINTER:
748 mapped_to_output = wlr_pointer_from_input_device(dev)->output_name;
749 break;
750 case WLR_INPUT_DEVICE_TOUCH:
751 mapped_to_output = wlr_touch_from_input_device(dev)->output_name;
752 break;
753 default:
754 mapped_to_output = NULL;
755 break;
756 }
757 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
758 sway_libinput_device_is_builtin(sway_device->input_device)) {
759 mapped_to_output = get_builtin_output_name();
760 if (mapped_to_output) {
761 sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'",
762 mapped_to_output, sway_device->input_device->identifier);
763 }
764 }
685 if (mapped_to_output == NULL) { 765 if (mapped_to_output == NULL) {
686 return; 766 return;
687 } 767 }
@@ -742,12 +822,14 @@ static void seat_configure_keyboard(struct sway_seat *seat,
742 } 822 }
743 sway_keyboard_configure(seat_device->keyboard); 823 sway_keyboard_configure(seat_device->keyboard);
744 wlr_seat_set_keyboard(seat->wlr_seat, 824 wlr_seat_set_keyboard(seat->wlr_seat,
745 seat_device->input_device->wlr_device); 825 wlr_keyboard_from_input_device(seat_device->input_device->wlr_device));
746 struct sway_node *focus = seat_get_focus(seat); 826
747 if (focus && node_is_view(focus)) { 827 // force notify reenter to pick up the new configuration. This reuses
748 // force notify reenter to pick up the new configuration 828 // the current focused surface to avoid breaking input grabs.
829 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
830 if (surface) {
749 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); 831 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
750 seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); 832 seat_keyboard_notify_enter(seat, surface);
751 } 833 }
752} 834}
753 835
@@ -999,7 +1081,8 @@ void seat_configure_xcursor(struct sway_seat *seat) {
999bool seat_is_input_allowed(struct sway_seat *seat, 1081bool seat_is_input_allowed(struct sway_seat *seat,
1000 struct wlr_surface *surface) { 1082 struct wlr_surface *surface) {
1001 struct wl_client *client = wl_resource_get_client(surface->resource); 1083 struct wl_client *client = wl_resource_get_client(surface->resource);
1002 return !seat->exclusive_client || seat->exclusive_client == client; 1084 return seat->exclusive_client == client ||
1085 (seat->exclusive_client == NULL && !server.session_lock.locked);
1003} 1086}
1004 1087
1005static void send_unfocus(struct sway_container *con, void *data) { 1088static void send_unfocus(struct sway_container *con, void *data) {
@@ -1086,29 +1169,23 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1086 } 1169 }
1087 1170
1088 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? 1171 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
1089 node->sway_workspace : node->sway_container->workspace; 1172 node->sway_workspace : node->sway_container->pending.workspace;
1090 struct sway_container *container = node->type == N_CONTAINER ? 1173 struct sway_container *container = node->type == N_CONTAINER ?
1091 node->sway_container : NULL; 1174 node->sway_container : NULL;
1092 1175
1093 // Deny setting focus to a view which is hidden by a fullscreen container 1176 // Deny setting focus to a view which is hidden by a fullscreen container or global
1094 if (new_workspace && new_workspace->fullscreen && container && 1177 if (container && container_obstructing_fullscreen_container(container)) {
1095 !container_is_fullscreen_or_child(container)) { 1178 return;
1096 // Unless it's a transient container
1097 if (!container_is_transient_for(container, new_workspace->fullscreen)) {
1098 return;
1099 }
1100 } 1179 }
1180
1101 // Deny setting focus to a workspace node when using fullscreen global 1181 // Deny setting focus to a workspace node when using fullscreen global
1102 if (root->fullscreen_global && !container && new_workspace) { 1182 if (root->fullscreen_global && !container && new_workspace) {
1103 return; 1183 return;
1104 } 1184 }
1105 // Deny setting focus to a view which is hidden by a fullscreen global 1185
1106 if (root->fullscreen_global && container != root->fullscreen_global && 1186 // Deny setting focus when an input grab or lockscreen is active
1107 !container_has_ancestor(container, root->fullscreen_global)) { 1187 if (container && container->view && !seat_is_input_allowed(seat, container->view->surface)) {
1108 // Unless it's a transient container 1188 return;
1109 if (!container_is_transient_for(container, root->fullscreen_global)) {
1110 return;
1111 }
1112 } 1189 }
1113 1190
1114 struct sway_output *new_output = 1191 struct sway_output *new_output =
@@ -1135,10 +1212,10 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1135 // Put the container parents on the focus stack, then the workspace, then 1212 // Put the container parents on the focus stack, then the workspace, then
1136 // the focused container. 1213 // the focused container.
1137 if (container) { 1214 if (container) {
1138 struct sway_container *parent = container->parent; 1215 struct sway_container *parent = container->pending.parent;
1139 while (parent) { 1216 while (parent) {
1140 seat_set_raw_focus(seat, &parent->node); 1217 seat_set_raw_focus(seat, &parent->node);
1141 parent = parent->parent; 1218 parent = parent->pending.parent;
1142 } 1219 }
1143 } 1220 }
1144 if (new_workspace) { 1221 if (new_workspace) {
@@ -1234,6 +1311,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1234 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); 1311 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
1235 } 1312 }
1236 1313
1314 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1237 seat_tablet_pads_notify_enter(seat, surface); 1315 seat_tablet_pads_notify_enter(seat, surface);
1238} 1316}
1239 1317
@@ -1326,7 +1404,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
1326 struct sway_node *node = current->node; 1404 struct sway_node *node = current->node;
1327 if (node->type == N_CONTAINER && 1405 if (node->type == N_CONTAINER &&
1328 !container_is_floating_or_child(node->sway_container) && 1406 !container_is_floating_or_child(node->sway_container) &&
1329 node->sway_container->workspace == workspace) { 1407 node->sway_container->pending.workspace == workspace) {
1330 return node->sway_container; 1408 return node->sway_container;
1331 } 1409 }
1332 } 1410 }
@@ -1343,7 +1421,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
1343 struct sway_node *node = current->node; 1421 struct sway_node *node = current->node;
1344 if (node->type == N_CONTAINER && 1422 if (node->type == N_CONTAINER &&
1345 container_is_floating_or_child(node->sway_container) && 1423 container_is_floating_or_child(node->sway_container) &&
1346 node->sway_container->workspace == workspace) { 1424 node->sway_container->pending.workspace == workspace) {
1347 return node->sway_container; 1425 return node->sway_container;
1348 } 1426 }
1349 } 1427 }
@@ -1377,9 +1455,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) {
1377 if (!seat->has_focus) { 1455 if (!seat->has_focus) {
1378 return NULL; 1456 return NULL;
1379 } 1457 }
1380 if (wl_list_empty(&seat->focus_stack)) { 1458 sway_assert(!wl_list_empty(&seat->focus_stack),
1381 return NULL; 1459 "focus_stack is empty, but has_focus is true");
1382 }
1383 struct sway_seat_node *current = 1460 struct sway_seat_node *current =
1384 wl_container_of(seat->focus_stack.next, current, link); 1461 wl_container_of(seat->focus_stack.next, current, link);
1385 return current->node; 1462 return current->node;
@@ -1391,7 +1468,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) {
1391 return NULL; 1468 return NULL;
1392 } 1469 }
1393 if (focus->type == N_CONTAINER) { 1470 if (focus->type == N_CONTAINER) {
1394 return focus->sway_container->workspace; 1471 return focus->sway_container->pending.workspace;
1395 } 1472 }
1396 if (focus->type == N_WORKSPACE) { 1473 if (focus->type == N_WORKSPACE) {
1397 return focus->sway_workspace; 1474 return focus->sway_workspace;
@@ -1404,8 +1481,8 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) {
1404 wl_list_for_each(current, &seat->focus_stack, link) { 1481 wl_list_for_each(current, &seat->focus_stack, link) {
1405 struct sway_node *node = current->node; 1482 struct sway_node *node = current->node;
1406 if (node->type == N_CONTAINER && 1483 if (node->type == N_CONTAINER &&
1407 node->sway_container->workspace) { 1484 node->sway_container->pending.workspace) {
1408 return node->sway_container->workspace; 1485 return node->sway_container->pending.workspace;
1409 } else if (node->type == N_WORKSPACE) { 1486 } else if (node->type == N_WORKSPACE) {
1410 return node->sway_workspace; 1487 return node->sway_workspace;
1411 } 1488 }
@@ -1514,7 +1591,7 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
1514} 1591}
1515 1592
1516void seatop_pointer_axis(struct sway_seat *seat, 1593void seatop_pointer_axis(struct sway_seat *seat,
1517 struct wlr_event_pointer_axis *event) { 1594 struct wlr_pointer_axis_event *event) {
1518 if (seat->seatop_impl->pointer_axis) { 1595 if (seat->seatop_impl->pointer_axis) {
1519 seat->seatop_impl->pointer_axis(seat, event); 1596 seat->seatop_impl->pointer_axis(seat, event);
1520 } 1597 }
@@ -1537,6 +1614,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
1537 } 1614 }
1538} 1615}
1539 1616
1617void seatop_hold_begin(struct sway_seat *seat,
1618 struct wlr_pointer_hold_begin_event *event) {
1619 if (seat->seatop_impl->hold_begin) {
1620 seat->seatop_impl->hold_begin(seat, event);
1621 }
1622}
1623
1624void seatop_hold_end(struct sway_seat *seat,
1625 struct wlr_pointer_hold_end_event *event) {
1626 if (seat->seatop_impl->hold_end) {
1627 seat->seatop_impl->hold_end(seat, event);
1628 }
1629}
1630
1631void seatop_pinch_begin(struct sway_seat *seat,
1632 struct wlr_pointer_pinch_begin_event *event) {
1633 if (seat->seatop_impl->pinch_begin) {
1634 seat->seatop_impl->pinch_begin(seat, event);
1635 }
1636}
1637
1638void seatop_pinch_update(struct sway_seat *seat,
1639 struct wlr_pointer_pinch_update_event *event) {
1640 if (seat->seatop_impl->pinch_update) {
1641 seat->seatop_impl->pinch_update(seat, event);
1642 }
1643}
1644
1645void seatop_pinch_end(struct sway_seat *seat,
1646 struct wlr_pointer_pinch_end_event *event) {
1647 if (seat->seatop_impl->pinch_end) {
1648 seat->seatop_impl->pinch_end(seat, event);
1649 }
1650}
1651
1652void seatop_swipe_begin(struct sway_seat *seat,
1653 struct wlr_pointer_swipe_begin_event *event) {
1654 if (seat->seatop_impl->swipe_begin) {
1655 seat->seatop_impl->swipe_begin(seat, event);
1656 }
1657}
1658
1659void seatop_swipe_update(struct sway_seat *seat,
1660 struct wlr_pointer_swipe_update_event *event) {
1661 if (seat->seatop_impl->swipe_update) {
1662 seat->seatop_impl->swipe_update(seat, event);
1663 }
1664}
1665
1666void seatop_swipe_end(struct sway_seat *seat,
1667 struct wlr_pointer_swipe_end_event *event) {
1668 if (seat->seatop_impl->swipe_end) {
1669 seat->seatop_impl->swipe_end(seat, event);
1670 }
1671}
1672
1540void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { 1673void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
1541 if (seat->seatop_impl->rebase) { 1674 if (seat->seatop_impl->rebase) {
1542 seat->seatop_impl->rebase(seat, time_msec); 1675 seat->seatop_impl->rebase(seat, time_msec);
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index a583ed62..875426bf 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -4,6 +4,8 @@
4#include <wlr/types/wlr_cursor.h> 4#include <wlr/types/wlr_cursor.h>
5#include <wlr/types/wlr_tablet_v2.h> 5#include <wlr/types/wlr_tablet_v2.h>
6#include <wlr/types/wlr_xcursor_manager.h> 6#include <wlr/types/wlr_xcursor_manager.h>
7#include "gesture.h"
8#include "sway/desktop/transaction.h"
7#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
8#include "sway/input/seat.h" 10#include "sway/input/seat.h"
9#include "sway/input/tablet.h" 11#include "sway/input/tablet.h"
@@ -19,6 +21,7 @@ struct seatop_default_event {
19 struct sway_node *previous_node; 21 struct sway_node *previous_node;
20 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 22 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
21 size_t pressed_button_count; 23 size_t pressed_button_count;
24 struct gesture_tracker gestures;
22}; 25};
23 26
24/*-----------------------------------------\ 27/*-----------------------------------------\
@@ -59,7 +62,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
59 return false; 62 return false;
60 } 63 }
61 } 64 }
62 cont = cont->parent; 65 cont = cont->pending.parent;
63 } 66 }
64 return true; 67 return true;
65} 68}
@@ -69,25 +72,25 @@ static enum wlr_edges find_edge(struct sway_container *cont,
69 if (!cont->view || (surface && cont->view->surface != surface)) { 72 if (!cont->view || (surface && cont->view->surface != surface)) {
70 return WLR_EDGE_NONE; 73 return WLR_EDGE_NONE;
71 } 74 }
72 if (cont->border == B_NONE || !cont->border_thickness || 75 if (cont->pending.border == B_NONE || !cont->pending.border_thickness ||
73 cont->border == B_CSD) { 76 cont->pending.border == B_CSD) {
74 return WLR_EDGE_NONE; 77 return WLR_EDGE_NONE;
75 } 78 }
76 if (cont->fullscreen_mode) { 79 if (cont->pending.fullscreen_mode) {
77 return WLR_EDGE_NONE; 80 return WLR_EDGE_NONE;
78 } 81 }
79 82
80 enum wlr_edges edge = 0; 83 enum wlr_edges edge = 0;
81 if (cursor->cursor->x < cont->x + cont->border_thickness) { 84 if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) {
82 edge |= WLR_EDGE_LEFT; 85 edge |= WLR_EDGE_LEFT;
83 } 86 }
84 if (cursor->cursor->y < cont->y + cont->border_thickness) { 87 if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) {
85 edge |= WLR_EDGE_TOP; 88 edge |= WLR_EDGE_TOP;
86 } 89 }
87 if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { 90 if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) {
88 edge |= WLR_EDGE_RIGHT; 91 edge |= WLR_EDGE_RIGHT;
89 } 92 }
90 if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { 93 if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) {
91 edge |= WLR_EDGE_BOTTOM; 94 edge |= WLR_EDGE_BOTTOM;
92 } 95 }
93 96
@@ -231,6 +234,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
231 wlr_layer_surface_v1_from_wlr_surface(surface); 234 wlr_layer_surface_v1_from_wlr_surface(surface);
232 if (layer->current.keyboard_interactive) { 235 if (layer->current.keyboard_interactive) {
233 seat_set_focus_layer(seat, layer); 236 seat_set_focus_layer(seat, layer);
237 transaction_commit_dirty();
234 } 238 }
235 } else if (cont) { 239 } else if (cont) {
236 bool is_floating_or_child = container_is_floating_or_child(cont); 240 bool is_floating_or_child = container_is_floating_or_child(cont);
@@ -249,7 +253,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
249 253
250 // Handle moving a tiling container 254 // Handle moving a tiling container
251 if (config->tiling_drag && mod_pressed && !is_floating_or_child && 255 if (config->tiling_drag && mod_pressed && !is_floating_or_child &&
252 cont->fullscreen_mode == FULLSCREEN_NONE) { 256 cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
253 seatop_begin_move_tiling(seat, cont); 257 seatop_begin_move_tiling(seat, cont);
254 return; 258 return;
255 } 259 }
@@ -268,6 +272,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
268 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 272 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
269 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 273 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
270 seat_set_focus_surface(seat, xsurface->surface, false); 274 seat_set_focus_surface(seat, xsurface->surface, false);
275 transaction_commit_dirty();
271 } 276 }
272 } 277 }
273#endif 278#endif
@@ -356,6 +361,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
356 if (node && node->type == N_WORKSPACE) { 361 if (node && node->type == N_WORKSPACE) {
357 if (state == WLR_BUTTON_PRESSED) { 362 if (state == WLR_BUTTON_PRESSED) {
358 seat_set_focus(seat, node); 363 seat_set_focus(seat, node);
364 transaction_commit_dirty();
359 } 365 }
360 seat_pointer_notify_button(seat, time_msec, button, state); 366 seat_pointer_notify_button(seat, time_msec, button, state);
361 return; 367 return;
@@ -367,6 +373,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
367 wlr_layer_surface_v1_from_wlr_surface(surface); 373 wlr_layer_surface_v1_from_wlr_surface(surface);
368 if (layer->current.keyboard_interactive) { 374 if (layer->current.keyboard_interactive) {
369 seat_set_focus_layer(seat, layer); 375 seat_set_focus_layer(seat, layer);
376 transaction_commit_dirty();
377 }
378 if (state == WLR_BUTTON_PRESSED) {
379 seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy);
370 } 380 }
371 seat_pointer_notify_button(seat, time_msec, button, state); 381 seat_pointer_notify_button(seat, time_msec, button, state);
372 return; 382 return;
@@ -381,7 +391,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
381 struct sway_container *cont_to_focus = cont; 391 struct sway_container *cont_to_focus = cont;
382 enum sway_container_layout layout = container_parent_layout(cont); 392 enum sway_container_layout layout = container_parent_layout(cont);
383 if (layout == L_TABBED || layout == L_STACKED) { 393 if (layout == L_TABBED || layout == L_STACKED) {
384 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); 394 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node);
385 } 395 }
386 396
387 seat_set_focus_container(seat, cont_to_focus); 397 seat_set_focus_container(seat, cont_to_focus);
@@ -397,9 +407,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
397 BTN_LEFT : BTN_RIGHT; 407 BTN_LEFT : BTN_RIGHT;
398 if (button == btn_resize) { 408 if (button == btn_resize) {
399 edge = 0; 409 edge = 0;
400 edge |= cursor->cursor->x > cont->x + cont->width / 2 ? 410 edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ?
401 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 411 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
402 edge |= cursor->cursor->y > cont->y + cont->height / 2 ? 412 edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ?
403 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 413 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
404 414
405 const char *image = NULL; 415 const char *image = NULL;
@@ -446,9 +456,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
446 if (mod_pressed && button == btn_resize) { 456 if (mod_pressed && button == btn_resize) {
447 struct sway_container *floater = container_toplevel_ancestor(cont); 457 struct sway_container *floater = container_toplevel_ancestor(cont);
448 edge = 0; 458 edge = 0;
449 edge |= cursor->cursor->x > floater->x + floater->width / 2 ? 459 edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ?
450 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 460 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
451 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 461 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
452 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 462 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
453 seatop_begin_resize_floating(seat, floater, edge); 463 seatop_begin_resize_floating(seat, floater, edge);
454 return; 464 return;
@@ -458,7 +468,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
458 // Handle moving a tiling container 468 // Handle moving a tiling container
459 if (config->tiling_drag && (mod_pressed || on_titlebar) && 469 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
460 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 470 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
461 cont && cont->fullscreen_mode == FULLSCREEN_NONE) { 471 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
462 struct sway_container *focus = seat_get_focused_container(seat); 472 struct sway_container *focus = seat_get_focused_container(seat);
463 bool focused = focus == cont || container_has_ancestor(focus, cont); 473 bool focused = focus == cont || container_has_ancestor(focus, cont);
464 if (on_titlebar && !focused) { 474 if (on_titlebar && !focused) {
@@ -466,7 +476,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
466 seat_set_focus(seat, node); 476 seat_set_focus(seat, node);
467 } 477 }
468 478
469 // If moving a container by it's title bar, use a threshold for the drag 479 // If moving a container by its title bar, use a threshold for the drag
470 if (!mod_pressed && config->tiling_drag_threshold > 0) { 480 if (!mod_pressed && config->tiling_drag_threshold > 0) {
471 seatop_begin_move_tiling_threshold(seat, cont); 481 seatop_begin_move_tiling_threshold(seat, cont);
472 } else { 482 } else {
@@ -487,6 +497,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
487 if (cont && state == WLR_BUTTON_PRESSED) { 497 if (cont && state == WLR_BUTTON_PRESSED) {
488 node = seat_get_focus_inactive(seat, &cont->node); 498 node = seat_get_focus_inactive(seat, &cont->node);
489 seat_set_focus(seat, node); 499 seat_set_focus(seat, node);
500 transaction_commit_dirty();
490 seat_pointer_notify_button(seat, time_msec, button, state); 501 seat_pointer_notify_button(seat, time_msec, button, state);
491 return; 502 return;
492 } 503 }
@@ -501,6 +512,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
501 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 512 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
502 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 513 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
503 seat_set_focus_surface(seat, xsurface->surface, false); 514 seat_set_focus_surface(seat, xsurface->surface, false);
515 transaction_commit_dirty();
504 seat_pointer_notify_button(seat, time_msec, button, state); 516 seat_pointer_notify_button(seat, time_msec, button, state);
505 return; 517 return;
506 } 518 }
@@ -530,6 +542,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
530 if (focus && hovered_output != node_get_output(focus)) { 542 if (focus && hovered_output != node_get_output(focus)) {
531 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 543 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
532 seat_set_focus(seat, &ws->node); 544 seat_set_focus(seat, &ws->node);
545 transaction_commit_dirty();
533 } 546 }
534 return; 547 return;
535 } 548 }
@@ -541,6 +554,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
541 struct sway_output *hovered_output = node_get_output(hovered_node); 554 struct sway_output *hovered_output = node_get_output(hovered_node);
542 if (hovered_output != focused_output) { 555 if (hovered_output != focused_output) {
543 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); 556 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node));
557 transaction_commit_dirty();
544 } 558 }
545 return; 559 return;
546 } 560 }
@@ -556,6 +570,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
556 if (hovered_node != e->previous_node || 570 if (hovered_node != e->previous_node ||
557 config->focus_follows_mouse == FOLLOWS_ALWAYS) { 571 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
558 seat_set_focus(seat, hovered_node); 572 seat_set_focus(seat, hovered_node);
573 transaction_commit_dirty();
559 } 574 }
560 } 575 }
561} 576}
@@ -632,7 +647,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
632 * Functions used by handle_pointer_axis / 647 * Functions used by handle_pointer_axis /
633 *--------------------------------------*/ 648 *--------------------------------------*/
634 649
635static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { 650static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
636 switch (event->orientation) { 651 switch (event->orientation) {
637 case WLR_AXIS_ORIENTATION_VERTICAL: 652 case WLR_AXIS_ORIENTATION_VERTICAL:
638 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 653 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
@@ -645,9 +660,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
645} 660}
646 661
647static void handle_pointer_axis(struct sway_seat *seat, 662static void handle_pointer_axis(struct sway_seat *seat,
648 struct wlr_event_pointer_axis *event) { 663 struct wlr_pointer_axis_event *event) {
649 struct sway_input_device *input_device = 664 struct sway_input_device *input_device =
650 event->device ? event->device->data : NULL; 665 event->pointer ? event->pointer->base.data : NULL;
651 struct input_config *ic = 666 struct input_config *ic =
652 input_device ? input_device_get_config(input_device) : NULL; 667 input_device ? input_device_get_config(input_device) : NULL;
653 struct sway_cursor *cursor = seat->cursor; 668 struct sway_cursor *cursor = seat->cursor;
@@ -664,7 +679,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
664 bool on_border = edge != WLR_EDGE_NONE; 679 bool on_border = edge != WLR_EDGE_NONE;
665 bool on_titlebar = cont && !on_border && !surface; 680 bool on_titlebar = cont && !on_border && !surface;
666 bool on_titlebar_border = cont && on_border && 681 bool on_titlebar_border = cont && on_border &&
667 cursor->cursor->y < cont->content_y; 682 cursor->cursor->y < cont->pending.content_y;
668 bool on_contents = cont && !on_border && surface; 683 bool on_contents = cont && !on_border && surface;
669 bool on_workspace = node && node->type == N_WORKSPACE; 684 bool on_workspace = node && node->type == N_WORKSPACE;
670 float scroll_factor = 685 float scroll_factor =
@@ -700,7 +715,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
700 seat_get_active_tiling_child(seat, tabcontainer); 715 seat_get_active_tiling_child(seat, tabcontainer);
701 list_t *siblings = container_get_siblings(cont); 716 list_t *siblings = container_get_siblings(cont);
702 int desired = list_find(siblings, active->sway_container) + 717 int desired = list_find(siblings, active->sway_container) +
703 round(scroll_factor * event->delta_discrete); 718 round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
704 if (desired < 0) { 719 if (desired < 0) {
705 desired = 0; 720 desired = 0;
706 } else if (desired >= siblings->length) { 721 } else if (desired >= siblings->length) {
@@ -714,6 +729,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
714 // Use the focused child of the tabbed/stacked container, not the 729 // Use the focused child of the tabbed/stacked container, not the
715 // container the user scrolled on. 730 // container the user scrolled on.
716 seat_set_focus(seat, new_focus); 731 seat_set_focus(seat, new_focus);
732 transaction_commit_dirty();
717 handled = true; 733 handled = true;
718 } 734 }
719 } 735 }
@@ -736,6 +752,304 @@ static void handle_pointer_axis(struct sway_seat *seat,
736 } 752 }
737} 753}
738 754
755/*------------------------------------\
756 * Functions used by gesture support /
757 *----------------------------------*/
758
759/**
760 * Check gesture binding for a specific gesture type and finger count.
761 * Returns true if binding is present, false otherwise
762 */
763static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
764 uint8_t fingers, struct sway_input_device *device) {
765 char *input =
766 device ? input_device_get_identifier(device->wlr_device) : strdup("*");
767
768 for (int i = 0; i < bindings->length; ++i) {
769 struct sway_gesture_binding *binding = bindings->items[i];
770
771 // Check type and finger count
772 if (!gesture_check(&binding->gesture, type, fingers)) {
773 continue;
774 }
775
776 // Check that input matches
777 if (strcmp(binding->input, "*") != 0 &&
778 strcmp(binding->input, input) != 0) {
779 continue;
780 }
781
782 free(input);
783
784 return true;
785 }
786
787 free(input);
788
789 return false;
790}
791
792/**
793 * Return the gesture binding which matches gesture type, finger count
794 * and direction, otherwise return null.
795 */
796static struct sway_gesture_binding* gesture_binding_match(
797 list_t *bindings, struct gesture *gesture, const char *input) {
798 struct sway_gesture_binding *current = NULL;
799
800 // Find best matching binding
801 for (int i = 0; i < bindings->length; ++i) {
802 struct sway_gesture_binding *binding = bindings->items[i];
803 bool exact = binding->flags & BINDING_EXACT;
804
805 // Check gesture matching
806 if (!gesture_match(&binding->gesture, gesture, exact)) {
807 continue;
808 }
809
810 // Check input matching
811 if (strcmp(binding->input, "*") != 0 &&
812 strcmp(binding->input, input) != 0) {
813 continue;
814 }
815
816 // If we already have a match ...
817 if (current) {
818 // ... check if input matching is equivalent
819 if (strcmp(current->input, binding->input) == 0) {
820
821 // ... - do not override an exact binding
822 if (!exact && current->flags & BINDING_EXACT) {
823 continue;
824 }
825
826 // ... - and ensure direction matching is better or equal
827 if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
828 continue;
829 }
830 } else if (strcmp(binding->input, "*") == 0) {
831 // ... do not accept worse input match
832 continue;
833 }
834 }
835
836 // Accept newer or better match
837 current = binding;
838
839 // If exact binding and input is found, quit search
840 if (strcmp(current->input, input) == 0 &&
841 gesture_compare(&current->gesture, gesture) == 0) {
842 break;
843 }
844 } // for all gesture bindings
845
846 return current;
847}
848
849// Wrapper around gesture_tracker_end to use tracker with sway bindings
850static struct sway_gesture_binding* gesture_tracker_end_and_match(
851 struct gesture_tracker *tracker, struct sway_input_device* device) {
852 // Determine name of input that received gesture
853 char *input = device
854 ? input_device_get_identifier(device->wlr_device)
855 : strdup("*");
856
857 // Match tracking result to binding
858 struct gesture *gesture = gesture_tracker_end(tracker);
859 struct sway_gesture_binding *binding = gesture_binding_match(
860 config->current_mode->gesture_bindings, gesture, input);
861 free(gesture);
862 free(input);
863
864 return binding;
865}
866
867// Small wrapper around seat_execute_command to work on gesture bindings
868static void gesture_binding_execute(struct sway_seat *seat,
869 struct sway_gesture_binding *binding) {
870 struct sway_binding *dummy_binding =
871 calloc(1, sizeof(struct sway_binding));
872 dummy_binding->type = BINDING_GESTURE;
873 dummy_binding->command = binding->command;
874
875 char *description = gesture_to_string(&binding->gesture);
876 sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
877 free(description);
878
879 seat_execute_command(seat, dummy_binding);
880
881 free(dummy_binding);
882}
883
884static void handle_hold_begin(struct sway_seat *seat,
885 struct wlr_pointer_hold_begin_event *event) {
886 // Start tracking gesture if there is a matching binding ...
887 struct sway_input_device *device =
888 event->pointer ? event->pointer->base.data : NULL;
889 list_t *bindings = config->current_mode->gesture_bindings;
890 if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
891 struct seatop_default_event *seatop = seat->seatop_data;
892 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
893 } else {
894 // ... otherwise forward to client
895 struct sway_cursor *cursor = seat->cursor;
896 wlr_pointer_gestures_v1_send_hold_begin(
897 cursor->pointer_gestures, cursor->seat->wlr_seat,
898 event->time_msec, event->fingers);
899 }
900}
901
902static void handle_hold_end(struct sway_seat *seat,
903 struct wlr_pointer_hold_end_event *event) {
904 // Ensure that gesture is being tracked and was not cancelled
905 struct seatop_default_event *seatop = seat->seatop_data;
906 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
907 struct sway_cursor *cursor = seat->cursor;
908 wlr_pointer_gestures_v1_send_hold_end(
909 cursor->pointer_gestures, cursor->seat->wlr_seat,
910 event->time_msec, event->cancelled);
911 return;
912 }
913 if (event->cancelled) {
914 gesture_tracker_cancel(&seatop->gestures);
915 return;
916 }
917
918 // End gesture tracking and execute matched binding
919 struct sway_input_device *device =
920 event->pointer ? event->pointer->base.data : NULL;
921 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
922 &seatop->gestures, device);
923
924 if (binding) {
925 gesture_binding_execute(seat, binding);
926 }
927}
928
929static void handle_pinch_begin(struct sway_seat *seat,
930 struct wlr_pointer_pinch_begin_event *event) {
931 // Start tracking gesture if there is a matching binding ...
932 struct sway_input_device *device =
933 event->pointer ? event->pointer->base.data : NULL;
934 list_t *bindings = config->current_mode->gesture_bindings;
935 if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
936 struct seatop_default_event *seatop = seat->seatop_data;
937 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
938 } else {
939 // ... otherwise forward to client
940 struct sway_cursor *cursor = seat->cursor;
941 wlr_pointer_gestures_v1_send_pinch_begin(
942 cursor->pointer_gestures, cursor->seat->wlr_seat,
943 event->time_msec, event->fingers);
944 }
945}
946
947static void handle_pinch_update(struct sway_seat *seat,
948 struct wlr_pointer_pinch_update_event *event) {
949 // Update any ongoing tracking ...
950 struct seatop_default_event *seatop = seat->seatop_data;
951 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
952 gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
953 event->scale, event->rotation);
954 } else {
955 // ... otherwise forward to client
956 struct sway_cursor *cursor = seat->cursor;
957 wlr_pointer_gestures_v1_send_pinch_update(
958 cursor->pointer_gestures,
959 cursor->seat->wlr_seat,
960 event->time_msec, event->dx, event->dy,
961 event->scale, event->rotation);
962 }
963}
964
965static void handle_pinch_end(struct sway_seat *seat,
966 struct wlr_pointer_pinch_end_event *event) {
967 // Ensure that gesture is being tracked and was not cancelled
968 struct seatop_default_event *seatop = seat->seatop_data;
969 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
970 struct sway_cursor *cursor = seat->cursor;
971 wlr_pointer_gestures_v1_send_pinch_end(
972 cursor->pointer_gestures, cursor->seat->wlr_seat,
973 event->time_msec, event->cancelled);
974 return;
975 }
976 if (event->cancelled) {
977 gesture_tracker_cancel(&seatop->gestures);
978 return;
979 }
980
981 // End gesture tracking and execute matched binding
982 struct sway_input_device *device =
983 event->pointer ? event->pointer->base.data : NULL;
984 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
985 &seatop->gestures, device);
986
987 if (binding) {
988 gesture_binding_execute(seat, binding);
989 }
990}
991
992static void handle_swipe_begin(struct sway_seat *seat,
993 struct wlr_pointer_swipe_begin_event *event) {
994 // Start tracking gesture if there is a matching binding ...
995 struct sway_input_device *device =
996 event->pointer ? event->pointer->base.data : NULL;
997 list_t *bindings = config->current_mode->gesture_bindings;
998 if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
999 struct seatop_default_event *seatop = seat->seatop_data;
1000 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
1001 } else {
1002 // ... otherwise forward to client
1003 struct sway_cursor *cursor = seat->cursor;
1004 wlr_pointer_gestures_v1_send_swipe_begin(
1005 cursor->pointer_gestures, cursor->seat->wlr_seat,
1006 event->time_msec, event->fingers);
1007 }
1008}
1009
1010static void handle_swipe_update(struct sway_seat *seat,
1011 struct wlr_pointer_swipe_update_event *event) {
1012
1013 // Update any ongoing tracking ...
1014 struct seatop_default_event *seatop = seat->seatop_data;
1015 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1016 gesture_tracker_update(&seatop->gestures,
1017 event->dx, event->dy, NAN, NAN);
1018 } else {
1019 // ... otherwise forward to client
1020 struct sway_cursor *cursor = seat->cursor;
1021 wlr_pointer_gestures_v1_send_swipe_update(
1022 cursor->pointer_gestures, cursor->seat->wlr_seat,
1023 event->time_msec, event->dx, event->dy);
1024 }
1025}
1026
1027static void handle_swipe_end(struct sway_seat *seat,
1028 struct wlr_pointer_swipe_end_event *event) {
1029 // Ensure gesture is being tracked and was not cancelled
1030 struct seatop_default_event *seatop = seat->seatop_data;
1031 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1032 struct sway_cursor *cursor = seat->cursor;
1033 wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
1034 cursor->seat->wlr_seat, event->time_msec, event->cancelled);
1035 return;
1036 }
1037 if (event->cancelled) {
1038 gesture_tracker_cancel(&seatop->gestures);
1039 return;
1040 }
1041
1042 // End gesture tracking and execute matched binding
1043 struct sway_input_device *device =
1044 event->pointer ? event->pointer->base.data : NULL;
1045 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1046 &seatop->gestures, device);
1047
1048 if (binding) {
1049 gesture_binding_execute(seat, binding);
1050 }
1051}
1052
739/*----------------------------------\ 1053/*----------------------------------\
740 * Functions used by handle_rebase / 1054 * Functions used by handle_rebase /
741 *--------------------------------*/ 1055 *--------------------------------*/
@@ -765,6 +1079,14 @@ static const struct sway_seatop_impl seatop_impl = {
765 .pointer_axis = handle_pointer_axis, 1079 .pointer_axis = handle_pointer_axis,
766 .tablet_tool_tip = handle_tablet_tool_tip, 1080 .tablet_tool_tip = handle_tablet_tool_tip,
767 .tablet_tool_motion = handle_tablet_tool_motion, 1081 .tablet_tool_motion = handle_tablet_tool_motion,
1082 .hold_begin = handle_hold_begin,
1083 .hold_end = handle_hold_end,
1084 .pinch_begin = handle_pinch_begin,
1085 .pinch_update = handle_pinch_update,
1086 .pinch_end = handle_pinch_end,
1087 .swipe_begin = handle_swipe_begin,
1088 .swipe_update = handle_swipe_update,
1089 .swipe_end = handle_swipe_end,
768 .rebase = handle_rebase, 1090 .rebase = handle_rebase,
769 .allow_set_cursor = true, 1091 .allow_set_cursor = true,
770}; 1092};
@@ -775,8 +1097,8 @@ void seatop_begin_default(struct sway_seat *seat) {
775 struct seatop_default_event *e = 1097 struct seatop_default_event *e =
776 calloc(1, sizeof(struct seatop_default_event)); 1098 calloc(1, sizeof(struct seatop_default_event));
777 sway_assert(e, "Unable to allocate seatop_default_event"); 1099 sway_assert(e, "Unable to allocate seatop_default_event");
1100
778 seat->seatop_impl = &seatop_impl; 1101 seat->seatop_impl = &seatop_impl;
779 seat->seatop_data = e; 1102 seat->seatop_data = e;
780
781 seatop_rebase(seat, 0); 1103 seatop_rebase(seat, 0);
782} 1104}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index 17f619e3..b40773d0 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -5,18 +5,22 @@
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/tree/view.h" 7#include "sway/tree/view.h"
8#include "sway/desktop/transaction.h"
8#include "log.h" 9#include "log.h"
9 10
10struct seatop_down_event { 11struct seatop_down_event {
11 struct sway_container *con; 12 struct sway_container *con;
13 struct sway_seat *seat;
14 struct wl_listener surface_destroy;
15 struct wlr_surface *surface;
12 double ref_lx, ref_ly; // cursor's x/y at start of op 16 double ref_lx, ref_ly; // cursor's x/y at start of op
13 double ref_con_lx, ref_con_ly; // container's x/y at start of op 17 double ref_con_lx, ref_con_ly; // container's x/y at start of op
14}; 18};
15 19
16static void handle_pointer_axis(struct sway_seat *seat, 20static void handle_pointer_axis(struct sway_seat *seat,
17 struct wlr_event_pointer_axis *event) { 21 struct wlr_pointer_axis_event *event) {
18 struct sway_input_device *input_device = 22 struct sway_input_device *input_device =
19 event->device ? event->device->data : NULL; 23 event->pointer ? event->pointer->base.data : NULL;
20 struct input_config *ic = 24 struct input_config *ic =
21 input_device ? input_device_get_config(input_device) : NULL; 25 input_device ? input_device_get_config(input_device) : NULL;
22 float scroll_factor = 26 float scroll_factor =
@@ -39,8 +43,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
39 43
40static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 44static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
41 struct seatop_down_event *e = seat->seatop_data; 45 struct seatop_down_event *e = seat->seatop_data;
42 struct sway_container *con = e->con; 46 if (seat_is_input_allowed(seat, e->surface)) {
43 if (seat_is_input_allowed(seat, con->view->surface)) {
44 double moved_x = seat->cursor->cursor->x - e->ref_lx; 47 double moved_x = seat->cursor->cursor->x - e->ref_lx;
45 double moved_y = seat->cursor->cursor->y - e->ref_ly; 48 double moved_y = seat->cursor->cursor->y - e->ref_ly;
46 double sx = e->ref_con_lx + moved_x; 49 double sx = e->ref_con_lx + moved_x;
@@ -61,8 +64,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
61static void handle_tablet_tool_motion(struct sway_seat *seat, 64static void handle_tablet_tool_motion(struct sway_seat *seat,
62 struct sway_tablet_tool *tool, uint32_t time_msec) { 65 struct sway_tablet_tool *tool, uint32_t time_msec) {
63 struct seatop_down_event *e = seat->seatop_data; 66 struct seatop_down_event *e = seat->seatop_data;
64 struct sway_container *con = e->con; 67 if (seat_is_input_allowed(seat, e->surface)) {
65 if (seat_is_input_allowed(seat, con->view->surface)) {
66 double moved_x = seat->cursor->cursor->x - e->ref_lx; 68 double moved_x = seat->cursor->cursor->x - e->ref_lx;
67 double moved_y = seat->cursor->cursor->y - e->ref_ly; 69 double moved_y = seat->cursor->cursor->y - e->ref_ly;
68 double sx = e->ref_con_lx + moved_x; 70 double sx = e->ref_con_lx + moved_x;
@@ -71,6 +73,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
71 } 73 }
72} 74}
73 75
76static void handle_destroy(struct wl_listener *listener, void *data) {
77 struct seatop_down_event *e =
78 wl_container_of(listener, e, surface_destroy);
79 if (e) {
80 seatop_begin_default(e->seat);
81 }
82}
83
74static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 84static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
75 struct seatop_down_event *e = seat->seatop_data; 85 struct seatop_down_event *e = seat->seatop_data;
76 if (e->con == con) { 86 if (e->con == con) {
@@ -78,6 +88,11 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
78 } 88 }
79} 89}
80 90
91static void handle_end(struct sway_seat *seat) {
92 struct seatop_down_event *e = seat->seatop_data;
93 wl_list_remove(&e->surface_destroy.link);
94}
95
81static const struct sway_seatop_impl seatop_impl = { 96static const struct sway_seatop_impl seatop_impl = {
82 .button = handle_button, 97 .button = handle_button,
83 .pointer_motion = handle_pointer_motion, 98 .pointer_motion = handle_pointer_motion,
@@ -85,11 +100,22 @@ static const struct sway_seatop_impl seatop_impl = {
85 .tablet_tool_tip = handle_tablet_tool_tip, 100 .tablet_tool_tip = handle_tablet_tool_tip,
86 .tablet_tool_motion = handle_tablet_tool_motion, 101 .tablet_tool_motion = handle_tablet_tool_motion,
87 .unref = handle_unref, 102 .unref = handle_unref,
103 .end = handle_end,
88 .allow_set_cursor = true, 104 .allow_set_cursor = true,
89}; 105};
90 106
91void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 107void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
92 uint32_t time_msec, int sx, int sy) { 108 uint32_t time_msec, double sx, double sy) {
109 seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy);
110 struct seatop_down_event *e = seat->seatop_data;
111 e->con = con;
112
113 container_raise_floating(con);
114 transaction_commit_dirty();
115}
116
117void seatop_begin_down_on_surface(struct sway_seat *seat,
118 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) {
93 seatop_end(seat); 119 seatop_end(seat);
94 120
95 struct seatop_down_event *e = 121 struct seatop_down_event *e =
@@ -97,7 +123,11 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
97 if (!e) { 123 if (!e) {
98 return; 124 return;
99 } 125 }
100 e->con = con; 126 e->con = NULL;
127 e->seat = seat;
128 e->surface = surface;
129 wl_signal_add(&e->surface->events.destroy, &e->surface_destroy);
130 e->surface_destroy.notify = handle_destroy;
101 e->ref_lx = seat->cursor->cursor->x; 131 e->ref_lx = seat->cursor->cursor->x;
102 e->ref_ly = seat->cursor->cursor->y; 132 e->ref_ly = seat->cursor->cursor->y;
103 e->ref_con_lx = sx; 133 e->ref_con_lx = sx;
@@ -105,6 +135,4 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
105 135
106 seat->seatop_impl = &seatop_impl; 136 seat->seatop_impl = &seatop_impl;
107 seat->seatop_data = e; 137 seat->seatop_data = e;
108
109 container_raise_floating(con);
110} 138}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
index 7f501fc9..ddcd4c53 100644
--- a/sway/input/seatop_move_floating.c
+++ b/sway/input/seatop_move_floating.c
@@ -1,6 +1,7 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h" 3#include "sway/desktop.h"
4#include "sway/desktop/transaction.h"
4#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
5#include "sway/input/seat.h" 6#include "sway/input/seat.h"
6 7
@@ -14,7 +15,8 @@ static void finalize_move(struct sway_seat *seat) {
14 15
15 // We "move" the container to its own location 16 // We "move" the container to its own location
16 // so it discovers its output again. 17 // so it discovers its output again.
17 container_floating_move_to(e->con, e->con->x, e->con->y); 18 container_floating_move_to(e->con, e->con->pending.x, e->con->pending.y);
19 transaction_commit_dirty();
18 20
19 seatop_begin_default(seat); 21 seatop_begin_default(seat);
20} 22}
@@ -40,6 +42,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
40 desktop_damage_whole_container(e->con); 42 desktop_damage_whole_container(e->con);
41 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); 43 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy);
42 desktop_damage_whole_container(e->con); 44 desktop_damage_whole_container(e->con);
45 transaction_commit_dirty();
43} 46}
44 47
45static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 48static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -67,13 +70,14 @@ void seatop_begin_move_floating(struct sway_seat *seat,
67 return; 70 return;
68 } 71 }
69 e->con = con; 72 e->con = con;
70 e->dx = cursor->cursor->x - con->x; 73 e->dx = cursor->cursor->x - con->pending.x;
71 e->dy = cursor->cursor->y - con->y; 74 e->dy = cursor->cursor->y - con->pending.y;
72 75
73 seat->seatop_impl = &seatop_impl; 76 seat->seatop_impl = &seatop_impl;
74 seat->seatop_data = e; 77 seat->seatop_data = e;
75 78
76 container_raise_floating(con); 79 container_raise_floating(con);
80 transaction_commit_dirty();
77 81
78 cursor_set_image(cursor, "grab", NULL); 82 cursor_set_image(cursor, "grab", NULL);
79 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 83 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 704e7270..223c6c08 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -3,6 +3,7 @@
3#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h> 4#include <wlr/util/edges.h>
5#include "sway/desktop.h" 5#include "sway/desktop.h"
6#include "sway/desktop/transaction.h"
6#include "sway/input/cursor.h" 7#include "sway/input/cursor.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"
@@ -15,6 +16,10 @@
15// Thickness of the dropzone when dragging to the edge of a layout container 16// Thickness of the dropzone when dragging to the edge of a layout container
16#define DROP_LAYOUT_BORDER 30 17#define DROP_LAYOUT_BORDER 30
17 18
19// Thickness of indicator when dropping onto a titlebar. This should be a
20// multiple of 2.
21#define DROP_SPLIT_INDICATOR 10
22
18struct seatop_move_tiling_event { 23struct seatop_move_tiling_event {
19 struct sway_container *con; 24 struct sway_container *con;
20 struct sway_node *target_node; 25 struct sway_node *target_node;
@@ -22,6 +27,8 @@ struct seatop_move_tiling_event {
22 struct wlr_box drop_box; 27 struct wlr_box drop_box;
23 double ref_lx, ref_ly; // cursor's x/y at start of op 28 double ref_lx, ref_ly; // cursor's x/y at start of op
24 bool threshold_reached; 29 bool threshold_reached;
30 bool split_target;
31 bool insert_after_target;
25}; 32};
26 33
27static void handle_render(struct sway_seat *seat, 34static void handle_render(struct sway_seat *seat,
@@ -91,8 +98,76 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge,
91 } 98 }
92} 99}
93 100
101static void split_border(double pos, int offset, int len, int n_children,
102 int avoid, int *out_pos, bool *out_after) {
103 int region = 2 * n_children * (pos - offset) / len;
104 // If the cursor is over the right side of a left-adjacent titlebar, or the
105 // left side of a right-adjacent titlebar, it's position when dropped will
106 // be the same. To avoid this, shift the region for adjacent containers.
107 if (avoid >= 0) {
108 if (region == 2 * avoid - 1 || region == 2 * avoid) {
109 region--;
110 } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) {
111 region++;
112 }
113 }
114
115 int child_index = (region + 1) / 2;
116 *out_after = region % 2;
117 // When dropping at the beginning or end of a container, show the drop
118 // region within the container boundary, otherwise show it on top of the
119 // border between two titlebars.
120 if (child_index == 0) {
121 *out_pos = offset;
122 } else if (child_index == n_children) {
123 *out_pos = offset + len - DROP_SPLIT_INDICATOR;
124 } else {
125 *out_pos = offset + child_index * len / n_children -
126 DROP_SPLIT_INDICATOR / 2;
127 }
128}
129
130static bool split_titlebar(struct sway_node *node, struct sway_container *avoid,
131 struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) {
132 struct sway_container *con = node->sway_container;
133 struct sway_node *parent = &con->pending.parent->node;
134 int title_height = container_titlebar_height();
135 struct wlr_box box;
136 int n_children, avoid_index;
137 enum sway_container_layout layout =
138 parent ? node_get_layout(parent) : L_NONE;
139 if (layout == L_TABBED || layout == L_STACKED) {
140 node_get_box(parent, &box);
141 n_children = node_get_children(parent)->length;
142 avoid_index = list_find(node_get_children(parent), avoid);
143 } else {
144 node_get_box(node, &box);
145 n_children = 1;
146 avoid_index = -1;
147 }
148 if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) {
149 // Drop into stacked titlebars.
150 title_box->width = box.width;
151 title_box->height = DROP_SPLIT_INDICATOR;
152 title_box->x = box.x;
153 split_border(cursor->y, box.y, title_height * n_children,
154 n_children, avoid_index, &title_box->y, after);
155 return true;
156 } else if (layout != L_STACKED && cursor->y < box.y + title_height) {
157 // Drop into side-by-side titlebars.
158 title_box->width = DROP_SPLIT_INDICATOR;
159 title_box->height = title_height;
160 title_box->y = box.y;
161 split_border(cursor->x, box.x, box.width, n_children,
162 avoid_index, &title_box->x, after);
163 return true;
164 }
165 return false;
166}
167
94static void handle_motion_postthreshold(struct sway_seat *seat) { 168static void handle_motion_postthreshold(struct sway_seat *seat) {
95 struct seatop_move_tiling_event *e = seat->seatop_data; 169 struct seatop_move_tiling_event *e = seat->seatop_data;
170 e->split_target = false;
96 struct wlr_surface *surface = NULL; 171 struct wlr_surface *surface = NULL;
97 double sx, sy; 172 double sx, sy;
98 struct sway_cursor *cursor = seat->cursor; 173 struct sway_cursor *cursor = seat->cursor;
@@ -119,34 +194,60 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
119 194
120 // Deny moving within own workspace if this is the only child 195 // Deny moving within own workspace if this is the only child
121 struct sway_container *con = node->sway_container; 196 struct sway_container *con = node->sway_container;
122 if (workspace_num_tiling_views(e->con->workspace) == 1 && 197 if (workspace_num_tiling_views(e->con->pending.workspace) == 1 &&
123 con->workspace == e->con->workspace) { 198 con->pending.workspace == e->con->pending.workspace) {
124 e->target_node = NULL; 199 e->target_node = NULL;
125 e->target_edge = WLR_EDGE_NONE; 200 e->target_edge = WLR_EDGE_NONE;
126 return; 201 return;
127 } 202 }
128 203
204 // Check if the cursor is over a tilebar only if the destination
205 // container is not a descendant of the source container.
206 if (!surface && !container_has_ancestor(con, e->con) &&
207 split_titlebar(node, e->con, cursor->cursor,
208 &e->drop_box, &e->insert_after_target)) {
209 // Don't allow dropping over the source container's titlebar
210 // to give users a chance to cancel a drag operation.
211 if (con == e->con) {
212 e->target_node = NULL;
213 } else {
214 e->target_node = node;
215 e->split_target = true;
216 }
217 e->target_edge = WLR_EDGE_NONE;
218 return;
219 }
220
129 // Traverse the ancestors, trying to find a layout container perpendicular 221 // Traverse the ancestors, trying to find a layout container perpendicular
130 // to the edge. Eg. close to the top or bottom of a horiz layout. 222 // to the edge. Eg. close to the top or bottom of a horiz layout.
223 int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER;
224 int thresh_bottom = con->pending.content_y +
225 con->pending.content_height - DROP_LAYOUT_BORDER;
226 int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER;
227 int thresh_right = con->pending.content_x +
228 con->pending.content_width - DROP_LAYOUT_BORDER;
131 while (con) { 229 while (con) {
132 enum wlr_edges edge = WLR_EDGE_NONE; 230 enum wlr_edges edge = WLR_EDGE_NONE;
133 enum sway_container_layout layout = container_parent_layout(con); 231 enum sway_container_layout layout = container_parent_layout(con);
134 struct wlr_box parent; 232 struct wlr_box box;
135 con->parent ? container_get_box(con->parent, &parent) : 233 node_get_box(node_get_parent(&con->node), &box);
136 workspace_get_box(con->workspace, &parent);
137 if (layout == L_HORIZ || layout == L_TABBED) { 234 if (layout == L_HORIZ || layout == L_TABBED) {
138 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { 235 if (cursor->cursor->y < thresh_top) {
139 edge = WLR_EDGE_TOP; 236 edge = WLR_EDGE_TOP;
140 } else if (cursor->cursor->y > parent.y + parent.height 237 box.height = thresh_top - box.y;
141 - DROP_LAYOUT_BORDER) { 238 } else if (cursor->cursor->y > thresh_bottom) {
142 edge = WLR_EDGE_BOTTOM; 239 edge = WLR_EDGE_BOTTOM;
240 box.height = box.y + box.height - thresh_bottom;
241 box.y = thresh_bottom;
143 } 242 }
144 } else if (layout == L_VERT || layout == L_STACKED) { 243 } else if (layout == L_VERT || layout == L_STACKED) {
145 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { 244 if (cursor->cursor->x < thresh_left) {
146 edge = WLR_EDGE_LEFT; 245 edge = WLR_EDGE_LEFT;
147 } else if (cursor->cursor->x > parent.x + parent.width 246 box.width = thresh_left - box.x;
148 - DROP_LAYOUT_BORDER) { 247 } else if (cursor->cursor->x > thresh_right) {
149 edge = WLR_EDGE_RIGHT; 248 edge = WLR_EDGE_RIGHT;
249 box.width = box.x + box.width - thresh_right;
250 box.x = thresh_right;
150 } 251 }
151 } 252 }
152 if (edge) { 253 if (edge) {
@@ -155,12 +256,11 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
155 e->target_node = node_get_parent(e->target_node); 256 e->target_node = node_get_parent(e->target_node);
156 } 257 }
157 e->target_edge = edge; 258 e->target_edge = edge;
158 node_get_box(e->target_node, &e->drop_box); 259 e->drop_box = box;
159 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
160 desktop_damage_box(&e->drop_box); 260 desktop_damage_box(&e->drop_box);
161 return; 261 return;
162 } 262 }
163 con = con->parent; 263 con = con->pending.parent;
164 } 264 }
165 265
166 // Use the hovered view - but we must be over the actual surface 266 // Use the hovered view - but we must be over the actual surface
@@ -173,23 +273,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
173 } 273 }
174 274
175 // Find the closest edge 275 // Find the closest edge
176 size_t thickness = fmin(con->content_width, con->content_height) * 0.3; 276 size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3;
177 size_t closest_dist = INT_MAX; 277 size_t closest_dist = INT_MAX;
178 size_t dist; 278 size_t dist;
179 e->target_edge = WLR_EDGE_NONE; 279 e->target_edge = WLR_EDGE_NONE;
180 if ((dist = cursor->cursor->y - con->y) < closest_dist) { 280 if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) {
181 closest_dist = dist; 281 closest_dist = dist;
182 e->target_edge = WLR_EDGE_TOP; 282 e->target_edge = WLR_EDGE_TOP;
183 } 283 }
184 if ((dist = cursor->cursor->x - con->x) < closest_dist) { 284 if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) {
185 closest_dist = dist; 285 closest_dist = dist;
186 e->target_edge = WLR_EDGE_LEFT; 286 e->target_edge = WLR_EDGE_LEFT;
187 } 287 }
188 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { 288 if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) {
189 closest_dist = dist; 289 closest_dist = dist;
190 e->target_edge = WLR_EDGE_RIGHT; 290 e->target_edge = WLR_EDGE_RIGHT;
191 } 291 }
192 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { 292 if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) {
193 closest_dist = dist; 293 closest_dist = dist;
194 e->target_edge = WLR_EDGE_BOTTOM; 294 e->target_edge = WLR_EDGE_BOTTOM;
195 } 295 }
@@ -199,10 +299,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
199 } 299 }
200 300
201 e->target_node = node; 301 e->target_node = node;
202 e->drop_box.x = con->content_x; 302 e->drop_box.x = con->pending.content_x;
203 e->drop_box.y = con->content_y; 303 e->drop_box.y = con->pending.content_y;
204 e->drop_box.width = con->content_width; 304 e->drop_box.width = con->pending.content_width;
205 e->drop_box.height = con->content_height; 305 e->drop_box.height = con->pending.content_height;
206 resize_box(&e->drop_box, e->target_edge, thickness); 306 resize_box(&e->drop_box, e->target_edge, thickness);
207 desktop_damage_box(&e->drop_box); 307 desktop_damage_box(&e->drop_box);
208} 308}
@@ -214,6 +314,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
214 } else { 314 } else {
215 handle_motion_prethreshold(seat); 315 handle_motion_prethreshold(seat);
216 } 316 }
317 transaction_commit_dirty();
217} 318}
218 319
219static bool is_parallel(enum sway_container_layout layout, 320static bool is_parallel(enum sway_container_layout layout,
@@ -232,14 +333,15 @@ static void finalize_move(struct sway_seat *seat) {
232 } 333 }
233 334
234 struct sway_container *con = e->con; 335 struct sway_container *con = e->con;
235 struct sway_container *old_parent = con->parent; 336 struct sway_container *old_parent = con->pending.parent;
236 struct sway_workspace *old_ws = con->workspace; 337 struct sway_workspace *old_ws = con->pending.workspace;
237 struct sway_node *target_node = e->target_node; 338 struct sway_node *target_node = e->target_node;
238 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? 339 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
239 target_node->sway_workspace : target_node->sway_container->workspace; 340 target_node->sway_workspace : target_node->sway_container->pending.workspace;
240 enum wlr_edges edge = e->target_edge; 341 enum wlr_edges edge = e->target_edge;
241 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; 342 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
242 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; 343 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER &&
344 !e->split_target;
243 345
244 if (!swap) { 346 if (!swap) {
245 container_detach(con); 347 container_detach(con);
@@ -248,6 +350,14 @@ static void finalize_move(struct sway_seat *seat) {
248 // Moving container into empty workspace 350 // Moving container into empty workspace
249 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { 351 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
250 con = workspace_add_tiling(new_ws, con); 352 con = workspace_add_tiling(new_ws, con);
353 } else if (e->split_target) {
354 struct sway_container *target = target_node->sway_container;
355 enum sway_container_layout layout = container_parent_layout(target);
356 if (layout != L_TABBED && layout != L_STACKED) {
357 container_split(target, L_TABBED);
358 }
359 container_add_sibling(target, con, e->insert_after_target);
360 ipc_event_window(con, "move");
251 } else if (target_node->type == N_CONTAINER) { 361 } else if (target_node->type == N_CONTAINER) {
252 // Moving container before/after another 362 // Moving container before/after another
253 struct sway_container *target = target_node->sway_container; 363 struct sway_container *target = target_node->sway_container;
@@ -283,8 +393,8 @@ static void finalize_move(struct sway_seat *seat) {
283 int index = list_find(siblings, con); 393 int index = list_find(siblings, con);
284 struct sway_container *sibling = index == 0 ? 394 struct sway_container *sibling = index == 0 ?
285 siblings->items[1] : siblings->items[index - 1]; 395 siblings->items[1] : siblings->items[index - 1];
286 con->width = sibling->width; 396 con->pending.width = sibling->pending.width;
287 con->height = sibling->height; 397 con->pending.height = sibling->pending.height;
288 con->width_fraction = sibling->width_fraction; 398 con->width_fraction = sibling->width_fraction;
289 con->height_fraction = sibling->height_fraction; 399 con->height_fraction = sibling->height_fraction;
290 } 400 }
@@ -294,6 +404,7 @@ static void finalize_move(struct sway_seat *seat) {
294 arrange_workspace(new_ws); 404 arrange_workspace(new_ws);
295 } 405 }
296 406
407 transaction_commit_dirty();
297 seatop_begin_default(seat); 408 seatop_begin_default(seat);
298} 409}
299 410
@@ -348,6 +459,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
348 seat->seatop_data = e; 459 seat->seatop_data = e;
349 460
350 container_raise_floating(con); 461 container_raise_floating(con);
462 transaction_commit_dirty();
351 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 463 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
352} 464}
353 465
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
index 5da22e47..df683026 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -2,6 +2,7 @@
2#include <limits.h> 2#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h> 4#include <wlr/types/wlr_xcursor_manager.h>
5#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 6#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 7#include "sway/input/seat.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
@@ -27,6 +28,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
27 if (seat->cursor->pressed_button_count == 0) { 28 if (seat->cursor->pressed_button_count == 0) {
28 container_set_resizing(con, false); 29 container_set_resizing(con, false);
29 arrange_container(con); // Send configure w/o resizing hint 30 arrange_container(con); // Send configure w/o resizing hint
31 transaction_commit_dirty();
30 seatop_begin_default(seat); 32 seatop_begin_default(seat);
31 } 33 }
32} 34}
@@ -78,17 +80,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
78 double height = e->ref_height + grow_height; 80 double height = e->ref_height + grow_height;
79 int min_width, max_width, min_height, max_height; 81 int min_width, max_width, min_height, max_height;
80 floating_calculate_constraints(&min_width, &max_width, 82 floating_calculate_constraints(&min_width, &max_width,
81 &min_height, &max_height); 83 &min_height, &max_height);
82 width = fmax(min_width + border_width, fmin(width, max_width)); 84 width = fmin(width, max_width - border_width);
83 height = fmax(min_height + border_height, fmin(height, max_height)); 85 width = fmax(width, min_width + border_width);
86 width = fmax(width, 1);
87 height = fmin(height, max_height - border_height);
88 height = fmax(height, min_height + border_height);
89 height = fmax(height, 1);
84 90
85 // Apply the view's min/max size 91 // Apply the view's min/max size
86 if (con->view) { 92 if (con->view) {
87 double view_min_width, view_max_width, view_min_height, view_max_height; 93 double view_min_width, view_max_width, view_min_height, view_max_height;
88 view_get_constraints(con->view, &view_min_width, &view_max_width, 94 view_get_constraints(con->view, &view_min_width, &view_max_width,
89 &view_min_height, &view_max_height); 95 &view_min_height, &view_max_height);
90 width = fmax(view_min_width + border_width, fmin(width, view_max_width)); 96 width = fmin(width, view_max_width - border_width);
91 height = fmax(view_min_height + border_height, fmin(height, view_max_height)); 97 width = fmax(width, view_min_width + border_width);
98 width = fmax(width, 1);
99 height = fmin(height, view_max_height - border_height);
100 height = fmax(height, view_min_height + border_height);
101 height = fmax(height, 1);
92 102
93 } 103 }
94 104
@@ -116,23 +126,24 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
116 126
117 // Determine the amounts we need to bump everything relative to the current 127 // Determine the amounts we need to bump everything relative to the current
118 // size. 128 // size.
119 int relative_grow_width = width - con->width; 129 int relative_grow_width = width - con->pending.width;
120 int relative_grow_height = height - con->height; 130 int relative_grow_height = height - con->pending.height;
121 int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; 131 int relative_grow_x = (e->ref_con_lx + grow_x) - con->pending.x;
122 int relative_grow_y = (e->ref_con_ly + grow_y) - con->y; 132 int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y;
123 133
124 // Actually resize stuff 134 // Actually resize stuff
125 con->x += relative_grow_x; 135 con->pending.x += relative_grow_x;
126 con->y += relative_grow_y; 136 con->pending.y += relative_grow_y;
127 con->width += relative_grow_width; 137 con->pending.width += relative_grow_width;
128 con->height += relative_grow_height; 138 con->pending.height += relative_grow_height;
129 139
130 con->content_x += relative_grow_x; 140 con->pending.content_x += relative_grow_x;
131 con->content_y += relative_grow_y; 141 con->pending.content_y += relative_grow_y;
132 con->content_width += relative_grow_width; 142 con->pending.content_width += relative_grow_width;
133 con->content_height += relative_grow_height; 143 con->pending.content_height += relative_grow_height;
134 144
135 arrange_container(con); 145 arrange_container(con);
146 transaction_commit_dirty();
136} 147}
137 148
138static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 149static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -166,16 +177,17 @@ void seatop_begin_resize_floating(struct sway_seat *seat,
166 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; 177 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
167 e->ref_lx = seat->cursor->cursor->x; 178 e->ref_lx = seat->cursor->cursor->x;
168 e->ref_ly = seat->cursor->cursor->y; 179 e->ref_ly = seat->cursor->cursor->y;
169 e->ref_con_lx = con->x; 180 e->ref_con_lx = con->pending.x;
170 e->ref_con_ly = con->y; 181 e->ref_con_ly = con->pending.y;
171 e->ref_width = con->width; 182 e->ref_width = con->pending.width;
172 e->ref_height = con->height; 183 e->ref_height = con->pending.height;
173 184
174 seat->seatop_impl = &seatop_impl; 185 seat->seatop_impl = &seatop_impl;
175 seat->seatop_data = e; 186 seat->seatop_data = e;
176 187
177 container_set_resizing(con, true); 188 container_set_resizing(con, true);
178 container_raise_floating(con); 189 container_raise_floating(con);
190 transaction_commit_dirty();
179 191
180 const char *image = edge == WLR_EDGE_NONE ? 192 const char *image = edge == WLR_EDGE_NONE ?
181 "se-resize" : wlr_xcursor_get_resize_name(edge); 193 "se-resize" : wlr_xcursor_get_resize_name(edge);
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
index 2cca805d..869d11b5 100644
--- a/sway/input/seatop_resize_tiling.c
+++ b/sway/input/seatop_resize_tiling.c
@@ -2,6 +2,7 @@
2#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
3#include <wlr/util/edges.h> 3#include <wlr/util/edges.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 6#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 7#include "sway/input/seat.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
@@ -52,21 +53,22 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
52 if (e->h_con) { 53 if (e->h_con) {
53 container_set_resizing(e->h_con, false); 54 container_set_resizing(e->h_con, false);
54 container_set_resizing(e->h_sib, false); 55 container_set_resizing(e->h_sib, false);
55 if (e->h_con->parent) { 56 if (e->h_con->pending.parent) {
56 arrange_container(e->h_con->parent); 57 arrange_container(e->h_con->pending.parent);
57 } else { 58 } else {
58 arrange_workspace(e->h_con->workspace); 59 arrange_workspace(e->h_con->pending.workspace);
59 } 60 }
60 } 61 }
61 if (e->v_con) { 62 if (e->v_con) {
62 container_set_resizing(e->v_con, false); 63 container_set_resizing(e->v_con, false);
63 container_set_resizing(e->v_sib, false); 64 container_set_resizing(e->v_sib, false);
64 if (e->v_con->parent) { 65 if (e->v_con->pending.parent) {
65 arrange_container(e->v_con->parent); 66 arrange_container(e->v_con->pending.parent);
66 } else { 67 } else {
67 arrange_workspace(e->v_con->workspace); 68 arrange_workspace(e->v_con->pending.workspace);
68 } 69 }
69 } 70 }
71 transaction_commit_dirty();
70 seatop_begin_default(seat); 72 seatop_begin_default(seat);
71 } 73 }
72} 74}
@@ -80,16 +82,16 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
80 82
81 if (e->h_con) { 83 if (e->h_con) {
82 if (e->edge & WLR_EDGE_LEFT) { 84 if (e->edge & WLR_EDGE_LEFT) {
83 amount_x = (e->h_con_orig_width - moved_x) - e->h_con->width; 85 amount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width;
84 } else if (e->edge & WLR_EDGE_RIGHT) { 86 } else if (e->edge & WLR_EDGE_RIGHT) {
85 amount_x = (e->h_con_orig_width + moved_x) - e->h_con->width; 87 amount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width;
86 } 88 }
87 } 89 }
88 if (e->v_con) { 90 if (e->v_con) {
89 if (e->edge & WLR_EDGE_TOP) { 91 if (e->edge & WLR_EDGE_TOP) {
90 amount_y = (e->v_con_orig_height - moved_y) - e->v_con->height; 92 amount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height;
91 } else if (e->edge & WLR_EDGE_BOTTOM) { 93 } else if (e->edge & WLR_EDGE_BOTTOM) {
92 amount_y = (e->v_con_orig_height + moved_y) - e->v_con->height; 94 amount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height;
93 } 95 }
94 } 96 }
95 97
@@ -99,6 +101,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
99 if (amount_y != 0) { 101 if (amount_y != 0) {
100 container_resize_tiled(e->v_con, e->edge_y, amount_y); 102 container_resize_tiled(e->v_con, e->edge_y, amount_y);
101 } 103 }
104 transaction_commit_dirty();
102} 105}
103 106
104static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 107static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -140,7 +143,7 @@ void seatop_begin_resize_tiling(struct sway_seat *seat,
140 if (e->h_con) { 143 if (e->h_con) {
141 container_set_resizing(e->h_con, true); 144 container_set_resizing(e->h_con, true);
142 container_set_resizing(e->h_sib, true); 145 container_set_resizing(e->h_sib, true);
143 e->h_con_orig_width = e->h_con->width; 146 e->h_con_orig_width = e->h_con->pending.width;
144 } 147 }
145 } 148 }
146 if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { 149 if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) {
@@ -151,12 +154,13 @@ void seatop_begin_resize_tiling(struct sway_seat *seat,
151 if (e->v_con) { 154 if (e->v_con) {
152 container_set_resizing(e->v_con, true); 155 container_set_resizing(e->v_con, true);
153 container_set_resizing(e->v_sib, true); 156 container_set_resizing(e->v_sib, true);
154 e->v_con_orig_height = e->v_con->height; 157 e->v_con_orig_height = e->v_con->pending.height;
155 } 158 }
156 } 159 }
157 160
158 seat->seatop_impl = &seatop_impl; 161 seat->seatop_impl = &seatop_impl;
159 seat->seatop_data = e; 162 seat->seatop_data = e;
160 163
164 transaction_commit_dirty();
161 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 165 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
162} 166}
diff --git a/sway/input/switch.c b/sway/input/switch.c
index b7c28df1..fc7dfaff 100644
--- a/sway/input/switch.c
+++ b/sway/input/switch.c
@@ -1,5 +1,4 @@
1#include "sway/config.h" 1#include "sway/config.h"
2#include "sway/desktop/transaction.h"
3#include "sway/input/switch.h" 2#include "sway/input/switch.h"
4#include <wlr/types/wlr_idle.h> 3#include <wlr/types/wlr_idle.h>
5#include "log.h" 4#include "log.h"
@@ -12,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
12 return NULL; 11 return NULL;
13 } 12 }
14 device->switch_device = switch_device; 13 device->switch_device = switch_device;
14 switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);
15 switch_device->seat_device = device; 15 switch_device->seat_device = device;
16 switch_device->state = WLR_SWITCH_STATE_OFF; 16 switch_device->state = WLR_SWITCH_STATE_OFF;
17 wl_list_init(&switch_device->switch_toggle.link); 17 wl_list_init(&switch_device->switch_toggle.link);
@@ -20,9 +20,23 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
20 return switch_device; 20 return switch_device;
21} 21}
22 22
23static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
24 enum wlr_switch_state state) {
25 switch (trigger) {
26 case SWAY_SWITCH_TRIGGER_ON:
27 return state == WLR_SWITCH_STATE_ON;
28 case SWAY_SWITCH_TRIGGER_OFF:
29 return state == WLR_SWITCH_STATE_OFF;
30 case SWAY_SWITCH_TRIGGER_TOGGLE:
31 return true;
32 }
33 abort(); // unreachable
34}
35
23static void execute_binding(struct sway_switch *sway_switch) { 36static void execute_binding(struct sway_switch *sway_switch) {
24 struct sway_seat* seat = sway_switch->seat_device->sway_seat; 37 struct sway_seat* seat = sway_switch->seat_device->sway_seat;
25 bool input_inhibited = seat->exclusive_client != NULL; 38 bool input_inhibited = seat->exclusive_client != NULL ||
39 server.session_lock.locked;
26 40
27 list_t *bindings = config->current_mode->switch_bindings; 41 list_t *bindings = config->current_mode->switch_bindings;
28 struct sway_switch_binding *matched_binding = NULL; 42 struct sway_switch_binding *matched_binding = NULL;
@@ -31,11 +45,10 @@ static void execute_binding(struct sway_switch *sway_switch) {
31 if (binding->type != sway_switch->type) { 45 if (binding->type != sway_switch->type) {
32 continue; 46 continue;
33 } 47 }
34 if (binding->state != WLR_SWITCH_STATE_TOGGLE && 48 if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {
35 binding->state != sway_switch->state) {
36 continue; 49 continue;
37 } 50 }
38 if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE 51 if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE
39 || (binding->flags & BINDING_RELOAD) == 0)) { 52 || (binding->flags & BINDING_RELOAD) == 0)) {
40 continue; 53 continue;
41 } 54 }
@@ -61,15 +74,12 @@ static void execute_binding(struct sway_switch *sway_switch) {
61 seat_execute_command(seat, dummy_binding); 74 seat_execute_command(seat, dummy_binding);
62 free(dummy_binding); 75 free(dummy_binding);
63 } 76 }
64
65 transaction_commit_dirty();
66
67} 77}
68 78
69static void handle_switch_toggle(struct wl_listener *listener, void *data) { 79static void handle_switch_toggle(struct wl_listener *listener, void *data) {
70 struct sway_switch *sway_switch = 80 struct sway_switch *sway_switch =
71 wl_container_of(listener, sway_switch, switch_toggle); 81 wl_container_of(listener, sway_switch, switch_toggle);
72 struct wlr_event_switch_toggle *event = data; 82 struct wlr_switch_toggle_event *event = data;
73 struct sway_seat *seat = sway_switch->seat_device->sway_seat; 83 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
74 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); 84 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);
75 85
@@ -86,10 +96,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) {
86} 96}
87 97
88void sway_switch_configure(struct sway_switch *sway_switch) { 98void sway_switch_configure(struct sway_switch *sway_switch) {
89 struct wlr_input_device *wlr_device =
90 sway_switch->seat_device->input_device->wlr_device;
91 wl_list_remove(&sway_switch->switch_toggle.link); 99 wl_list_remove(&sway_switch->switch_toggle.link);
92 wl_signal_add(&wlr_device->switch_device->events.toggle, 100 wl_signal_add(&sway_switch->wlr->events.toggle,
93 &sway_switch->switch_toggle); 101 &sway_switch->switch_toggle);
94 sway_switch->switch_toggle.notify = handle_switch_toggle; 102 sway_switch->switch_toggle.notify = handle_switch_toggle;
95 sway_log(SWAY_DEBUG, "Configured switch for device"); 103 sway_log(SWAY_DEBUG, "Configured switch for device");
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 26e86e36..92ede3fa 100644
--- a/sway/input/tablet.c
+++ b/sway/input/tablet.c
@@ -196,7 +196,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener,
196 196
197static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { 197static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
198 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); 198 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
199 struct wlr_event_tablet_pad_ring *event = data; 199 struct wlr_tablet_pad_ring_event *event = data;
200 200
201 if (!pad->current_surface) { 201 if (!pad->current_surface) {
202 return; 202 return;
@@ -210,7 +210,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
210 210
211static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { 211static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
212 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); 212 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
213 struct wlr_event_tablet_pad_strip *event = data; 213 struct wlr_tablet_pad_strip_event *event = data;
214 214
215 if (!pad->current_surface) { 215 if (!pad->current_surface) {
216 return; 216 return;
@@ -224,7 +224,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
224 224
225static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { 225static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
226 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); 226 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
227 struct wlr_event_tablet_pad_button *event = data; 227 struct wlr_tablet_pad_button_event *event = data;
228 228
229 if (!pad->current_surface) { 229 if (!pad->current_surface) {
230 return; 230 return;
@@ -246,6 +246,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
246 return NULL; 246 return NULL;
247 } 247 }
248 248
249 tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);
249 tablet_pad->seat_device = device; 250 tablet_pad->seat_device = device;
250 wl_list_init(&tablet_pad->attach.link); 251 wl_list_init(&tablet_pad->attach.link);
251 wl_list_init(&tablet_pad->button.link); 252 wl_list_init(&tablet_pad->button.link);
@@ -260,40 +261,40 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
260} 261}
261 262
262void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { 263void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
263 struct wlr_input_device *device = 264 struct wlr_input_device *wlr_device =
264 tablet_pad->seat_device->input_device->wlr_device; 265 tablet_pad->seat_device->input_device->wlr_device;
265 struct sway_seat *seat = tablet_pad->seat_device->sway_seat; 266 struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
266 267
267 if (!tablet_pad->tablet_v2_pad) { 268 if (!tablet_pad->tablet_v2_pad) {
268 tablet_pad->tablet_v2_pad = 269 tablet_pad->tablet_v2_pad =
269 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); 270 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);
270 } 271 }
271 272
272 wl_list_remove(&tablet_pad->attach.link); 273 wl_list_remove(&tablet_pad->attach.link);
273 tablet_pad->attach.notify = handle_tablet_pad_attach; 274 tablet_pad->attach.notify = handle_tablet_pad_attach;
274 wl_signal_add(&device->tablet_pad->events.attach_tablet, 275 wl_signal_add(&tablet_pad->wlr->events.attach_tablet,
275 &tablet_pad->attach); 276 &tablet_pad->attach);
276 277
277 wl_list_remove(&tablet_pad->button.link); 278 wl_list_remove(&tablet_pad->button.link);
278 tablet_pad->button.notify = handle_tablet_pad_button; 279 tablet_pad->button.notify = handle_tablet_pad_button;
279 wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); 280 wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);
280 281
281 wl_list_remove(&tablet_pad->strip.link); 282 wl_list_remove(&tablet_pad->strip.link);
282 tablet_pad->strip.notify = handle_tablet_pad_strip; 283 tablet_pad->strip.notify = handle_tablet_pad_strip;
283 wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); 284 wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);
284 285
285 wl_list_remove(&tablet_pad->ring.link); 286 wl_list_remove(&tablet_pad->ring.link);
286 tablet_pad->ring.notify = handle_tablet_pad_ring; 287 tablet_pad->ring.notify = handle_tablet_pad_ring;
287 wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); 288 wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
288 289
289 /* Search for a sibling tablet */ 290 /* Search for a sibling tablet */
290 if (!wlr_input_device_is_libinput(device)) { 291 if (!wlr_input_device_is_libinput(wlr_device)) {
291 /* We can only do this on libinput devices */ 292 /* We can only do this on libinput devices */
292 return; 293 return;
293 } 294 }
294 295
295 struct libinput_device_group *group = 296 struct libinput_device_group *group =
296 libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); 297 libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));
297 struct sway_tablet *tool; 298 struct sway_tablet *tool;
298 wl_list_for_each(tool, &seat->cursor->tablets, link) { 299 wl_list_for_each(tool, &seat->cursor->tablets, link) {
299 struct wlr_input_device *tablet = 300 struct wlr_input_device *tablet =
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index f83726ee..58911c2d 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -55,6 +55,35 @@ static void handle_im_commit(struct wl_listener *listener, void *data) {
55 wlr_text_input_v3_send_done(text_input->input); 55 wlr_text_input_v3_send_done(text_input->input);
56} 56}
57 57
58static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) {
59 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
60 input_method_keyboard_grab_destroy);
61 struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
62 wl_list_remove(&relay->input_method_keyboard_grab_destroy.link);
63
64 if (keyboard_grab->keyboard) {
65 // send modifier state to original client
66 wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat,
67 &keyboard_grab->keyboard->modifiers);
68 }
69}
70
71static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) {
72 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
73 input_method_grab_keyboard);
74 struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
75
76 // send modifier state to grab
77 struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);
78 wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,
79 active_keyboard);
80
81 wl_signal_add(&keyboard_grab->events.destroy,
82 &relay->input_method_keyboard_grab_destroy);
83 relay->input_method_keyboard_grab_destroy.notify =
84 handle_im_keyboard_grab_destroy;
85}
86
58static void text_input_set_pending_focused_surface( 87static void text_input_set_pending_focused_surface(
59 struct sway_text_input *text_input, struct wlr_surface *surface) { 88 struct sway_text_input *text_input, struct wlr_surface *surface) {
60 wl_list_remove(&text_input->pending_focused_surface_destroy.link); 89 wl_list_remove(&text_input->pending_focused_surface_destroy.link);
@@ -92,13 +121,18 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
92 return; 121 return;
93 } 122 }
94 // TODO: only send each of those if they were modified 123 // TODO: only send each of those if they were modified
95 wlr_input_method_v2_send_surrounding_text(input_method, 124 if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {
96 input->current.surrounding.text, input->current.surrounding.cursor, 125 wlr_input_method_v2_send_surrounding_text(input_method,
97 input->current.surrounding.anchor); 126 input->current.surrounding.text, input->current.surrounding.cursor,
127 input->current.surrounding.anchor);
128 }
98 wlr_input_method_v2_send_text_change_cause(input_method, 129 wlr_input_method_v2_send_text_change_cause(input_method,
99 input->current.text_change_cause); 130 input->current.text_change_cause);
100 wlr_input_method_v2_send_content_type(input_method, 131 if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) {
101 input->current.content_type.hint, input->current.content_type.purpose); 132 wlr_input_method_v2_send_content_type(input_method,
133 input->current.content_type.hint,
134 input->current.content_type.purpose);
135 }
102 wlr_input_method_v2_send_done(input_method); 136 wlr_input_method_v2_send_done(input_method);
103 // TODO: pass intent, display popup size 137 // TODO: pass intent, display popup size
104} 138}
@@ -144,6 +178,10 @@ static void handle_text_input_disable(struct wl_listener *listener,
144 void *data) { 178 void *data) {
145 struct sway_text_input *text_input = wl_container_of(listener, text_input, 179 struct sway_text_input *text_input = wl_container_of(listener, text_input,
146 text_input_disable); 180 text_input_disable);
181 if (text_input->input->focused_surface == NULL) {
182 sway_log(SWAY_DEBUG, "Disabling text input, but no longer focused");
183 return;
184 }
147 relay_disable_text_input(text_input->relay, text_input); 185 relay_disable_text_input(text_input->relay, text_input);
148} 186}
149 187
@@ -236,6 +274,9 @@ static void relay_handle_input_method(struct wl_listener *listener,
236 wl_signal_add(&relay->input_method->events.commit, 274 wl_signal_add(&relay->input_method->events.commit,
237 &relay->input_method_commit); 275 &relay->input_method_commit);
238 relay->input_method_commit.notify = handle_im_commit; 276 relay->input_method_commit.notify = handle_im_commit;
277 wl_signal_add(&relay->input_method->events.grab_keyboard,
278 &relay->input_method_grab_keyboard);
279 relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard;
239 wl_signal_add(&relay->input_method->events.destroy, 280 wl_signal_add(&relay->input_method->events.destroy,
240 &relay->input_method_destroy); 281 &relay->input_method_destroy);
241 relay->input_method_destroy.notify = handle_im_destroy; 282 relay->input_method_destroy.notify = handle_im_destroy;
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index fceee84d..e422b24d 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -1,7 +1,11 @@
1#include <ctype.h>
2#include <float.h>
1#include <json.h> 3#include <json.h>
2#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
3#include <stdio.h> 5#include <stdio.h>
4#include <ctype.h> 6#include <wlr/backend/libinput.h>
7#include <wlr/types/wlr_output.h>
8#include <xkbcommon/xkbcommon.h>
5#include "config.h" 9#include "config.h"
6#include "log.h" 10#include "log.h"
7#include "sway/config.h" 11#include "sway/config.h"
@@ -13,16 +17,26 @@
13#include "sway/input/input-manager.h" 17#include "sway/input/input-manager.h"
14#include "sway/input/cursor.h" 18#include "sway/input/cursor.h"
15#include "sway/input/seat.h" 19#include "sway/input/seat.h"
16#include <wlr/backend/libinput.h>
17#include <wlr/types/wlr_box.h>
18#include <wlr/types/wlr_output.h>
19#include <xkbcommon/xkbcommon.h>
20#include "wlr-layer-shell-unstable-v1-protocol.h" 20#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22 22
23static const int i3_output_id = INT32_MAX; 23static const int i3_output_id = INT32_MAX;
24static const int i3_scratch_id = INT32_MAX - 1; 24static const int i3_scratch_id = INT32_MAX - 1;
25 25
26static const char *ipc_json_node_type_description(enum sway_node_type node_type) {
27 switch (node_type) {
28 case N_ROOT:
29 return "root";
30 case N_OUTPUT:
31 return "output";
32 case N_WORKSPACE:
33 return "workspace";
34 case N_CONTAINER:
35 return "con";
36 }
37 return "none";
38}
39
26static const char *ipc_json_layout_description(enum sway_container_layout l) { 40static const char *ipc_json_layout_description(enum sway_container_layout l) {
27 switch (l) { 41 switch (l) {
28 case L_VERT: 42 case L_VERT:
@@ -189,16 +203,22 @@ static json_object *ipc_json_create_empty_rect(void) {
189 return ipc_json_create_rect(&empty); 203 return ipc_json_create_rect(&empty);
190} 204}
191 205
192static json_object *ipc_json_create_node(int id, char *name, 206static json_object *ipc_json_create_node(int id, const char* type, char *name,
193 bool focused, json_object *focus, struct wlr_box *box) { 207 bool focused, json_object *focus, struct wlr_box *box) {
194 json_object *object = json_object_new_object(); 208 json_object *object = json_object_new_object();
195 209
196 json_object_object_add(object, "id", json_object_new_int(id)); 210 json_object_object_add(object, "id", json_object_new_int(id));
197 json_object_object_add(object, "name", 211 json_object_object_add(object, "type", json_object_new_string(type));
198 name ? json_object_new_string(name) : NULL); 212 json_object_object_add(object, "orientation",
199 json_object_object_add(object, "rect", ipc_json_create_rect(box)); 213 json_object_new_string(
214 ipc_json_orientation_description(L_HORIZ)));
215 json_object_object_add(object, "percent", NULL);
216 json_object_object_add(object, "urgent", json_object_new_boolean(false));
217 json_object_object_add(object, "marks", json_object_new_array());
200 json_object_object_add(object, "focused", json_object_new_boolean(focused)); 218 json_object_object_add(object, "focused", json_object_new_boolean(focused));
201 json_object_object_add(object, "focus", focus); 219 json_object_object_add(object, "layout",
220 json_object_new_string(
221 ipc_json_layout_description(L_HORIZ)));
202 222
203 // set default values to be compatible with i3 223 // set default values to be compatible with i3
204 json_object_object_add(object, "border", 224 json_object_object_add(object, "border",
@@ -206,49 +226,63 @@ static json_object *ipc_json_create_node(int id, char *name,
206 ipc_json_border_description(B_NONE))); 226 ipc_json_border_description(B_NONE)));
207 json_object_object_add(object, "current_border_width", 227 json_object_object_add(object, "current_border_width",
208 json_object_new_int(0)); 228 json_object_new_int(0));
209 json_object_object_add(object, "layout", 229 json_object_object_add(object, "rect", ipc_json_create_rect(box));
210 json_object_new_string(
211 ipc_json_layout_description(L_HORIZ)));
212 json_object_object_add(object, "orientation",
213 json_object_new_string(
214 ipc_json_orientation_description(L_HORIZ)));
215 json_object_object_add(object, "percent", NULL);
216 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
217 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); 230 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
231 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
218 json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); 232 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
233 json_object_object_add(object, "name",
234 name ? json_object_new_string(name) : NULL);
219 json_object_object_add(object, "window", NULL); 235 json_object_object_add(object, "window", NULL);
220 json_object_object_add(object, "urgent", json_object_new_boolean(false));
221 json_object_object_add(object, "marks", json_object_new_array());
222 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
223 json_object_object_add(object, "nodes", json_object_new_array()); 236 json_object_object_add(object, "nodes", json_object_new_array());
224 json_object_object_add(object, "floating_nodes", json_object_new_array()); 237 json_object_object_add(object, "floating_nodes", json_object_new_array());
238 json_object_object_add(object, "focus", focus);
239 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
225 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 240 json_object_object_add(object, "sticky", json_object_new_boolean(false));
226 241
227 return object; 242 return object;
228} 243}
229 244
230static void ipc_json_describe_root(struct sway_root *root, json_object *object) { 245static void ipc_json_describe_output(struct sway_output *output,
231 json_object_object_add(object, "type", json_object_new_string("root")); 246 json_object *object) {
247 struct wlr_output *wlr_output = output->wlr_output;
248
249 json_object_object_add(object, "primary", json_object_new_boolean(false));
250 json_object_object_add(object, "make",
251 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
252 json_object_object_add(object, "model",
253 json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown"));
254 json_object_object_add(object, "serial",
255 json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown"));
256
257 json_object *modes_array = json_object_new_array();
258 struct wlr_output_mode *mode;
259 wl_list_for_each(mode, &wlr_output->modes, link) {
260 json_object *mode_object = json_object_new_object();
261 json_object_object_add(mode_object, "width",
262 json_object_new_int(mode->width));
263 json_object_object_add(mode_object, "height",
264 json_object_new_int(mode->height));
265 json_object_object_add(mode_object, "refresh",
266 json_object_new_int(mode->refresh));
267 json_object_array_add(modes_array, mode_object);
268 }
269 json_object_object_add(object, "modes", modes_array);
232} 270}
233 271
234static void ipc_json_describe_output(struct sway_output *output, 272static void ipc_json_describe_enabled_output(struct sway_output *output,
235 json_object *object) { 273 json_object *object) {
274 ipc_json_describe_output(output, object);
275
236 struct wlr_output *wlr_output = output->wlr_output; 276 struct wlr_output *wlr_output = output->wlr_output;
237 json_object_object_add(object, "type", json_object_new_string("output"));
238 json_object_object_add(object, "active", json_object_new_boolean(true)); 277 json_object_object_add(object, "active", json_object_new_boolean(true));
239 json_object_object_add(object, "dpms", 278 json_object_object_add(object, "dpms",
240 json_object_new_boolean(wlr_output->enabled)); 279 json_object_new_boolean(wlr_output->enabled));
241 json_object_object_add(object, "primary", json_object_new_boolean(false)); 280 json_object_object_add(object, "power",
281 json_object_new_boolean(wlr_output->enabled));
242 json_object_object_add(object, "layout", json_object_new_string("output")); 282 json_object_object_add(object, "layout", json_object_new_string("output"));
243 json_object_object_add(object, "orientation", 283 json_object_object_add(object, "orientation",
244 json_object_new_string( 284 json_object_new_string(
245 ipc_json_orientation_description(L_NONE))); 285 ipc_json_orientation_description(L_NONE)));
246 json_object_object_add(object, "make",
247 json_object_new_string(wlr_output->make));
248 json_object_object_add(object, "model",
249 json_object_new_string(wlr_output->model));
250 json_object_object_add(object, "serial",
251 json_object_new_string(wlr_output->serial));
252 json_object_object_add(object, "scale", 286 json_object_object_add(object, "scale",
253 json_object_new_double(wlr_output->scale)); 287 json_object_new_double(wlr_output->scale));
254 json_object_object_add(object, "scale_filter", 288 json_object_object_add(object, "scale_filter",
@@ -315,33 +349,14 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
315 349
316 json_object *object = json_object_new_object(); 350 json_object *object = json_object_new_object();
317 351
352 ipc_json_describe_output(output, object);
353
318 json_object_object_add(object, "type", json_object_new_string("output")); 354 json_object_object_add(object, "type", json_object_new_string("output"));
319 json_object_object_add(object, "name", 355 json_object_object_add(object, "name",
320 json_object_new_string(wlr_output->name)); 356 json_object_new_string(wlr_output->name));
321 json_object_object_add(object, "active", json_object_new_boolean(false)); 357 json_object_object_add(object, "active", json_object_new_boolean(false));
322 json_object_object_add(object, "dpms", json_object_new_boolean(false)); 358 json_object_object_add(object, "dpms", json_object_new_boolean(false));
323 json_object_object_add(object, "primary", json_object_new_boolean(false)); 359 json_object_object_add(object, "power", json_object_new_boolean(false));
324 json_object_object_add(object, "make",
325 json_object_new_string(wlr_output->make));
326 json_object_object_add(object, "model",
327 json_object_new_string(wlr_output->model));
328 json_object_object_add(object, "serial",
329 json_object_new_string(wlr_output->serial));
330
331 json_object *modes_array = json_object_new_array();
332 struct wlr_output_mode *mode;
333 wl_list_for_each(mode, &wlr_output->modes, link) {
334 json_object *mode_object = json_object_new_object();
335 json_object_object_add(mode_object, "width",
336 json_object_new_int(mode->width));
337 json_object_object_add(mode_object, "height",
338 json_object_new_int(mode->height));
339 json_object_object_add(mode_object, "refresh",
340 json_object_new_int(mode->refresh));
341 json_object_array_add(modes_array, mode_object);
342 }
343
344 json_object_object_add(object, "modes", modes_array);
345 360
346 json_object_object_add(object, "current_workspace", NULL); 361 json_object_object_add(object, "current_workspace", NULL);
347 362
@@ -369,11 +384,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
369 json_object_new_int(container->node.id)); 384 json_object_new_int(container->node.id));
370 } 385 }
371 386
372 json_object *workspace = ipc_json_create_node(i3_scratch_id, 387 json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace",
373 "__i3_scratch", false, workspace_focus, &box); 388 "__i3_scratch", false, workspace_focus, &box);
374 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); 389 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1));
375 json_object_object_add(workspace, "type",
376 json_object_new_string("workspace"));
377 390
378 // List all hidden scratchpad containers as floating nodes 391 // List all hidden scratchpad containers as floating nodes
379 json_object *floating_array = json_object_new_array(); 392 json_object *floating_array = json_object_new_array();
@@ -390,10 +403,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
390 json_object *output_focus = json_object_new_array(); 403 json_object *output_focus = json_object_new_array();
391 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); 404 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
392 405
393 json_object *output = ipc_json_create_node(i3_output_id, 406 json_object *output = ipc_json_create_node(i3_output_id, "output",
394 "__i3", false, output_focus, &box); 407 "__i3", false, output_focus, &box);
395 json_object_object_add(output, "type",
396 json_object_new_string("output"));
397 json_object_object_add(output, "layout", 408 json_object_object_add(output, "layout",
398 json_object_new_string("output")); 409 json_object_new_string("output"));
399 410
@@ -423,7 +434,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
423 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); 434 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1));
424 json_object_object_add(object, "output", workspace->output ? 435 json_object_object_add(object, "output", workspace->output ?
425 json_object_new_string(workspace->output->wlr_output->name) : NULL); 436 json_object_new_string(workspace->output->wlr_output->name) : NULL);
426 json_object_object_add(object, "type", json_object_new_string("workspace"));
427 json_object_object_add(object, "urgent", 437 json_object_object_add(object, "urgent",
428 json_object_new_boolean(workspace->urgent)); 438 json_object_new_boolean(workspace->urgent));
429 json_object_object_add(object, "representation", workspace->representation ? 439 json_object_object_add(object, "representation", workspace->representation ?
@@ -448,30 +458,32 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
448 458
449static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { 459static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
450 enum sway_container_layout parent_layout = container_parent_layout(c); 460 enum sway_container_layout parent_layout = container_parent_layout(c);
451 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; 461 list_t *siblings = container_get_siblings(c);
462 bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)
463 && ((siblings && siblings->length > 1) || !config->hide_lone_tab);
452 if (((!tab_or_stack || container_is_floating(c)) && 464 if (((!tab_or_stack || container_is_floating(c)) &&
453 c->current.border != B_NORMAL) || 465 c->current.border != B_NORMAL) ||
454 c->fullscreen_mode != FULLSCREEN_NONE || 466 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
455 c->workspace == NULL) { 467 c->pending.workspace == NULL) {
456 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; 468 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0;
457 return; 469 return;
458 } 470 }
459 471
460 if (c->parent) { 472 if (c->pending.parent) {
461 deco_rect->x = c->x - c->parent->x; 473 deco_rect->x = c->pending.x - c->pending.parent->pending.x;
462 deco_rect->y = c->y - c->parent->y; 474 deco_rect->y = c->pending.y - c->pending.parent->pending.y;
463 } else { 475 } else {
464 deco_rect->x = c->x - c->workspace->x; 476 deco_rect->x = c->pending.x - c->pending.workspace->x;
465 deco_rect->y = c->y - c->workspace->y; 477 deco_rect->y = c->pending.y - c->pending.workspace->y;
466 } 478 }
467 deco_rect->width = c->width; 479 deco_rect->width = c->pending.width;
468 deco_rect->height = container_titlebar_height(); 480 deco_rect->height = container_titlebar_height();
469 481
470 if (!container_is_floating(c)) { 482 if (!container_is_floating(c)) {
471 if (parent_layout == L_TABBED) { 483 if (parent_layout == L_TABBED) {
472 deco_rect->width = c->parent 484 deco_rect->width = c->pending.parent
473 ? c->parent->width / c->parent->children->length 485 ? c->pending.parent->pending.width / c->pending.parent->pending.children->length
474 : c->workspace->width / c->workspace->tiling->length; 486 : c->pending.workspace->width / c->pending.workspace->tiling->length;
475 deco_rect->x += deco_rect->width * container_sibling_index(c); 487 deco_rect->x += deco_rect->width * container_sibling_index(c);
476 } else if (parent_layout == L_STACKED) { 488 } else if (parent_layout == L_STACKED) {
477 if (!c->view) { 489 if (!c->view) {
@@ -494,10 +506,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
494 json_object_object_add(object, "visible", json_object_new_boolean(visible)); 506 json_object_object_add(object, "visible", json_object_new_boolean(visible));
495 507
496 struct wlr_box window_box = { 508 struct wlr_box window_box = {
497 c->content_x - c->x, 509 c->pending.content_x - c->pending.x,
498 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 510 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0,
499 c->content_width, 511 c->pending.content_width,
500 c->content_height 512 c->pending.content_height
501 }; 513 };
502 514
503 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); 515 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box));
@@ -583,16 +595,18 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
583static void ipc_json_describe_container(struct sway_container *c, json_object *object) { 595static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
584 json_object_object_add(object, "name", 596 json_object_object_add(object, "name",
585 c->title ? json_object_new_string(c->title) : NULL); 597 c->title ? json_object_new_string(c->title) : NULL);
586 json_object_object_add(object, "type", 598 if (container_is_floating(c)) {
587 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 599 json_object_object_add(object, "type",
600 json_object_new_string("floating_con"));
601 }
588 602
589 json_object_object_add(object, "layout", 603 json_object_object_add(object, "layout",
590 json_object_new_string( 604 json_object_new_string(
591 ipc_json_layout_description(c->layout))); 605 ipc_json_layout_description(c->pending.layout)));
592 606
593 json_object_object_add(object, "orientation", 607 json_object_object_add(object, "orientation",
594 json_object_new_string( 608 json_object_new_string(
595 ipc_json_orientation_description(c->layout))); 609 ipc_json_orientation_description(c->pending.layout)));
596 610
597 bool urgent = c->view ? 611 bool urgent = c->view ?
598 view_is_urgent(c->view) : container_has_urgent_child(c); 612 view_is_urgent(c->view) : container_has_urgent_child(c);
@@ -600,7 +614,7 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
600 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 614 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
601 615
602 json_object_object_add(object, "fullscreen_mode", 616 json_object_object_add(object, "fullscreen_mode",
603 json_object_new_int(c->fullscreen_mode)); 617 json_object_new_int(c->pending.fullscreen_mode));
604 618
605 struct sway_node *parent = node_get_parent(&c->node); 619 struct sway_node *parent = node_get_parent(&c->node);
606 struct wlr_box parent_box = {0, 0, 0, 0}; 620 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -610,8 +624,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
610 } 624 }
611 625
612 if (parent_box.width != 0 && parent_box.height != 0) { 626 if (parent_box.width != 0 && parent_box.height != 0) {
613 double percent = ((double)c->width / parent_box.width) 627 double percent = ((double)c->pending.width / parent_box.width)
614 * ((double)c->height / parent_box.height); 628 * ((double)c->pending.height / parent_box.height);
615 json_object_object_add(object, "percent", json_object_new_double(percent)); 629 json_object_object_add(object, "percent", json_object_new_double(percent));
616 } 630 }
617 631
@@ -692,15 +706,14 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
692 }; 706 };
693 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 707 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
694 708
695 json_object *object = ipc_json_create_node( 709 json_object *object = ipc_json_create_node((int)node->id,
696 (int)node->id, name, focused, focus, &box); 710 ipc_json_node_type_description(node->type), name, focused, focus, &box);
697 711
698 switch (node->type) { 712 switch (node->type) {
699 case N_ROOT: 713 case N_ROOT:
700 ipc_json_describe_root(root, object);
701 break; 714 break;
702 case N_OUTPUT: 715 case N_OUTPUT:
703 ipc_json_describe_output(node->sway_output, object); 716 ipc_json_describe_enabled_output(node->sway_output, object);
704 break; 717 break;
705 case N_CONTAINER: 718 case N_CONTAINER:
706 ipc_json_describe_container(node->sway_container, object); 719 ipc_json_describe_container(node->sway_container, object);
@@ -743,10 +756,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
743 } 756 }
744 break; 757 break;
745 case N_CONTAINER: 758 case N_CONTAINER:
746 if (node->sway_container->children) { 759 if (node->sway_container->pending.children) {
747 for (i = 0; i < node->sway_container->children->length; ++i) { 760 for (i = 0; i < node->sway_container->pending.children->length; ++i) {
748 struct sway_container *child = 761 struct sway_container *child =
749 node->sway_container->children->items[i]; 762 node->sway_container->pending.children->items[i];
750 json_object_array_add(children, 763 json_object_array_add(children,
751 ipc_json_describe_node_recursive(&child->node)); 764 ipc_json_describe_node_recursive(&child->node));
752 } 765 }
@@ -971,10 +984,16 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
971 input_device_get_type(device))); 984 input_device_get_type(device)));
972 985
973 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { 986 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
974 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 987 struct wlr_keyboard *keyboard =
988 wlr_keyboard_from_input_device(device->wlr_device);
975 struct xkb_keymap *keymap = keyboard->keymap; 989 struct xkb_keymap *keymap = keyboard->keymap;
976 struct xkb_state *state = keyboard->xkb_state; 990 struct xkb_state *state = keyboard->xkb_state;
977 991
992 json_object_object_add(object, "repeat_delay",
993 json_object_new_int(keyboard->repeat_info.delay));
994 json_object_object_add(object, "repeat_rate",
995 json_object_new_int(keyboard->repeat_info.rate));
996
978 json_object *layouts_arr = json_object_new_array(); 997 json_object *layouts_arr = json_object_new_array();
979 json_object_object_add(object, "xkb_layout_names", layouts_arr); 998 json_object_object_add(object, "xkb_layout_names", layouts_arr);
980 999
@@ -996,6 +1015,17 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
996 } 1015 }
997 } 1016 }
998 1017
1018 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1019 struct input_config *ic = input_device_get_config(device);
1020 float scroll_factor = 1.0f;
1021 if (ic != NULL && !isnan(ic->scroll_factor) &&
1022 ic->scroll_factor != FLT_MIN) {
1023 scroll_factor = ic->scroll_factor;
1024 }
1025 json_object_object_add(object, "scroll_factor",
1026 json_object_new_double(scroll_factor));
1027 }
1028
999 if (wlr_input_device_is_libinput(device->wlr_device)) { 1029 if (wlr_input_device_is_libinput(device->wlr_device)) {
1000 struct libinput_device *libinput_dev; 1030 struct libinput_device *libinput_dev;
1001 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1031 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
@@ -1109,7 +1139,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
1109 json_object_object_add(json, "verbose", 1139 json_object_object_add(json, "verbose",
1110 json_object_new_boolean(bar->verbose)); 1140 json_object_new_boolean(bar->verbose));
1111 json_object_object_add(json, "pango_markup", 1141 json_object_object_add(json, "pango_markup",
1112 json_object_new_boolean(bar->pango_markup)); 1142 json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT
1143 ? config->pango_markup
1144 : bar->pango_markup));
1113 1145
1114 json_object *colors = json_object_new_object(); 1146 json_object *colors = json_object_new_object();
1115 json_object_object_add(colors, "background", 1147 json_object_object_add(colors, "background",
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index aad9a7b5..3cbf7889 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -150,7 +150,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
150int ipc_handle_connection(int fd, uint32_t mask, void *data) { 150int ipc_handle_connection(int fd, uint32_t mask, void *data) {
151 (void) fd; 151 (void) fd;
152 struct sway_server *server = data; 152 struct sway_server *server = data;
153 sway_log(SWAY_DEBUG, "Event on IPC listening socket");
154 assert(mask == WL_EVENT_READABLE); 153 assert(mask == WL_EVENT_READABLE);
155 154
156 int client_fd = accept(ipc_socket, NULL, NULL); 155 int client_fd = accept(ipc_socket, NULL, NULL);
@@ -211,13 +210,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
211 } 210 }
212 211
213 if (mask & WL_EVENT_HANGUP) { 212 if (mask & WL_EVENT_HANGUP) {
214 sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
215 ipc_client_disconnect(client); 213 ipc_client_disconnect(client);
216 return 0; 214 return 0;
217 } 215 }
218 216
219 sway_log(SWAY_DEBUG, "Client %d readable", client->fd);
220
221 int read_available; 217 int read_available;
222 if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 218 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
223 sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); 219 sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size");
@@ -523,7 +519,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
523 } 519 }
524 520
525 if (mask & WL_EVENT_HANGUP) { 521 if (mask & WL_EVENT_HANGUP) {
526 sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
527 ipc_client_disconnect(client); 522 ipc_client_disconnect(client);
528 return 0; 523 return 0;
529 } 524 }
@@ -532,8 +527,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
532 return 0; 527 return 0;
533 } 528 }
534 529
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); 530 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
538 531
539 if (written == -1 && errno == EAGAIN) { 532 if (written == -1 && errno == EAGAIN) {
@@ -687,7 +680,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
687 } 680 }
688 struct sway_output *output; 681 struct sway_output *output;
689 wl_list_for_each(output, &root->all_outputs, link) { 682 wl_list_for_each(output, &root->all_outputs, link) {
690 if (!output->enabled && output != root->noop_output) { 683 if (!output->enabled && output != root->fallback_output) {
691 json_object_array_add(outputs, 684 json_object_array_add(outputs,
692 ipc_json_describe_disabled_output(output)); 685 ipc_json_describe_disabled_output(output));
693 } 686 }
@@ -955,7 +948,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ
955 ipc_client_handle_writable, client); 948 ipc_client_handle_writable, client);
956 } 949 }
957 950
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; 951 return true;
961} 952}
diff --git a/sway/lock.c b/sway/lock.c
new file mode 100644
index 00000000..04f80079
--- /dev/null
+++ b/sway/lock.c
@@ -0,0 +1,184 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include "log.h"
4#include "sway/input/keyboard.h"
5#include "sway/input/seat.h"
6#include "sway/output.h"
7#include "sway/server.h"
8
9struct sway_session_lock_surface {
10 struct wlr_session_lock_surface_v1 *lock_surface;
11 struct sway_output *output;
12 struct wlr_surface *surface;
13 struct wl_listener map;
14 struct wl_listener destroy;
15 struct wl_listener surface_commit;
16 struct wl_listener output_mode;
17 struct wl_listener output_commit;
18};
19
20static void handle_surface_map(struct wl_listener *listener, void *data) {
21 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map);
22 sway_force_focus(surf->surface);
23 output_damage_whole(surf->output);
24}
25
26static void handle_surface_commit(struct wl_listener *listener, void *data) {
27 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit);
28 output_damage_surface(surf->output, 0, 0, surf->surface, false);
29}
30
31static void handle_output_mode(struct wl_listener *listener, void *data) {
32 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode);
33 wlr_session_lock_surface_v1_configure(surf->lock_surface,
34 surf->output->width, surf->output->height);
35}
36
37static void handle_output_commit(struct wl_listener *listener, void *data) {
38 struct wlr_output_event_commit *event = data;
39 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit);
40 if (event->committed & (
41 WLR_OUTPUT_STATE_MODE |
42 WLR_OUTPUT_STATE_SCALE |
43 WLR_OUTPUT_STATE_TRANSFORM)) {
44 wlr_session_lock_surface_v1_configure(surf->lock_surface,
45 surf->output->width, surf->output->height);
46 }
47}
48
49static void handle_surface_destroy(struct wl_listener *listener, void *data) {
50 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy);
51 wl_list_remove(&surf->map.link);
52 wl_list_remove(&surf->destroy.link);
53 wl_list_remove(&surf->surface_commit.link);
54 wl_list_remove(&surf->output_mode.link);
55 wl_list_remove(&surf->output_commit.link);
56 output_damage_whole(surf->output);
57 free(surf);
58}
59
60static void handle_new_surface(struct wl_listener *listener, void *data) {
61 struct wlr_session_lock_surface_v1 *lock_surface = data;
62 struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf));
63 if (surf == NULL) {
64 return;
65 }
66
67 sway_log(SWAY_DEBUG, "new lock layer surface");
68
69 struct sway_output *output = lock_surface->output->data;
70 wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height);
71
72 surf->lock_surface = lock_surface;
73 surf->surface = lock_surface->surface;
74 surf->output = output;
75 surf->map.notify = handle_surface_map;
76 wl_signal_add(&lock_surface->events.map, &surf->map);
77 surf->destroy.notify = handle_surface_destroy;
78 wl_signal_add(&lock_surface->events.destroy, &surf->destroy);
79 surf->surface_commit.notify = handle_surface_commit;
80 wl_signal_add(&surf->surface->events.commit, &surf->surface_commit);
81 surf->output_mode.notify = handle_output_mode;
82 wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode);
83 surf->output_commit.notify = handle_output_commit;
84 wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit);
85}
86
87static void handle_unlock(struct wl_listener *listener, void *data) {
88 sway_log(SWAY_DEBUG, "session unlocked");
89 server.session_lock.locked = false;
90 server.session_lock.lock = NULL;
91
92 wl_list_remove(&server.session_lock.lock_new_surface.link);
93 wl_list_remove(&server.session_lock.lock_unlock.link);
94 wl_list_remove(&server.session_lock.lock_destroy.link);
95
96 struct sway_seat *seat;
97 wl_list_for_each(seat, &server.input->seats, link) {
98 seat_set_exclusive_client(seat, NULL);
99 // copied from seat_set_focus_layer -- deduplicate?
100 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
101 if (previous) {
102 // Hack to get seat to re-focus the return value of get_focus
103 seat_set_focus(seat, NULL);
104 seat_set_focus(seat, previous);
105 }
106 }
107
108 // redraw everything
109 for (int i = 0; i < root->outputs->length; ++i) {
110 struct sway_output *output = root->outputs->items[i];
111 output_damage_whole(output);
112 }
113}
114
115static void handle_abandon(struct wl_listener *listener, void *data) {
116 sway_log(SWAY_INFO, "session lock abandoned");
117 server.session_lock.lock = NULL;
118
119 wl_list_remove(&server.session_lock.lock_new_surface.link);
120 wl_list_remove(&server.session_lock.lock_unlock.link);
121 wl_list_remove(&server.session_lock.lock_destroy.link);
122
123 struct sway_seat *seat;
124 wl_list_for_each(seat, &server.input->seats, link) {
125 seat->exclusive_client = NULL;
126 }
127
128 // redraw everything
129 for (int i = 0; i < root->outputs->length; ++i) {
130 struct sway_output *output = root->outputs->items[i];
131 output_damage_whole(output);
132 }
133}
134
135static void handle_session_lock(struct wl_listener *listener, void *data) {
136 struct wlr_session_lock_v1 *lock = data;
137 struct wl_client *client = wl_resource_get_client(lock->resource);
138
139 if (server.session_lock.lock) {
140 wlr_session_lock_v1_destroy(lock);
141 return;
142 }
143
144 sway_log(SWAY_DEBUG, "session locked");
145 server.session_lock.locked = true;
146 server.session_lock.lock = lock;
147
148 struct sway_seat *seat;
149 wl_list_for_each(seat, &server.input->seats, link) {
150 seat_set_exclusive_client(seat, client);
151 }
152
153 wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface);
154 wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock);
155 wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy);
156
157 wlr_session_lock_v1_send_locked(lock);
158
159 // redraw everything
160 for (int i = 0; i < root->outputs->length; ++i) {
161 struct sway_output *output = root->outputs->items[i];
162 output_damage_whole(output);
163 }
164}
165
166static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
167 assert(server.session_lock.lock == NULL);
168 wl_list_remove(&server.session_lock.new_lock.link);
169 wl_list_remove(&server.session_lock.manager_destroy.link);
170}
171
172void sway_session_lock_init(void) {
173 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
174
175 server.session_lock.lock_new_surface.notify = handle_new_surface;
176 server.session_lock.lock_unlock.notify = handle_unlock;
177 server.session_lock.lock_destroy.notify = handle_abandon;
178 server.session_lock.new_lock.notify = handle_session_lock;
179 server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
180 wl_signal_add(&server.session_lock.manager->events.new_lock,
181 &server.session_lock.new_lock);
182 wl_signal_add(&server.session_lock.manager->events.destroy,
183 &server.session_lock.manager_destroy);
184}
diff --git a/sway/main.c b/sway/main.c
index 0c219fb3..85bc2f1c 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -6,12 +6,14 @@
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/resource.h>
9#include <sys/stat.h> 10#include <sys/stat.h>
10#include <sys/types.h> 11#include <sys/types.h>
11#include <sys/wait.h> 12#include <sys/wait.h>
12#include <sys/un.h> 13#include <sys/un.h>
13#include <unistd.h> 14#include <unistd.h>
14#include <wlr/util/log.h> 15#include <wlr/util/log.h>
16#include <wlr/version.h>
15#include "sway/commands.h" 17#include "sway/commands.h"
16#include "sway/config.h" 18#include "sway/config.h"
17#include "sway/server.h" 19#include "sway/server.h"
@@ -26,6 +28,7 @@
26 28
27static bool terminate_request = false; 29static bool terminate_request = false;
28static int exit_value = 0; 30static int exit_value = 0;
31static struct rlimit original_nofile_rlimit = {0};
29struct sway_server server = {0}; 32struct sway_server server = {0};
30struct sway_debug debug = {0}; 33struct sway_debug debug = {0};
31 34
@@ -46,43 +49,6 @@ void sig_handler(int signal) {
46 sway_terminate(EXIT_SUCCESS); 49 sway_terminate(EXIT_SUCCESS);
47} 50}
48 51
49void detect_raspi(void) {
50 bool raspi = false;
51 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
52 if (!f) {
53 return;
54 }
55 char *line = NULL;
56 size_t line_size = 0;
57 while (getline(&line, &line_size, f) != -1) {
58 if (strstr(line, "Raspberry Pi")) {
59 raspi = true;
60 break;
61 }
62 }
63 fclose(f);
64 FILE *g = fopen("/proc/modules", "r");
65 if (!g) {
66 free(line);
67 return;
68 }
69 bool vc4 = false;
70 while (getline(&line, &line_size, g) != -1) {
71 if (strstr(line, "vc4")) {
72 vc4 = true;
73 break;
74 }
75 }
76 free(line);
77 fclose(g);
78 if (!vc4 && raspi) {
79 fprintf(stderr, "\x1B[1;31mWarning: You have a "
80 "Raspberry Pi, but the vc4 Module is "
81 "not loaded! Set 'dtoverlay=vc4-kms-v3d'"
82 "in /boot/config.txt and reboot.\x1B[0m\n");
83 }
84}
85
86void detect_proprietary(int allow_unsupported_gpu) { 52void detect_proprietary(int allow_unsupported_gpu) {
87 FILE *f = fopen("/proc/modules", "r"); 53 FILE *f = fopen("/proc/modules", "r");
88 if (!f) { 54 if (!f) {
@@ -99,7 +65,7 @@ void detect_proprietary(int allow_unsupported_gpu) {
99 sway_log(SWAY_ERROR, 65 sway_log(SWAY_ERROR,
100 "Proprietary Nvidia drivers are NOT supported. " 66 "Proprietary Nvidia drivers are NOT supported. "
101 "Use Nouveau. To launch sway anyway, launch with " 67 "Use Nouveau. To launch sway anyway, launch with "
102 "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); 68 "--unsupported-gpu and DO NOT report issues.");
103 exit(EXIT_FAILURE); 69 exit(EXIT_FAILURE);
104 } 70 }
105 break; 71 break;
@@ -184,27 +150,47 @@ static void log_kernel(void) {
184 pclose(f); 150 pclose(f);
185} 151}
186 152
187 153static bool detect_suid(void) {
188static bool drop_permissions(void) { 154 if (geteuid() != 0 && getegid() != 0) {
189 if (getuid() != geteuid() || getgid() != getegid()) { 155 return false;
190 // Set the gid and uid in the correct order.
191 if (setgid(getgid()) != 0) {
192 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
193 return false;
194 }
195 if (setuid(getuid()) != 0) {
196 sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start");
197 return false;
198 }
199 } 156 }
200 if (setgid(0) != -1 || setuid(0) != -1) { 157
201 sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " 158 if (getuid() == geteuid() && getgid() == getegid()) {
202 "restore it after setuid), refusing to start");
203 return false; 159 return false;
204 } 160 }
161
162 sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. "
163 "This check will be removed in a future release.");
205 return true; 164 return true;
206} 165}
207 166
167static void increase_nofile_limit(void) {
168 if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
169 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
170 "getrlimit(NOFILE) failed");
171 return;
172 }
173
174 struct rlimit new_rlimit = original_nofile_rlimit;
175 new_rlimit.rlim_cur = new_rlimit.rlim_max;
176 if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {
177 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
178 "setrlimit(NOFILE) failed");
179 sway_log(SWAY_INFO, "Running with %d max open files",
180 (int)original_nofile_rlimit.rlim_cur);
181 }
182}
183
184void restore_nofile_limit(void) {
185 if (original_nofile_rlimit.rlim_cur == 0) {
186 return;
187 }
188 if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
189 sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: "
190 "setrlimit(NOFILE) failed");
191 }
192}
193
208void enable_debug_flag(const char *flag) { 194void enable_debug_flag(const char *flag) {
209 if (strcmp(flag, "damage=highlight") == 0) { 195 if (strcmp(flag, "damage=highlight") == 0) {
210 debug.damage = DAMAGE_HIGHLIGHT; 196 debug.damage = DAMAGE_HIGHLIGHT;
@@ -218,6 +204,8 @@ void enable_debug_flag(const char *flag) {
218 debug.txn_timings = true; 204 debug.txn_timings = true;
219 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 205 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
220 server.txn_timeout_ms = atoi(&flag[12]); 206 server.txn_timeout_ms = atoi(&flag[12]);
207 } else if (strcmp(flag, "noscanout") == 0) {
208 debug.noscanout = true;
221 } else { 209 } else {
222 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 210 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
223 } 211 }
@@ -242,36 +230,35 @@ static void handle_wlr_log(enum wlr_log_importance importance,
242 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); 230 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
243} 231}
244 232
233static const struct option long_options[] = {
234 {"help", no_argument, NULL, 'h'},
235 {"config", required_argument, NULL, 'c'},
236 {"validate", no_argument, NULL, 'C'},
237 {"debug", no_argument, NULL, 'd'},
238 {"version", no_argument, NULL, 'v'},
239 {"verbose", no_argument, NULL, 'V'},
240 {"get-socketpath", no_argument, NULL, 'p'},
241 {"unsupported-gpu", no_argument, NULL, 'u'},
242 {0, 0, 0, 0}
243};
244
245static const char usage[] =
246 "Usage: sway [options] [command]\n"
247 "\n"
248 " -h, --help Show help message and quit.\n"
249 " -c, --config <config> Specify a config file.\n"
250 " -C, --validate Check the validity of the config file, then exit.\n"
251 " -d, --debug Enables full logging, including debug information.\n"
252 " -v, --version Show the version number and quit.\n"
253 " -V, --verbose Enables more verbose logging.\n"
254 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
255 "\n";
256
245int main(int argc, char **argv) { 257int main(int argc, char **argv) {
246 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 258 static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false;
247
248 static struct option long_options[] = {
249 {"help", no_argument, NULL, 'h'},
250 {"config", required_argument, NULL, 'c'},
251 {"validate", no_argument, NULL, 'C'},
252 {"debug", no_argument, NULL, 'd'},
253 {"version", no_argument, NULL, 'v'},
254 {"verbose", no_argument, NULL, 'V'},
255 {"get-socketpath", no_argument, NULL, 'p'},
256 {"unsupported-gpu", no_argument, NULL, 'u'},
257 {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'},
258 {0, 0, 0, 0}
259 };
260 259
261 char *config_path = NULL; 260 char *config_path = NULL;
262 261
263 const char* usage =
264 "Usage: sway [options] [command]\n"
265 "\n"
266 " -h, --help Show help message and quit.\n"
267 " -c, --config <config> Specify a config file.\n"
268 " -C, --validate Check the validity of the config file, then exit.\n"
269 " -d, --debug Enables full logging, including debug information.\n"
270 " -v, --version Show the version number and quit.\n"
271 " -V, --verbose Enables more verbose logging.\n"
272 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
273 "\n";
274
275 int c; 262 int c;
276 while (1) { 263 while (1) {
277 int option_index = 0; 264 int option_index = 0;
@@ -289,25 +276,25 @@ int main(int argc, char **argv) {
289 config_path = strdup(optarg); 276 config_path = strdup(optarg);
290 break; 277 break;
291 case 'C': // validate 278 case 'C': // validate
292 validate = 1; 279 validate = true;
293 break; 280 break;
294 case 'd': // debug 281 case 'd': // debug
295 debug = 1; 282 debug = true;
296 break; 283 break;
297 case 'D': // extended debug options 284 case 'D': // extended debug options
298 enable_debug_flag(optarg); 285 enable_debug_flag(optarg);
299 break; 286 break;
300 case 'u': 287 case 'u':
301 allow_unsupported_gpu = 1; 288 allow_unsupported_gpu = true;
302 break; 289 break;
303 case 'v': // version 290 case 'v': // version
304 printf("sway version " SWAY_VERSION "\n"); 291 printf("sway version " SWAY_VERSION "\n");
305 exit(EXIT_SUCCESS); 292 exit(EXIT_SUCCESS);
306 break; 293 break;
307 case 'V': // verbose 294 case 'V': // verbose
308 verbose = 1; 295 verbose = true;
309 break; 296 break;
310 case 'p': ; // --get-socketpath 297 case 'p': // --get-socketpath
311 if (getenv("SWAYSOCK")) { 298 if (getenv("SWAYSOCK")) {
312 printf("%s\n", getenv("SWAYSOCK")); 299 printf("%s\n", getenv("SWAYSOCK"));
313 exit(EXIT_SUCCESS); 300 exit(EXIT_SUCCESS);
@@ -322,6 +309,11 @@ int main(int argc, char **argv) {
322 } 309 }
323 } 310 }
324 311
312 // SUID operation is deprecated, so block it for now.
313 if (detect_suid()) {
314 exit(EXIT_FAILURE);
315 }
316
325 // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the 317 // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the
326 // clear error message (when not running as an IPC client). 318 // clear error message (when not running as an IPC client).
327 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { 319 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) {
@@ -344,11 +336,10 @@ int main(int argc, char **argv) {
344 } 336 }
345 337
346 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); 338 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION);
339 sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR);
347 log_kernel(); 340 log_kernel();
348 log_distro(); 341 log_distro();
349 log_env(); 342 log_env();
350 detect_proprietary(allow_unsupported_gpu);
351 detect_raspi();
352 343
353 if (optind < argc) { // Behave as IPC client 344 if (optind < argc) { // Behave as IPC client
354 if (optind != 1) { 345 if (optind != 1) {
@@ -361,9 +352,6 @@ int main(int argc, char **argv) {
361 "`sway -d 2>sway.log`."); 352 "`sway -d 2>sway.log`.");
362 exit(EXIT_FAILURE); 353 exit(EXIT_FAILURE);
363 } 354 }
364 if (!drop_permissions()) {
365 exit(EXIT_FAILURE);
366 }
367 char *socket_path = getenv("SWAYSOCK"); 355 char *socket_path = getenv("SWAYSOCK");
368 if (!socket_path) { 356 if (!socket_path) {
369 sway_log(SWAY_ERROR, "Unable to retrieve socket path"); 357 sway_log(SWAY_ERROR, "Unable to retrieve socket path");
@@ -375,14 +363,8 @@ int main(int argc, char **argv) {
375 return 0; 363 return 0;
376 } 364 }
377 365
378 if (!server_privileged_prepare(&server)) { 366 detect_proprietary(allow_unsupported_gpu);
379 return 1; 367 increase_nofile_limit();
380 }
381
382 if (!drop_permissions()) {
383 server_fini(&server);
384 exit(EXIT_FAILURE);
385 }
386 368
387 // handle SIGTERM signals 369 // handle SIGTERM signals
388 signal(SIGTERM, sig_handler); 370 signal(SIGTERM, sig_handler);
@@ -413,6 +395,8 @@ int main(int argc, char **argv) {
413 goto shutdown; 395 goto shutdown;
414 } 396 }
415 397
398 set_rr_scheduling();
399
416 if (!server_start(&server)) { 400 if (!server_start(&server)) {
417 sway_terminate(EXIT_FAILURE); 401 sway_terminate(EXIT_FAILURE);
418 goto shutdown; 402 goto shutdown;
diff --git a/sway/meson.build b/sway/meson.build
index 6e138101..ced7419c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -5,9 +5,12 @@ 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',
9 'server.c', 11 'server.c',
10 'swaynag.c', 12 'swaynag.c',
13 'xdg_activation_v1.c',
11 'xdg_decoration.c', 14 'xdg_decoration.c',
12 15
13 'desktop/desktop.c', 16 'desktop/desktop.c',
@@ -64,6 +67,7 @@ sway_sources = files(
64 'commands/force_focus_wrapping.c', 67 'commands/force_focus_wrapping.c',
65 'commands/fullscreen.c', 68 'commands/fullscreen.c',
66 'commands/gaps.c', 69 'commands/gaps.c',
70 'commands/gesture.c',
67 'commands/hide_edge_borders.c', 71 'commands/hide_edge_borders.c',
68 'commands/inhibit_idle.c', 72 'commands/inhibit_idle.c',
69 'commands/kill.c', 73 'commands/kill.c',
@@ -187,6 +191,8 @@ sway_sources = files(
187 'commands/output/max_render_time.c', 191 'commands/output/max_render_time.c',
188 'commands/output/mode.c', 192 'commands/output/mode.c',
189 'commands/output/position.c', 193 'commands/output/position.c',
194 'commands/output/power.c',
195 'commands/output/render_bit_depth.c',
190 'commands/output/scale.c', 196 'commands/output/scale.c',
191 'commands/output/scale_filter.c', 197 'commands/output/scale_filter.c',
192 'commands/output/subpixel.c', 198 'commands/output/subpixel.c',
@@ -204,18 +210,22 @@ sway_sources = files(
204 210
205sway_deps = [ 211sway_deps = [
206 cairo, 212 cairo,
213 drm,
207 jsonc, 214 jsonc,
208 libevdev, 215 libevdev,
209 libinput, 216 libinput,
217 libudev,
210 math, 218 math,
211 pango, 219 pango,
212 pcre, 220 pcre2,
213 glesv2, 221 glesv2,
214 pixman, 222 pixman,
215 server_protos, 223 server_protos,
224 threads,
216 wayland_server, 225 wayland_server,
217 wlroots, 226 wlroots,
218 xkbcommon, 227 xkbcommon,
228 xcb_icccm,
219] 229]
220 230
221if have_xwayland 231if have_xwayland
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/server.c b/sway/server.c
index f51fcfe2..a87fc7cf 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -7,24 +7,33 @@
7#include <wlr/backend.h> 7#include <wlr/backend.h>
8#include <wlr/backend/headless.h> 8#include <wlr/backend/headless.h>
9#include <wlr/backend/multi.h> 9#include <wlr/backend/multi.h>
10#include <wlr/backend/noop.h>
11#include <wlr/backend/session.h> 10#include <wlr/backend/session.h>
11#include <wlr/config.h>
12#include <wlr/render/wlr_renderer.h> 12#include <wlr/render/wlr_renderer.h>
13#include <wlr/types/wlr_compositor.h> 13#include <wlr/types/wlr_compositor.h>
14#include <wlr/types/wlr_data_control_v1.h> 14#include <wlr/types/wlr_data_control_v1.h>
15#include <wlr/types/wlr_drm_lease_v1.h>
16#include <wlr/types/wlr_drm.h>
15#include <wlr/types/wlr_export_dmabuf_v1.h> 17#include <wlr/types/wlr_export_dmabuf_v1.h>
16#include <wlr/types/wlr_gamma_control_v1.h> 18#include <wlr/types/wlr_gamma_control_v1.h>
17#include <wlr/types/wlr_idle.h> 19#include <wlr/types/wlr_idle.h>
18#include <wlr/types/wlr_layer_shell_v1.h> 20#include <wlr/types/wlr_layer_shell_v1.h>
21#include <wlr/types/wlr_linux_dmabuf_v1.h>
19#include <wlr/types/wlr_pointer_constraints_v1.h> 22#include <wlr/types/wlr_pointer_constraints_v1.h>
20#include <wlr/types/wlr_primary_selection_v1.h> 23#include <wlr/types/wlr_primary_selection_v1.h>
21#include <wlr/types/wlr_relative_pointer_v1.h> 24#include <wlr/types/wlr_relative_pointer_v1.h>
22#include <wlr/types/wlr_screencopy_v1.h> 25#include <wlr/types/wlr_screencopy_v1.h>
26#include <wlr/types/wlr_single_pixel_buffer_v1.h>
23#include <wlr/types/wlr_server_decoration.h> 27#include <wlr/types/wlr_server_decoration.h>
28#include <wlr/types/wlr_subcompositor.h>
24#include <wlr/types/wlr_tablet_v2.h> 29#include <wlr/types/wlr_tablet_v2.h>
25#include <wlr/types/wlr_viewporter.h> 30#include <wlr/types/wlr_viewporter.h>
26#include <wlr/types/wlr_xcursor_manager.h> 31#include <wlr/types/wlr_xcursor_manager.h>
32#include <wlr/types/wlr_xdg_activation_v1.h>
27#include <wlr/types/wlr_xdg_decoration_v1.h> 33#include <wlr/types/wlr_xdg_decoration_v1.h>
34#include <wlr/types/wlr_xdg_foreign_registry.h>
35#include <wlr/types/wlr_xdg_foreign_v1.h>
36#include <wlr/types/wlr_xdg_foreign_v2.h>
28#include <wlr/types/wlr_xdg_output_v1.h> 37#include <wlr/types/wlr_xdg_output_v1.h>
29#include "config.h" 38#include "config.h"
30#include "list.h" 39#include "list.h"
@@ -39,8 +48,22 @@
39#include "sway/xwayland.h" 48#include "sway/xwayland.h"
40#endif 49#endif
41 50
42bool server_privileged_prepare(struct sway_server *server) { 51static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
43 sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); 52 /* We only offer non-desktop outputs, but in the future we might want to do
53 * more logic here. */
54
55 struct wlr_drm_lease_request_v1 *req = data;
56 struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
57 if (!lease) {
58 sway_log(SWAY_ERROR, "Failed to grant lease request");
59 wlr_drm_lease_request_v1_reject(req);
60 }
61}
62
63#define SWAY_XDG_SHELL_VERSION 2
64
65bool server_init(struct sway_server *server) {
66 sway_log(SWAY_DEBUG, "Initializing Wayland server");
44 server->wl_display = wl_display_create(); 67 server->wl_display = wl_display_create();
45 server->wl_event_loop = wl_display_get_event_loop(server->wl_display); 68 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
46 server->backend = wlr_backend_autocreate(server->wl_display); 69 server->backend = wlr_backend_autocreate(server->wl_display);
@@ -49,22 +72,36 @@ bool server_privileged_prepare(struct sway_server *server) {
49 sway_log(SWAY_ERROR, "Unable to create backend"); 72 sway_log(SWAY_ERROR, "Unable to create backend");
50 return false; 73 return false;
51 } 74 }
52 return true;
53}
54 75
55bool server_init(struct sway_server *server) { 76 server->renderer = wlr_renderer_autocreate(server->backend);
56 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 77 if (!server->renderer) {
78 sway_log(SWAY_ERROR, "Failed to create renderer");
79 return false;
80 }
81
82 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
57 83
58 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); 84 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) {
59 assert(renderer); 85 wlr_drm_create(server->wl_display, server->renderer);
86 server->linux_dmabuf_v1 =
87 wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer);
88 }
60 89
61 wlr_renderer_init_wl_display(renderer, server->wl_display); 90 server->allocator = wlr_allocator_autocreate(server->backend,
91 server->renderer);
92 if (!server->allocator) {
93 sway_log(SWAY_ERROR, "Failed to create allocator");
94 return false;
95 }
62 96
63 server->compositor = wlr_compositor_create(server->wl_display, renderer); 97 server->compositor = wlr_compositor_create(server->wl_display,
98 server->renderer);
64 server->compositor_new_surface.notify = handle_compositor_new_surface; 99 server->compositor_new_surface.notify = handle_compositor_new_surface;
65 wl_signal_add(&server->compositor->events.new_surface, 100 wl_signal_add(&server->compositor->events.new_surface,
66 &server->compositor_new_surface); 101 &server->compositor_new_surface);
67 102
103 wlr_subcompositor_create(server->wl_display);
104
68 server->data_device_manager = 105 server->data_device_manager =
69 wlr_data_device_manager_create(server->wl_display); 106 wlr_data_device_manager_create(server->wl_display);
70 107
@@ -87,7 +124,8 @@ bool server_init(struct sway_server *server) {
87 &server->layer_shell_surface); 124 &server->layer_shell_surface);
88 server->layer_shell_surface.notify = handle_layer_shell_surface; 125 server->layer_shell_surface.notify = handle_layer_shell_surface;
89 126
90 server->xdg_shell = wlr_xdg_shell_create(server->wl_display); 127 server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
128 SWAY_XDG_SHELL_VERSION);
91 wl_signal_add(&server->xdg_shell->events.new_surface, 129 wl_signal_add(&server->xdg_shell->events.new_surface,
92 &server->xdg_shell_surface); 130 &server->xdg_shell_surface);
93 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 131 server->xdg_shell_surface.notify = handle_xdg_shell_surface;
@@ -144,16 +182,41 @@ bool server_init(struct sway_server *server) {
144 server->foreign_toplevel_manager = 182 server->foreign_toplevel_manager =
145 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 183 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
146 184
185 sway_session_lock_init();
186
187 server->drm_lease_manager=
188 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
189 if (server->drm_lease_manager) {
190 server->drm_lease_request.notify = handle_drm_lease_request;
191 wl_signal_add(&server->drm_lease_manager->events.request,
192 &server->drm_lease_request);
193 } else {
194 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
195 sway_log(SWAY_INFO, "VR will not be available");
196 }
197
147 wlr_export_dmabuf_manager_v1_create(server->wl_display); 198 wlr_export_dmabuf_manager_v1_create(server->wl_display);
148 wlr_screencopy_manager_v1_create(server->wl_display); 199 wlr_screencopy_manager_v1_create(server->wl_display);
149 wlr_data_control_manager_v1_create(server->wl_display); 200 wlr_data_control_manager_v1_create(server->wl_display);
150 wlr_primary_selection_v1_device_manager_create(server->wl_display); 201 wlr_primary_selection_v1_device_manager_create(server->wl_display);
151 wlr_viewporter_create(server->wl_display); 202 wlr_viewporter_create(server->wl_display);
203 wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
204
205 struct wlr_xdg_foreign_registry *foreign_registry =
206 wlr_xdg_foreign_registry_create(server->wl_display);
207 wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);
208 wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry);
209
210 server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display);
211 server->xdg_activation_v1_request_activate.notify =
212 xdg_activation_v1_handle_request_activate;
213 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
214 &server->xdg_activation_v1_request_activate);
152 215
153 // Avoid using "wayland-0" as display socket 216 // Avoid using "wayland-0" as display socket
154 char name_candidate[16]; 217 char name_candidate[16];
155 for (int i = 1; i <= 32; ++i) { 218 for (unsigned int i = 1; i <= 32; ++i) {
156 sprintf(name_candidate, "wayland-%d", i); 219 snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
157 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { 220 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
158 server->socket = strdup(name_candidate); 221 server->socket = strdup(name_candidate);
159 break; 222 break;
@@ -166,27 +229,26 @@ bool server_init(struct sway_server *server) {
166 return false; 229 return false;
167 } 230 }
168 231
169 server->noop_backend = wlr_noop_backend_create(server->wl_display); 232 server->headless_backend = wlr_headless_backend_create(server->wl_display);
170
171 struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend);
172 root->noop_output = output_create(wlr_output);
173
174 server->headless_backend =
175 wlr_headless_backend_create_with_renderer(server->wl_display, renderer);
176 if (!server->headless_backend) { 233 if (!server->headless_backend) {
177 sway_log(SWAY_INFO, "Failed to create secondary headless backend, " 234 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
178 "starting without it"); 235 wlr_backend_destroy(server->backend);
236 return false;
179 } else { 237 } else {
180 wlr_multi_backend_add(server->backend, server->headless_backend); 238 wlr_multi_backend_add(server->backend, server->headless_backend);
181 } 239 }
182 240
241 struct wlr_output *wlr_output =
242 wlr_headless_add_output(server->headless_backend, 800, 600);
243 wlr_output_set_name(wlr_output, "FALLBACK");
244 root->fallback_output = output_create(wlr_output);
245
183 // This may have been set already via -Dtxn-timeout 246 // This may have been set already via -Dtxn-timeout
184 if (!server->txn_timeout_ms) { 247 if (!server->txn_timeout_ms) {
185 server->txn_timeout_ms = 200; 248 server->txn_timeout_ms = 200;
186 } 249 }
187 250
188 server->dirty_nodes = create_list(); 251 server->dirty_nodes = create_list();
189 server->transactions = create_list();
190 252
191 server->input = input_manager_create(server); 253 server->input = input_manager_create(server);
192 input_manager_get_default_seat(); // create seat0 254 input_manager_get_default_seat(); // create seat0
@@ -202,7 +264,6 @@ void server_fini(struct sway_server *server) {
202 wl_display_destroy_clients(server->wl_display); 264 wl_display_destroy_clients(server->wl_display);
203 wl_display_destroy(server->wl_display); 265 wl_display_destroy(server->wl_display);
204 list_free(server->dirty_nodes); 266 list_free(server->dirty_nodes);
205 list_free(server->transactions);
206} 267}
207 268
208bool server_start(struct sway_server *server) { 269bool server_start(struct sway_server *server) {
@@ -238,6 +299,7 @@ bool server_start(struct sway_server *server) {
238 wlr_backend_destroy(server->backend); 299 wlr_backend_destroy(server->backend);
239 return false; 300 return false;
240 } 301 }
302
241 return true; 303 return true;
242} 304}
243 305
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 80d08449..42e59d57 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -40,7 +40,7 @@ runtime.
40*font* <font> 40*font* <font>
41 Specifies the font to be used in the bar. _font_ should be specified as a 41 Specifies the font to be used in the bar. _font_ should be specified as a
42 pango font description. For more information on pango font descriptions, 42 pango font description. For more information on pango font descriptions,
43 see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 43 see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
44 44
45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> 45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
46 Sets the gaps from the edge of the screen for the bar. Gaps can either be 46 Sets the gaps from the edge of the screen for the bar. Gaps can either be
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index dbf21d93..8b702b77 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -111,6 +111,9 @@ The following commands may only be used in the configuration file.
111 Maps inputs from this device to the specified output. Only meaningful if the 111 Maps inputs from this device to the specified output. Only meaningful if the
112 device is a pointer, touch, or drawing tablet device. 112 device is a pointer, touch, or drawing tablet device.
113 113
114 The wildcard _\*_ can be used to map the input device to the whole desktop
115 layout.
116
114*input* <identifier> map_to_region <X> <Y> <width> <height> 117*input* <identifier> map_to_region <X> <Y> <width> <height>
115 Maps inputs from this device to the specified region of the global output 118 Maps inputs from this device to the specified region of the global output
116 layout. Only meaningful if the device is a pointer, touch, or drawing tablet 119 layout. Only meaningful if the device is a pointer, touch, or drawing tablet
@@ -230,7 +233,7 @@ correct seat.
230 not explicitly attached to another seat (similar to a "default" seat). 233 not explicitly attached to another seat (similar to a "default" seat).
231 234
232*seat* <name> hide_cursor <timeout>|when-typing [enable|disable] 235*seat* <name> hide_cursor <timeout>|when-typing [enable|disable]
233 Hides the cursor image after the specified event occured. 236 Hides the cursor image after the specified event occurred.
234 237
235 If _timeout_ is specified, then the cursor will be hidden after _timeout_ 238 If _timeout_ is specified, then the cursor will be hidden after _timeout_
236 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 239 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0
@@ -274,7 +277,7 @@ correct seat.
274 whether future inhibitors are honoured by default, i.e. activated 277 whether future inhibitors are honoured by default, i.e. activated
275 automatically, the default being _enable_. When used at runtime, 278 automatically, the default being _enable_. When used at runtime,
276 _disable_ also disables any currently active inhibitors. _activate_, 279 _disable_ also disables any currently active inhibitors. _activate_,
277 _deactivate_ and _toggle_ are only useable at runtime and change the 280 _deactivate_ and _toggle_ are only usable at runtime and change the
278 state of a potentially existing inhibitor on the currently focused 281 state of a potentially existing inhibitor on the currently focused
279 window. This can be used with the current seat alias (_-_) to affect 282 window. This can be used with the current seat alias (_-_) to affect
280 only the currently focused window of the current seat. Subcommand 283 only the currently focused window of the current seat. Subcommand
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 1b855959..c7a2c473 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*
@@ -370,7 +373,7 @@ node and will have the following properties:
370 that can be used as an aid in submitting reproduction steps for bug reports 373 that can be used as an aid in submitting reproduction steps for bug reports
371|- fullscreen_mode 374|- fullscreen_mode
372: integer 375: integer
373: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means 376: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
374 full workspace, and 2 means global fullscreen 377 full workspace, and 2 means global fullscreen
375|- app_id 378|- app_id
376: string 379: string
@@ -1131,6 +1134,9 @@ following properties:
1131|- xkb_active_layout_index 1134|- xkb_active_layout_index
1132: integer 1135: integer
1133: (Only keyboards) The index of the active keyboard layout in use 1136: (Only keyboards) The index of the active keyboard layout in use
1137|- scroll_factor
1138: floating
1139: (Only pointers) Multiplier applied on scroll event values.
1134|- libinput 1140|- libinput
1135: object 1141: object
1136: (Only libinput devices) An object describing the current device settings. 1142: (Only libinput devices) An object describing the current device settings.
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 69f529fe..b7d5e577 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
@@ -109,12 +119,20 @@ must be separated by one space. For example:
109 Enables or disables the specified output (all outputs are enabled by 119 Enables or disables the specified output (all outputs are enabled by
110 default). 120 default).
111 121
122 As opposed to the _power_ command, the output will loose its current
123 workspace and windows.
124
112*output* <name> toggle 125*output* <name> toggle
113 Toggle the specified output. 126 Toggle the specified output.
114 127
115*output* <name> dpms on|off 128*output* <name> power on|off|toggle
116 Enables or disables the specified output via DPMS. To turn an output off 129 Turns on or off the specified output.
117 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. 130
131 As opposed to the _enable_ and _disable_ commands, the output keeps its
132 current workspaces and windows.
133
134*output* <name> dpms on|off|toggle
135 Deprecated. Alias for _power_.
118 136
119*output* <name> max_render_time off|<msec> 137*output* <name> max_render_time off|<msec>
120 Controls when sway composites the output, as a positive number of 138 Controls when sway composites the output, as a positive number of
@@ -142,11 +160,26 @@ must be separated by one space. For example:
142 Enables or disables adaptive synchronization (often referred to as Variable 160 Enables or disables adaptive synchronization (often referred to as Variable
143 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). 161 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync).
144 162
145 Adaptive sync allows clients to submit frames a little to late without 163 Adaptive sync allows clients to submit frames a little too late without
146 having to wait a whole refresh period to display it on screen. Enabling 164 having to wait a whole refresh period to display it on screen. Enabling
147 adaptive sync can improve latency, but can cause flickering on some 165 adaptive sync can improve latency, but can cause flickering on some
148 hardware. 166 hardware.
149 167
168*output* <name> render_bit_depth 8|10
169 Controls the color channel bit depth at which frames are rendered; the
170 default is currently 8 bits per channel.
171
172 Setting higher values will not have an effect if hardware and software lack
173 support for such bit depths. Successfully increasing the render bit depth
174 will not necessarily increase the bit depth of the frames sent to a display.
175 An increased render bit depth may provide smoother rendering of gradients,
176 and screenshots which can more precisely store the colors of programs
177 which display high bit depth colors.
178
179 Warnings: this can break screenshot/screencast programs which have not been
180 updated to work with different bit depths. This command is experimental,
181 and may be removed or changed in the future.
182
150# SEE ALSO 183# SEE ALSO
151 184
152*sway*(5) *sway-input*(5) 185*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 02592b5f..d8a462d3 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -155,7 +155,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
155 is specified, the view will be fullscreen across all outputs. 155 is specified, the view will be fullscreen across all outputs.
156 156
157*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current 157*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current
158set|plus|minus <amount> 158set|plus|minus|toggle <amount>
159 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the 159 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the
160 _current_ workspace. _outer_ gaps can be altered per side with _top_, 160 _current_ workspace. _outer_ gaps can be altered per side with _top_,
161 _right_, _bottom_, and _left_ or per direction with _horizontal_ and 161 _right_, _bottom_, and _left_ or per direction with _horizontal_ and
@@ -176,6 +176,12 @@ set|plus|minus <amount>
176*layout* default|splith|splitv|stacking|tabbed 176*layout* default|splith|splitv|stacking|tabbed
177 Sets the layout mode of the focused container. 177 Sets the layout mode of the focused container.
178 178
179 When using the _stacking_ layout, only the focused window in the container is
180 displayed, with the opened windows' list on the top of the container.
181
182 The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically
183 split.
184
179*layout* toggle [split|all] 185*layout* toggle [split|all]
180 Cycles the layout mode of the focused container though a preset list of 186 Cycles the layout mode of the focused container though a preset list of
181 layouts. If no argument is given, then it cycles through stacking, tabbed 187 layouts. If no argument is given, then it cycles through stacking, tabbed
@@ -210,15 +216,14 @@ set|plus|minus <amount>
210 further details. 216 further details.
211 217
212*move* left|right|up|down [<px> px] 218*move* left|right|up|down [<px> px]
213 Moves the focused container in the direction specified. If the container, 219 Moves the focused container in the direction specified. The optional _px_
214 the optional _px_ argument specifies how many pixels to move the container. 220 argument specifies how many pixels to move the container. If unspecified,
215 If unspecified, the default is 10 pixels. Pixels are ignored when moving 221 the default is 10 pixels. Pixels are ignored when moving tiled containers.
216 tiled containers.
217 222
218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] 223*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
219 Moves the focused container to the specified position in the workspace. 224 Moves the focused container to the specified position in the workspace.
220 The position can be specified in pixels or percentage points, omitting 225 The position can be specified in pixels or percentage points, omitting
221 the unit defaults to pixels. If _absolute_ is used, the position is 226 the unit defaults to pixels. If _absolute_ is used, the position is
222 relative to all outputs. _absolute_ can not be used with percentage points. 227 relative to all outputs. _absolute_ can not be used with percentage points.
223 228
224*move* [absolute] position center 229*move* [absolute] position center
@@ -319,8 +324,10 @@ set|plus|minus <amount>
319 established by the *seat* subcommand of the same name. See 324 established by the *seat* subcommand of the same name. See
320 *sway-input*(5) for more ways to affect inhibitors. 325 *sway-input*(5) for more ways to affect inhibitors.
321 326
322*split* vertical|v|horizontal|h|toggle|t 327*split* vertical|v|horizontal|h|none|n|toggle|t
323 Splits the current container, vertically or horizontally. When _toggle_ is 328 Splits the current container, vertically or horizontally. When _none_ is
329 specified, the effect of a previous split is undone if the current
330 container is the only child of a split parent. When _toggle_ is
324 specified, the current container is split opposite to the parent 331 specified, the current container is split opposite to the parent
325 container's layout. 332 container's layout.
326 333
@@ -404,7 +411,7 @@ runtime.
404 a keyboard shortcuts inhibitor is active for the currently focused 411 a keyboard shortcuts inhibitor is active for the currently focused
405 window. Such inhibitors are usually requested by remote desktop and 412 window. Such inhibitors are usually requested by remote desktop and
406 virtualization software to enable the user to send keyboard shortcuts 413 virtualization software to enable the user to send keyboard shortcuts
407 to the remote or virtual session. The _--inhibited_ flag allows to 414 to the remote or virtual session. The _--inhibited_ flag allows one to
408 define bindings which will be exempt from pass-through to such 415 define bindings which will be exempt from pass-through to such
409 software. The same preference logic as for _--locked_ applies. 416 software. The same preference logic as for _--locked_ applies.
410 417
@@ -480,6 +487,62 @@ runtime.
480 bindswitch lid:toggle exec echo "Lid moved" 487 bindswitch lid:toggle exec echo "Lid moved"
481``` 488```
482 489
490*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
491<gesture>[:<fingers>][:directions] <command>
492 Binds _gesture_ to execute the sway command _command_ when detected.
493 Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
494 can be limited to bind to a certain number of _fingers_ or, for a
495 _pinch_ or _swipe_ gesture, to certain _directions_.
496
497[[ *type*
498:[ *fingers*
499:< *direction*
500| hold
501:- 1 - 5
502: none
503| swipe
504: 3 - 5
505: up, down, left, right
506| pinch
507: 2 - 5
508: all above + inward, outward, clockwise, counterclockwise
509
510 The _fingers_ can be limited to any sensible number or left empty to accept
511 any finger counts.
512 Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
513 _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
514 Multiple directions can be combined by a plus.
515
516 If a _input-device_ is given, the binding will only be executed for
517 that input device and will be executed instead of any binding that is
518 generic to all devices. By default, if you overwrite a binding,
519 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
520
521 The _--exact_ flag can be used to ensure a binding only matches when exactly
522 all specified directions are matched and nothing more. If there is matching
523 binding with _--exact_, it will be preferred.
524
525 The priority for matching bindings is as follows: input device, then
526 exact matches followed by matches with the highest number of matching
527 directions.
528
529 Gestures executed while the pointer is above a bar are not handled by sway.
530 See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
531
532 Example:
533```
534 # Allow switching between workspaces with left and right swipes
535 bindgesture swipe:right workspace prev
536 bindgesture swipe:left workspace next
537
538 # Allow container movements by pinching them
539 bindgesture pinch:inward+up move up
540 bindgesture pinch:inward+down move down
541 bindgesture pinch:inward+left move left
542 bindgesture pinch:inward+right move right
543
544```
545
483*client.background* <color> 546*client.background* <color>
484 This command is ignored and is only present for i3 compatibility. 547 This command is ignored and is only present for i3 compatibility.
485 548
@@ -497,6 +560,12 @@ runtime.
497 *client.focused_inactive* 560 *client.focused_inactive*
498 The most recently focused view within a container which is not focused. 561 The most recently focused view within a container which is not focused.
499 562
563 *client.focused_tab_title*
564 A view that has focused descendant container.
565 Tab or stack container title that is the parent of the focused container
566 but is not directly focused. Defaults to focused_inactive if not
567 specified and does not use the indicator and child_border colors.
568
500 *client.placeholder* 569 *client.placeholder*
501 Ignored (present for i3 compatibility). 570 Ignored (present for i3 compatibility).
502 571
@@ -552,6 +621,12 @@ The default colors are:
552: #ffffff 621: #ffffff
553: #484e50 622: #484e50
554: #5f676a 623: #5f676a
624| *focused_tab_title*
625: #333333
626: #5f676a
627: #ffffff
628: n/a
629: n/a
555| *unfocused* 630| *unfocused*
556: #333333 631: #333333
557: #222222 632: #222222
@@ -606,11 +681,11 @@ The default colors are:
606 after switching between workspaces. 681 after switching between workspaces.
607 682
608*focus_on_window_activation* smart|urgent|focus|none 683*focus_on_window_activation* smart|urgent|focus|none
609 This option determines what to do when an xwayland client requests 684 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 685 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. 686 _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 687 become focused only if it is already visible, otherwise the urgent state
613 visible, otherwise the urgent state will be set. Default is _urgent_. 688 will be set. Default is _urgent_.
614 689
615*focus_wrapping* yes|no|force|workspace 690*focus_wrapping* yes|no|force|workspace
616 This option determines what to do when attempting to focus over the edge 691 This option determines what to do when attempting to focus over the edge
@@ -630,14 +705,14 @@ The default colors are:
630 should be used instead. Regardless of whether pango markup is enabled, 705 should be used instead. Regardless of whether pango markup is enabled,
631 _font_ should be specified as a pango font description. For more 706 _font_ should be specified as a pango font description. For more
632 information on pango font descriptions, see 707 information on pango font descriptions, see
633 https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 708 https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
634 709
635*force_display_urgency_hint* <timeout> [ms] 710*force_display_urgency_hint* <timeout> [ms]
636 If an application on another workspace sets an urgency hint, switching to this 711 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 712 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 713 window decoration color would be immediately reset to *client.focused*. This
639 may make it unnecessarily hard to tell which window originally raised the 714 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. 715 event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset.
641 716
642*titlebar_border_thickness* <thickness> 717*titlebar_border_thickness* <thickness>
643 Thickness of the titlebar border in pixels 718 Thickness of the titlebar border in pixels
@@ -690,9 +765,10 @@ The default colors are:
690 borders will only be enabled if the workspace has more than one visible 765 borders will only be enabled if the workspace has more than one visible
691 child and gaps equal to zero. 766 child and gaps equal to zero.
692 767
693*smart_gaps* on|off 768*smart_gaps* on|off|toggle|inverse_outer
694 If smart_gaps are _on_ gaps will only be enabled if a workspace has more 769 If smart_gaps are _on_ gaps will only be enabled if a workspace has more
695 than one child. 770 than one child. If smart_gaps are _inverse_outer_ outer gaps will only
771 be enabled if a workspace has exactly one child.
696 772
697*mark* --add|--replace [--toggle] <identifier> 773*mark* --add|--replace [--toggle] <identifier>
698 Marks are arbitrary labels that can be used to identify certain windows and 774 Marks are arbitrary labels that can be used to identify certain windows and
@@ -771,6 +847,11 @@ The default colors are:
771*unbindswitch* <switch>:<state> 847*unbindswitch* <switch>:<state>
772 Removes a binding for when <switch> changes to <state>. 848 Removes a binding for when <switch> changes to <state>.
773 849
850*unbindgesture* [--exact] [--input-device=<device>] \
851<gesture>[:<fingers>][:directions]
852 Removes a binding for the specified _gesture_, _fingers_
853 and _directions_ combination.
854
774*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 855*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
775[--to-code] [--input-device=<device>] <key combo> 856[--to-code] [--input-device=<device>] <key combo>
776 Removes the binding for _key combo_ that was previously bound with the 857 Removes the binding for _key combo_ that was previously bound with the
diff --git a/sway/swaynag.c b/sway/swaynag.c
index db5a919a..4a0a6d30 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -64,6 +64,8 @@ bool swaynag_spawn(const char *swaynag_command,
64 sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); 64 sway_log(SWAY_ERROR, "Failed to create fork for swaynag");
65 goto failed; 65 goto failed;
66 } else if (pid == 0) { 66 } else if (pid == 0) {
67 restore_nofile_limit();
68
67 pid = fork(); 69 pid = fork();
68 if (pid < 0) { 70 if (pid < 0) {
69 sway_log_errno(SWAY_ERROR, "fork failed"); 71 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -87,8 +89,8 @@ bool swaynag_spawn(const char *swaynag_command,
87 size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; 89 size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2;
88 char *cmd = malloc(length); 90 char *cmd = malloc(length);
89 snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); 91 snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args);
90 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 92 execlp("sh", "sh", "-c", cmd, NULL);
91 sway_log_errno(SWAY_ERROR, "execl failed"); 93 sway_log_errno(SWAY_ERROR, "execlp failed");
92 _exit(EXIT_FAILURE); 94 _exit(EXIT_FAILURE);
93 } 95 }
94 _exit(EXIT_SUCCESS); 96 _exit(EXIT_SUCCESS);
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index bac9f2fa..9c1a11e5 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -55,7 +55,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) {
55 // Calculate gap size 55 // Calculate gap size
56 double inner_gap = 0; 56 double inner_gap = 0;
57 struct sway_container *child = children->items[0]; 57 struct sway_container *child = children->items[0];
58 struct sway_workspace *ws = child->workspace; 58 struct sway_workspace *ws = child->pending.workspace;
59 if (ws) { 59 if (ws) {
60 inner_gap = ws->gaps_inner; 60 inner_gap = ws->gaps_inner;
61 } 61 }
@@ -66,7 +66,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) {
66 if (layout == L_TABBED || layout == L_STACKED) { 66 if (layout == L_TABBED || layout == L_STACKED) {
67 inner_gap = 0; 67 inner_gap = 0;
68 } 68 }
69 temp = temp->parent; 69 temp = temp->pending.parent;
70 } 70 }
71 double total_gap = fmin(inner_gap * (children->length - 1), 71 double total_gap = fmin(inner_gap * (children->length - 1),
72 fmax(0, parent->width - MIN_SANE_W * children->length)); 72 fmax(0, parent->width - MIN_SANE_W * children->length));
@@ -79,15 +79,15 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) {
79 for (int i = 0; i < children->length; ++i) { 79 for (int i = 0; i < children->length; ++i) {
80 struct sway_container *child = children->items[i]; 80 struct sway_container *child = children->items[i];
81 child->child_total_width = child_total_width; 81 child->child_total_width = child_total_width;
82 child->x = child_x; 82 child->pending.x = child_x;
83 child->y = parent->y; 83 child->pending.y = parent->y;
84 child->width = round(child->width_fraction * child_total_width); 84 child->pending.width = round(child->width_fraction * child_total_width);
85 child->height = parent->height; 85 child->pending.height = parent->height;
86 child_x += child->width + inner_gap; 86 child_x += child->pending.width + inner_gap;
87 87
88 // Make last child use remaining width of parent 88 // Make last child use remaining width of parent
89 if (i == children->length - 1) { 89 if (i == children->length - 1) {
90 child->width = parent->x + parent->width - child->x; 90 child->pending.width = parent->x + parent->width - child->pending.x;
91 } 91 }
92 } 92 }
93} 93}
@@ -134,7 +134,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) {
134 // Calculate gap size 134 // Calculate gap size
135 double inner_gap = 0; 135 double inner_gap = 0;
136 struct sway_container *child = children->items[0]; 136 struct sway_container *child = children->items[0];
137 struct sway_workspace *ws = child->workspace; 137 struct sway_workspace *ws = child->pending.workspace;
138 if (ws) { 138 if (ws) {
139 inner_gap = ws->gaps_inner; 139 inner_gap = ws->gaps_inner;
140 } 140 }
@@ -145,7 +145,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) {
145 if (layout == L_TABBED || layout == L_STACKED) { 145 if (layout == L_TABBED || layout == L_STACKED) {
146 inner_gap = 0; 146 inner_gap = 0;
147 } 147 }
148 temp = temp->parent; 148 temp = temp->pending.parent;
149 } 149 }
150 double total_gap = fmin(inner_gap * (children->length - 1), 150 double total_gap = fmin(inner_gap * (children->length - 1),
151 fmax(0, parent->height - MIN_SANE_H * children->length)); 151 fmax(0, parent->height - MIN_SANE_H * children->length));
@@ -158,15 +158,15 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) {
158 for (int i = 0; i < children->length; ++i) { 158 for (int i = 0; i < children->length; ++i) {
159 struct sway_container *child = children->items[i]; 159 struct sway_container *child = children->items[i];
160 child->child_total_height = child_total_height; 160 child->child_total_height = child_total_height;
161 child->x = parent->x; 161 child->pending.x = parent->x;
162 child->y = child_y; 162 child->pending.y = child_y;
163 child->width = parent->width; 163 child->pending.width = parent->width;
164 child->height = round(child->height_fraction * child_total_height); 164 child->pending.height = round(child->height_fraction * child_total_height);
165 child_y += child->height + inner_gap; 165 child_y += child->pending.height + inner_gap;
166 166
167 // Make last child use remaining height of parent 167 // Make last child use remaining height of parent
168 if (i == children->length - 1) { 168 if (i == children->length - 1) {
169 child->height = parent->y + parent->height - child->y; 169 child->pending.height = parent->y + parent->height - child->pending.y;
170 } 170 }
171 } 171 }
172} 172}
@@ -178,10 +178,10 @@ static void apply_tabbed_layout(list_t *children, struct wlr_box *parent) {
178 for (int i = 0; i < children->length; ++i) { 178 for (int i = 0; i < children->length; ++i) {
179 struct sway_container *child = children->items[i]; 179 struct sway_container *child = children->items[i];
180 int parent_offset = child->view ? 0 : container_titlebar_height(); 180 int parent_offset = child->view ? 0 : container_titlebar_height();
181 child->x = parent->x; 181 child->pending.x = parent->x;
182 child->y = parent->y + parent_offset; 182 child->pending.y = parent->y + parent_offset;
183 child->width = parent->width; 183 child->pending.width = parent->width;
184 child->height = parent->height - parent_offset; 184 child->pending.height = parent->height - parent_offset;
185 } 185 }
186} 186}
187 187
@@ -193,10 +193,10 @@ static void apply_stacked_layout(list_t *children, struct wlr_box *parent) {
193 struct sway_container *child = children->items[i]; 193 struct sway_container *child = children->items[i];
194 int parent_offset = child->view ? 0 : 194 int parent_offset = child->view ? 0 :
195 container_titlebar_height() * children->length; 195 container_titlebar_height() * children->length;
196 child->x = parent->x; 196 child->pending.x = parent->x;
197 child->y = parent->y + parent_offset; 197 child->pending.y = parent->y + parent_offset;
198 child->width = parent->width; 198 child->pending.width = parent->width;
199 child->height = parent->height - parent_offset; 199 child->pending.height = parent->height - parent_offset;
200 } 200 }
201} 201}
202 202
@@ -246,7 +246,7 @@ void arrange_container(struct sway_container *container) {
246 } 246 }
247 struct wlr_box box; 247 struct wlr_box box;
248 container_get_box(container, &box); 248 container_get_box(container, &box);
249 arrange_children(container->children, container->layout, &box); 249 arrange_children(container->pending.children, container->pending.layout, &box);
250 node_set_dirty(&container->node); 250 node_set_dirty(&container->node);
251} 251}
252 252
@@ -278,8 +278,8 @@ void arrange_workspace(struct sway_workspace *workspace) {
278 for (int i = 0; i < workspace->floating->length; ++i) { 278 for (int i = 0; i < workspace->floating->length; ++i) {
279 struct sway_container *floater = workspace->floating->items[i]; 279 struct sway_container *floater = workspace->floating->items[i];
280 container_floating_translate(floater, diff_x, diff_y); 280 container_floating_translate(floater, diff_x, diff_y);
281 double center_x = floater->x + floater->width / 2; 281 double center_x = floater->pending.x + floater->pending.width / 2;
282 double center_y = floater->y + floater->height / 2; 282 double center_y = floater->pending.y + floater->pending.height / 2;
283 struct wlr_box workspace_box; 283 struct wlr_box workspace_box;
284 workspace_get_box(workspace, &workspace_box); 284 workspace_get_box(workspace, &workspace_box);
285 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 285 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) {
@@ -294,10 +294,10 @@ void arrange_workspace(struct sway_workspace *workspace) {
294 workspace->x, workspace->y); 294 workspace->x, workspace->y);
295 if (workspace->fullscreen) { 295 if (workspace->fullscreen) {
296 struct sway_container *fs = workspace->fullscreen; 296 struct sway_container *fs = workspace->fullscreen;
297 fs->x = output->lx; 297 fs->pending.x = output->lx;
298 fs->y = output->ly; 298 fs->pending.y = output->ly;
299 fs->width = output->width; 299 fs->pending.width = output->width;
300 fs->height = output->height; 300 fs->pending.height = output->height;
301 arrange_container(fs); 301 arrange_container(fs);
302 } else { 302 } else {
303 struct wlr_box box; 303 struct wlr_box box;
@@ -311,12 +311,13 @@ void arrange_output(struct sway_output *output) {
311 if (config->reloading) { 311 if (config->reloading) {
312 return; 312 return;
313 } 313 }
314 const struct wlr_box *output_box = wlr_output_layout_get_box( 314 struct wlr_box output_box;
315 root->output_layout, output->wlr_output); 315 wlr_output_layout_get_box(root->output_layout,
316 output->lx = output_box->x; 316 output->wlr_output, &output_box);
317 output->ly = output_box->y; 317 output->lx = output_box.x;
318 output->width = output_box->width; 318 output->ly = output_box.y;
319 output->height = output_box->height; 319 output->width = output_box.width;
320 output->height = output_box.height;
320 321
321 for (int i = 0; i < output->workspaces->length; ++i) { 322 for (int i = 0; i < output->workspaces->length; ++i) {
322 struct sway_workspace *workspace = output->workspaces->items[i]; 323 struct sway_workspace *workspace = output->workspaces->items[i];
@@ -328,19 +329,19 @@ void arrange_root(void) {
328 if (config->reloading) { 329 if (config->reloading) {
329 return; 330 return;
330 } 331 }
331 const struct wlr_box *layout_box = 332 struct wlr_box layout_box;
332 wlr_output_layout_get_box(root->output_layout, NULL); 333 wlr_output_layout_get_box(root->output_layout, NULL, &layout_box);
333 root->x = layout_box->x; 334 root->x = layout_box.x;
334 root->y = layout_box->y; 335 root->y = layout_box.y;
335 root->width = layout_box->width; 336 root->width = layout_box.width;
336 root->height = layout_box->height; 337 root->height = layout_box.height;
337 338
338 if (root->fullscreen_global) { 339 if (root->fullscreen_global) {
339 struct sway_container *fs = root->fullscreen_global; 340 struct sway_container *fs = root->fullscreen_global;
340 fs->x = root->x; 341 fs->pending.x = root->x;
341 fs->y = root->y; 342 fs->pending.y = root->y;
342 fs->width = root->width; 343 fs->pending.width = root->width;
343 fs->height = root->height; 344 fs->pending.height = root->height;
344 arrange_container(fs); 345 arrange_container(fs);
345 } else { 346 } else {
346 for (int i = 0; i < root->outputs->length; ++i) { 347 for (int i = 0; i < root->outputs->length; ++i) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6a9ce1c4..04ef965f 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,12 +1,18 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <drm_fourcc.h>
3#include <stdint.h> 4#include <stdint.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h> 6#include <string.h>
6#include <strings.h> 7#include <strings.h>
8#include <sys/stat.h>
7#include <wayland-server-core.h> 9#include <wayland-server-core.h>
10#include <wlr/types/wlr_linux_dmabuf_v1.h>
8#include <wlr/types/wlr_output_layout.h> 11#include <wlr/types/wlr_output_layout.h>
9#include "cairo.h" 12#include <wlr/types/wlr_subcompositor.h>
13#include <wlr/render/drm_format_set.h>
14#include "linux-dmabuf-unstable-v1-protocol.h"
15#include "cairo_util.h"
10#include "pango.h" 16#include "pango.h"
11#include "sway/config.h" 17#include "sway/config.h"
12#include "sway/desktop.h" 18#include "sway/desktop.h"
@@ -19,6 +25,7 @@
19#include "sway/tree/arrange.h" 25#include "sway/tree/arrange.h"
20#include "sway/tree/view.h" 26#include "sway/tree/view.h"
21#include "sway/tree/workspace.h" 27#include "sway/tree/workspace.h"
28#include "sway/xdg_decoration.h"
22#include "list.h" 29#include "list.h"
23#include "log.h" 30#include "log.h"
24#include "stringop.h" 31#include "stringop.h"
@@ -30,12 +37,12 @@ struct sway_container *container_create(struct sway_view *view) {
30 return NULL; 37 return NULL;
31 } 38 }
32 node_init(&c->node, N_CONTAINER, c); 39 node_init(&c->node, N_CONTAINER, c);
33 c->layout = L_NONE; 40 c->pending.layout = L_NONE;
34 c->view = view; 41 c->view = view;
35 c->alpha = 1.0f; 42 c->alpha = 1.0f;
36 43
37 if (!view) { 44 if (!view) {
38 c->children = create_list(); 45 c->pending.children = create_list();
39 c->current.children = create_list(); 46 c->current.children = create_list();
40 } 47 }
41 c->marks = create_list(); 48 c->marks = create_list();
@@ -62,7 +69,8 @@ void container_destroy(struct sway_container *con) {
62 wlr_texture_destroy(con->title_focused_inactive); 69 wlr_texture_destroy(con->title_focused_inactive);
63 wlr_texture_destroy(con->title_unfocused); 70 wlr_texture_destroy(con->title_unfocused);
64 wlr_texture_destroy(con->title_urgent); 71 wlr_texture_destroy(con->title_urgent);
65 list_free(con->children); 72 wlr_texture_destroy(con->title_focused_tab_title);
73 list_free(con->pending.children);
66 list_free(con->current.children); 74 list_free(con->current.children);
67 list_free(con->outputs); 75 list_free(con->outputs);
68 76
@@ -71,11 +79,10 @@ void container_destroy(struct sway_container *con) {
71 wlr_texture_destroy(con->marks_focused_inactive); 79 wlr_texture_destroy(con->marks_focused_inactive);
72 wlr_texture_destroy(con->marks_unfocused); 80 wlr_texture_destroy(con->marks_unfocused);
73 wlr_texture_destroy(con->marks_urgent); 81 wlr_texture_destroy(con->marks_urgent);
82 wlr_texture_destroy(con->marks_focused_tab_title);
74 83
75 if (con->view) { 84 if (con->view && con->view->container == con) {
76 if (con->view->container == con) { 85 con->view->container = NULL;
77 con->view->container = NULL;
78 }
79 if (con->view->destroying) { 86 if (con->view->destroying) {
80 view_destroy(con->view); 87 view_destroy(con->view);
81 } 88 }
@@ -90,10 +97,10 @@ void container_begin_destroy(struct sway_container *con) {
90 } 97 }
91 // The workspace must have the fullscreen pointer cleared so that the 98 // The workspace must have the fullscreen pointer cleared so that the
92 // seat code can find an appropriate new focus. 99 // seat code can find an appropriate new focus.
93 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { 100 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) {
94 con->workspace->fullscreen = NULL; 101 con->pending.workspace->fullscreen = NULL;
95 } 102 }
96 if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { 103 if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
97 container_fullscreen_disable(con); 104 container_fullscreen_disable(con);
98 } 105 }
99 106
@@ -108,11 +115,11 @@ void container_begin_destroy(struct sway_container *con) {
108 root_scratchpad_remove_container(con); 115 root_scratchpad_remove_container(con);
109 } 116 }
110 117
111 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 118 if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
112 container_fullscreen_disable(con); 119 container_fullscreen_disable(con);
113 } 120 }
114 121
115 if (con->parent || con->workspace) { 122 if (con->pending.parent || con->pending.workspace) {
116 container_detach(con); 123 container_detach(con);
117 } 124 }
118} 125}
@@ -121,12 +128,12 @@ void container_reap_empty(struct sway_container *con) {
121 if (con->view) { 128 if (con->view) {
122 return; 129 return;
123 } 130 }
124 struct sway_workspace *ws = con->workspace; 131 struct sway_workspace *ws = con->pending.workspace;
125 while (con) { 132 while (con) {
126 if (con->children->length) { 133 if (con->pending.children->length) {
127 return; 134 return;
128 } 135 }
129 struct sway_container *parent = con->parent; 136 struct sway_container *parent = con->pending.parent;
130 container_begin_destroy(con); 137 container_begin_destroy(con);
131 con = parent; 138 con = parent;
132 } 139 }
@@ -139,9 +146,9 @@ struct sway_container *container_flatten(struct sway_container *container) {
139 if (container->view) { 146 if (container->view) {
140 return NULL; 147 return NULL;
141 } 148 }
142 while (container && container->children->length == 1) { 149 while (container && container->pending.children->length == 1) {
143 struct sway_container *child = container->children->items[0]; 150 struct sway_container *child = container->pending.children->items[0];
144 struct sway_container *parent = container->parent; 151 struct sway_container *parent = container->pending.parent;
145 container_replace(container, child); 152 container_replace(container, child);
146 container_begin_destroy(container); 153 container_begin_destroy(container);
147 container = parent; 154 container = parent;
@@ -151,11 +158,11 @@ struct sway_container *container_flatten(struct sway_container *container) {
151 158
152struct sway_container *container_find_child(struct sway_container *container, 159struct sway_container *container_find_child(struct sway_container *container,
153 bool (*test)(struct sway_container *con, void *data), void *data) { 160 bool (*test)(struct sway_container *con, void *data), void *data) {
154 if (!container->children) { 161 if (!container->pending.children) {
155 return NULL; 162 return NULL;
156 } 163 }
157 for (int i = 0; i < container->children->length; ++i) { 164 for (int i = 0; i < container->pending.children->length; ++i) {
158 struct sway_container *child = container->children->items[i]; 165 struct sway_container *child = container->pending.children->items[i];
159 if (test(child, data)) { 166 if (test(child, data)) {
160 return child; 167 return child;
161 } 168 }
@@ -187,7 +194,7 @@ static struct sway_container *surface_at_view(struct sway_container *con, double
187#endif 194#endif
188 case SWAY_VIEW_XDG_SHELL: 195 case SWAY_VIEW_XDG_SHELL:
189 _surface = wlr_xdg_surface_surface_at( 196 _surface = wlr_xdg_surface_surface_at(
190 view->wlr_xdg_surface, 197 view->wlr_xdg_toplevel->base,
191 view_sx, view_sy, &_sx, &_sy); 198 view_sx, view_sy, &_sx, &_sy);
192 break; 199 break;
193 } 200 }
@@ -310,7 +317,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
310 return NULL; 317 return NULL;
311} 318}
312 319
313struct sway_container *view_container_at(struct sway_node *parent, 320static struct sway_container *view_container_content_at(struct sway_node *parent,
314 double lx, double ly, 321 double lx, double ly,
315 struct wlr_surface **surface, double *sx, double *sy) { 322 struct wlr_surface **surface, double *sx, double *sy) {
316 if (!sway_assert(node_is_view(parent), "Expected a view")) { 323 if (!sway_assert(node_is_view(parent), "Expected a view")) {
@@ -319,10 +326,33 @@ struct sway_container *view_container_at(struct sway_node *parent,
319 326
320 struct sway_container *container = parent->sway_container; 327 struct sway_container *container = parent->sway_container;
321 struct wlr_box box = { 328 struct wlr_box box = {
322 .x = container->x, 329 .x = container->pending.content_x,
323 .y = container->y, 330 .y = container->pending.content_y,
324 .width = container->width, 331 .width = container->pending.content_width,
325 .height = container->height, 332 .height = container->pending.content_height,
333 };
334
335 if (wlr_box_contains_point(&box, lx, ly)) {
336 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
337 return container;
338 }
339
340 return NULL;
341}
342
343static struct sway_container *view_container_at(struct sway_node *parent,
344 double lx, double ly,
345 struct wlr_surface **surface, double *sx, double *sy) {
346 if (!sway_assert(node_is_view(parent), "Expected a view")) {
347 return NULL;
348 }
349
350 struct sway_container *container = parent->sway_container;
351 struct wlr_box box = {
352 .x = container->pending.x,
353 .y = container->pending.y,
354 .width = container->pending.width,
355 .height = container->pending.height,
326 }; 356 };
327 357
328 if (wlr_box_contains_point(&box, lx, ly)) { 358 if (wlr_box_contains_point(&box, lx, ly)) {
@@ -357,19 +387,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent,
357} 387}
358 388
359static bool surface_is_popup(struct wlr_surface *surface) { 389static bool surface_is_popup(struct wlr_surface *surface) {
360 if (wlr_surface_is_xdg_surface(surface)) { 390 while (!wlr_surface_is_xdg_surface(surface)) {
361 struct wlr_xdg_surface *xdg_surface = 391 if (!wlr_surface_is_subsurface(surface)) {
362 wlr_xdg_surface_from_wlr_surface(surface); 392 return false;
363 while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
364 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
365 return true;
366 }
367 xdg_surface = xdg_surface->toplevel->parent;
368 } 393 }
369 return false; 394 struct wlr_subsurface *subsurface =
395 wlr_subsurface_from_wlr_surface(surface);
396 surface = subsurface->parent;
370 } 397 }
371 398 struct wlr_xdg_surface *xdg_surface =
372 return false; 399 wlr_xdg_surface_from_wlr_surface(surface);
400 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
373} 401}
374 402
375struct sway_container *container_at(struct sway_workspace *workspace, 403struct sway_container *container_at(struct sway_workspace *workspace,
@@ -394,7 +422,7 @@ struct sway_container *container_at(struct sway_workspace *workspace,
394 } 422 }
395 // Tiling (focused) 423 // Tiling (focused)
396 if (focus && focus->view && !is_floating) { 424 if (focus && focus->view && !is_floating) {
397 if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { 425 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
398 return c; 426 return c;
399 } 427 }
400 } 428 }
@@ -408,19 +436,41 @@ struct sway_container *container_at(struct sway_workspace *workspace,
408void container_for_each_child(struct sway_container *container, 436void container_for_each_child(struct sway_container *container,
409 void (*f)(struct sway_container *container, void *data), 437 void (*f)(struct sway_container *container, void *data),
410 void *data) { 438 void *data) {
411 if (container->children) { 439 if (container->pending.children) {
412 for (int i = 0; i < container->children->length; ++i) { 440 for (int i = 0; i < container->pending.children->length; ++i) {
413 struct sway_container *child = container->children->items[i]; 441 struct sway_container *child = container->pending.children->items[i];
414 f(child, data); 442 f(child, data);
415 container_for_each_child(child, f, data); 443 container_for_each_child(child, f, data);
416 } 444 }
417 } 445 }
418} 446}
419 447
448struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container)
449{
450 struct sway_workspace *workspace = container->pending.workspace;
451
452 if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) {
453 if (container_is_transient_for(container, workspace->fullscreen)) {
454 return NULL;
455 }
456 return workspace->fullscreen;
457 }
458
459 struct sway_container *fullscreen_global = root->fullscreen_global;
460 if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) {
461 if (container_is_transient_for(container, fullscreen_global)) {
462 return NULL;
463 }
464 return fullscreen_global;
465 }
466
467 return NULL;
468}
469
420bool container_has_ancestor(struct sway_container *descendant, 470bool container_has_ancestor(struct sway_container *descendant,
421 struct sway_container *ancestor) { 471 struct sway_container *ancestor) {
422 while (descendant) { 472 while (descendant) {
423 descendant = descendant->parent; 473 descendant = descendant->pending.parent;
424 if (descendant == ancestor) { 474 if (descendant == ancestor) {
425 return true; 475 return true;
426 } 476 }
@@ -446,23 +496,13 @@ struct sway_output *container_get_effective_output(struct sway_container *con) {
446 return con->outputs->items[con->outputs->length - 1]; 496 return con->outputs->items[con->outputs->length - 1];
447} 497}
448 498
449static void update_title_texture(struct sway_container *con, 499static void render_titlebar_text_texture(struct sway_output *output,
450 struct wlr_texture **texture, struct border_colors *class) { 500 struct sway_container *con, struct wlr_texture **texture,
451 struct sway_output *output = container_get_effective_output(con); 501 struct border_colors *class, bool pango_markup, char *text) {
452 if (!output) {
453 return;
454 }
455 if (*texture) {
456 wlr_texture_destroy(*texture);
457 *texture = NULL;
458 }
459 if (!con->formatted_title) {
460 return;
461 }
462
463 double scale = output->wlr_output->scale; 502 double scale = output->wlr_output->scale;
464 int width = 0; 503 int width = 0;
465 int height = con->title_height * scale; 504 int height = config->font_height * scale;
505 int baseline;
466 506
467 // We must use a non-nil cairo_t for cairo_set_font_options to work. 507 // We must use a non-nil cairo_t for cairo_set_font_options to work.
468 // Therefore, we cannot use cairo_create(NULL). 508 // Therefore, we cannot use cairo_create(NULL).
@@ -480,13 +520,28 @@ static void update_title_texture(struct sway_container *con,
480 to_cairo_subpixel_order(output->wlr_output->subpixel)); 520 to_cairo_subpixel_order(output->wlr_output->subpixel));
481 } 521 }
482 cairo_set_font_options(c, fo); 522 cairo_set_font_options(c, fo);
483 get_text_size(c, config->font, &width, NULL, NULL, scale, 523 get_text_size(c, config->font_description, &width, NULL, &baseline, scale,
484 config->pango_markup, "%s", con->formatted_title); 524 config->pango_markup, "%s", text);
485 cairo_surface_destroy(dummy_surface); 525 cairo_surface_destroy(dummy_surface);
486 cairo_destroy(c); 526 cairo_destroy(c);
487 527
528 if (width == 0 || height == 0) {
529 return;
530 }
531
532 if (height > config->font_height * scale) {
533 height = config->font_height * scale;
534 }
535
488 cairo_surface_t *surface = cairo_image_surface_create( 536 cairo_surface_t *surface = cairo_image_surface_create(
489 CAIRO_FORMAT_ARGB32, width, height); 537 CAIRO_FORMAT_ARGB32, width, height);
538 cairo_status_t status = cairo_surface_status(surface);
539 if (status != CAIRO_STATUS_SUCCESS) {
540 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
541 cairo_status_to_string(status));
542 return;
543 }
544
490 cairo_t *cairo = cairo_create(surface); 545 cairo_t *cairo = cairo_create(surface);
491 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 546 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
492 cairo_set_font_options(cairo, fo); 547 cairo_set_font_options(cairo, fo);
@@ -497,23 +552,39 @@ static void update_title_texture(struct sway_container *con,
497 PangoContext *pango = pango_cairo_create_context(cairo); 552 PangoContext *pango = pango_cairo_create_context(cairo);
498 cairo_set_source_rgba(cairo, class->text[0], class->text[1], 553 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
499 class->text[2], class->text[3]); 554 class->text[2], class->text[3]);
500 cairo_move_to(cairo, 0, 0); 555 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
501 556
502 pango_printf(cairo, config->font, scale, config->pango_markup, 557 render_text(cairo, config->font_description, scale, pango_markup, "%s", text);
503 "%s", con->formatted_title);
504 558
505 cairo_surface_flush(surface); 559 cairo_surface_flush(surface);
506 unsigned char *data = cairo_image_surface_get_data(surface); 560 unsigned char *data = cairo_image_surface_get_data(surface);
507 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 561 int stride = cairo_image_surface_get_stride(surface);
508 struct wlr_renderer *renderer = wlr_backend_get_renderer( 562 struct wlr_renderer *renderer = output->wlr_output->renderer;
509 output->wlr_output->backend);
510 *texture = wlr_texture_from_pixels( 563 *texture = wlr_texture_from_pixels(
511 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 564 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
512 cairo_surface_destroy(surface); 565 cairo_surface_destroy(surface);
513 g_object_unref(pango); 566 g_object_unref(pango);
514 cairo_destroy(cairo); 567 cairo_destroy(cairo);
515} 568}
516 569
570static void update_title_texture(struct sway_container *con,
571 struct wlr_texture **texture, struct border_colors *class) {
572 struct sway_output *output = container_get_effective_output(con);
573 if (!output) {
574 return;
575 }
576 if (*texture) {
577 wlr_texture_destroy(*texture);
578 *texture = NULL;
579 }
580 if (!con->formatted_title) {
581 return;
582 }
583
584 render_titlebar_text_texture(output, con, texture, class,
585 config->pango_markup, con->formatted_title);
586}
587
517void container_update_title_textures(struct sway_container *container) { 588void container_update_title_textures(struct sway_container *container) {
518 update_title_texture(container, &container->title_focused, 589 update_title_texture(container, &container->title_focused,
519 &config->border_colors.focused); 590 &config->border_colors.focused);
@@ -523,24 +594,11 @@ void container_update_title_textures(struct sway_container *container) {
523 &config->border_colors.unfocused); 594 &config->border_colors.unfocused);
524 update_title_texture(container, &container->title_urgent, 595 update_title_texture(container, &container->title_urgent,
525 &config->border_colors.urgent); 596 &config->border_colors.urgent);
597 update_title_texture(container, &container->title_focused_tab_title,
598 &config->border_colors.focused_tab_title);
526 container_damage_whole(container); 599 container_damage_whole(container);
527} 600}
528 601
529void container_calculate_title_height(struct sway_container *container) {
530 if (!container->formatted_title) {
531 container->title_height = 0;
532 return;
533 }
534 cairo_t *cairo = cairo_create(NULL);
535 int height;
536 int baseline;
537 get_text_size(cairo, config->font, NULL, &height, &baseline, 1,
538 config->pango_markup, "%s", container->formatted_title);
539 cairo_destroy(cairo);
540 container->title_height = height;
541 container->title_baseline = baseline;
542}
543
544/** 602/**
545 * Calculate and return the length of the tree representation. 603 * Calculate and return the length of the tree representation.
546 * An example tree representation is: V[Terminal, Firefox] 604 * An example tree representation is: V[Terminal, Firefox]
@@ -596,23 +654,22 @@ size_t container_build_representation(enum sway_container_layout layout,
596 654
597void container_update_representation(struct sway_container *con) { 655void container_update_representation(struct sway_container *con) {
598 if (!con->view) { 656 if (!con->view) {
599 size_t len = container_build_representation(con->layout, 657 size_t len = container_build_representation(con->pending.layout,
600 con->children, NULL); 658 con->pending.children, NULL);
601 free(con->formatted_title); 659 free(con->formatted_title);
602 con->formatted_title = calloc(len + 1, sizeof(char)); 660 con->formatted_title = calloc(len + 1, sizeof(char));
603 if (!sway_assert(con->formatted_title, 661 if (!sway_assert(con->formatted_title,
604 "Unable to allocate title string")) { 662 "Unable to allocate title string")) {
605 return; 663 return;
606 } 664 }
607 container_build_representation(con->layout, con->children, 665 container_build_representation(con->pending.layout, con->pending.children,
608 con->formatted_title); 666 con->formatted_title);
609 container_calculate_title_height(con);
610 container_update_title_textures(con); 667 container_update_title_textures(con);
611 } 668 }
612 if (con->parent) { 669 if (con->pending.parent) {
613 container_update_representation(con->parent); 670 container_update_representation(con->pending.parent);
614 } else if (con->workspace) { 671 } else if (con->pending.workspace) {
615 workspace_update_representation(con->workspace); 672 workspace_update_representation(con->pending.workspace);
616 } 673 }
617} 674}
618 675
@@ -638,12 +695,13 @@ void floating_calculate_constraints(int *min_width, int *max_width,
638 *min_height = config->floating_minimum_height; 695 *min_height = config->floating_minimum_height;
639 } 696 }
640 697
641 struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); 698 struct wlr_box box;
699 wlr_output_layout_get_box(root->output_layout, NULL, &box);
642 700
643 if (config->floating_maximum_width == -1) { // no maximum 701 if (config->floating_maximum_width == -1) { // no maximum
644 *max_width = INT_MAX; 702 *max_width = INT_MAX;
645 } else if (config->floating_maximum_width == 0) { // automatic 703 } else if (config->floating_maximum_width == 0) { // automatic
646 *max_width = box->width; 704 *max_width = box.width;
647 } else { 705 } else {
648 *max_width = config->floating_maximum_width; 706 *max_width = config->floating_maximum_width;
649 } 707 }
@@ -651,7 +709,7 @@ void floating_calculate_constraints(int *min_width, int *max_width,
651 if (config->floating_maximum_height == -1) { // no maximum 709 if (config->floating_maximum_height == -1) { // no maximum
652 *max_height = INT_MAX; 710 *max_height = INT_MAX;
653 } else if (config->floating_maximum_height == 0) { // automatic 711 } else if (config->floating_maximum_height == 0) { // automatic
654 *max_height = box->height; 712 *max_height = box.height;
655 } else { 713 } else {
656 *max_height = config->floating_maximum_height; 714 *max_height = config->floating_maximum_height;
657 } 715 }
@@ -663,66 +721,66 @@ static void floating_natural_resize(struct sway_container *con) {
663 floating_calculate_constraints(&min_width, &max_width, 721 floating_calculate_constraints(&min_width, &max_width,
664 &min_height, &max_height); 722 &min_height, &max_height);
665 if (!con->view) { 723 if (!con->view) {
666 con->width = fmax(min_width, fmin(con->width, max_width)); 724 con->pending.width = fmax(min_width, fmin(con->pending.width, max_width));
667 con->height = fmax(min_height, fmin(con->height, max_height)); 725 con->pending.height = fmax(min_height, fmin(con->pending.height, max_height));
668 } else { 726 } else {
669 struct sway_view *view = con->view; 727 struct sway_view *view = con->view;
670 con->content_width = 728 con->pending.content_width =
671 fmax(min_width, fmin(view->natural_width, max_width)); 729 fmax(min_width, fmin(view->natural_width, max_width));
672 con->content_height = 730 con->pending.content_height =
673 fmax(min_height, fmin(view->natural_height, max_height)); 731 fmax(min_height, fmin(view->natural_height, max_height));
674 container_set_geometry_from_content(con); 732 container_set_geometry_from_content(con);
675 } 733 }
676} 734}
677 735
678void container_floating_resize_and_center(struct sway_container *con) { 736void container_floating_resize_and_center(struct sway_container *con) {
679 struct sway_workspace *ws = con->workspace; 737 struct sway_workspace *ws = con->pending.workspace;
680 if (!ws) { 738 if (!ws) {
681 // On scratchpad, just resize 739 // On scratchpad, just resize
682 floating_natural_resize(con); 740 floating_natural_resize(con);
683 return; 741 return;
684 } 742 }
685 743
686 struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, 744 struct wlr_box ob;
687 ws->output->wlr_output); 745 wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob);
688 if (!ob) { 746 if (wlr_box_empty(&ob)) {
689 // On NOOP output. Will be called again when moved to an output 747 // On NOOP output. Will be called again when moved to an output
690 con->x = 0; 748 con->pending.x = 0;
691 con->y = 0; 749 con->pending.y = 0;
692 con->width = 0; 750 con->pending.width = 0;
693 con->height = 0; 751 con->pending.height = 0;
694 return; 752 return;
695 } 753 }
696 754
697 floating_natural_resize(con); 755 floating_natural_resize(con);
698 if (!con->view) { 756 if (!con->view) {
699 if (con->width > ws->width || con->height > ws->height) { 757 if (con->pending.width > ws->width || con->pending.height > ws->height) {
700 con->x = ob->x + (ob->width - con->width) / 2; 758 con->pending.x = ob.x + (ob.width - con->pending.width) / 2;
701 con->y = ob->y + (ob->height - con->height) / 2; 759 con->pending.y = ob.y + (ob.height - con->pending.height) / 2;
702 } else { 760 } else {
703 con->x = ws->x + (ws->width - con->width) / 2; 761 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
704 con->y = ws->y + (ws->height - con->height) / 2; 762 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
705 } 763 }
706 } else { 764 } else {
707 if (con->content_width > ws->width 765 if (con->pending.content_width > ws->width
708 || con->content_height > ws->height) { 766 || con->pending.content_height > ws->height) {
709 con->content_x = ob->x + (ob->width - con->content_width) / 2; 767 con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2;
710 con->content_y = ob->y + (ob->height - con->content_height) / 2; 768 con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2;
711 } else { 769 } else {
712 con->content_x = ws->x + (ws->width - con->content_width) / 2; 770 con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2;
713 con->content_y = ws->y + (ws->height - con->content_height) / 2; 771 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
714 } 772 }
715 773
716 // If the view's border is B_NONE then these properties are ignored. 774 // If the view's border is B_NONE then these properties are ignored.
717 con->border_top = con->border_bottom = true; 775 con->pending.border_top = con->pending.border_bottom = true;
718 con->border_left = con->border_right = true; 776 con->pending.border_left = con->pending.border_right = true;
719 777
720 container_set_geometry_from_content(con); 778 container_set_geometry_from_content(con);
721 } 779 }
722} 780}
723 781
724void container_floating_set_default_size(struct sway_container *con) { 782void container_floating_set_default_size(struct sway_container *con) {
725 if (!sway_assert(con->workspace, "Expected a container on a workspace")) { 783 if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) {
726 return; 784 return;
727 } 785 }
728 786
@@ -730,16 +788,16 @@ void container_floating_set_default_size(struct sway_container *con) {
730 floating_calculate_constraints(&min_width, &max_width, 788 floating_calculate_constraints(&min_width, &max_width,
731 &min_height, &max_height); 789 &min_height, &max_height);
732 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 790 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
733 workspace_get_box(con->workspace, box); 791 workspace_get_box(con->pending.workspace, box);
734 792
735 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 793 double width = fmax(min_width, fmin(box->width * 0.5, max_width));
736 double height = fmax(min_height, fmin(box->height * 0.75, max_height)); 794 double height = fmax(min_height, fmin(box->height * 0.75, max_height));
737 if (!con->view) { 795 if (!con->view) {
738 con->width = width; 796 con->pending.width = width;
739 con->height = height; 797 con->pending.height = height;
740 } else { 798 } else {
741 con->content_width = width; 799 con->pending.content_width = width;
742 con->content_height = height; 800 con->pending.content_height = height;
743 container_set_geometry_from_content(con); 801 container_set_geometry_from_content(con);
744 } 802 }
745 803
@@ -761,8 +819,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) {
761 con->view->impl->set_resizing(con->view, resizing); 819 con->view->impl->set_resizing(con->view, resizing);
762 } 820 }
763 } else { 821 } else {
764 for (int i = 0; i < con->children->length; ++i ) { 822 for (int i = 0; i < con->pending.children->length; ++i ) {
765 struct sway_container *child = con->children->items[i]; 823 struct sway_container *child = con->pending.children->items[i];
766 container_set_resizing(child, resizing); 824 container_set_resizing(child, resizing);
767 } 825 }
768 } 826 }
@@ -774,21 +832,33 @@ void container_set_floating(struct sway_container *container, bool enable) {
774 } 832 }
775 833
776 struct sway_seat *seat = input_manager_current_seat(); 834 struct sway_seat *seat = input_manager_current_seat();
777 struct sway_workspace *workspace = container->workspace; 835 struct sway_workspace *workspace = container->pending.workspace;
836 struct sway_container *focus = seat_get_focused_container(seat);
837 bool set_focus = focus == container;
778 838
779 if (enable) { 839 if (enable) {
780 struct sway_container *old_parent = container->parent; 840 struct sway_container *old_parent = container->pending.parent;
781 container_detach(container); 841 container_detach(container);
782 workspace_add_floating(workspace, container); 842 workspace_add_floating(workspace, container);
783 if (container->view) { 843 if (container->view) {
784 view_set_tiled(container->view, false); 844 view_set_tiled(container->view, false);
785 if (container->view->using_csd) { 845 if (container->view->using_csd) {
786 container->border = B_CSD; 846 container->saved_border = container->pending.border;
847 container->pending.border = B_CSD;
848 if (container->view->xdg_decoration) {
849 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
850 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
851 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
852 }
787 } 853 }
788 } 854 }
789 container_floating_set_default_size(container); 855 container_floating_set_default_size(container);
790 container_floating_resize_and_center(container); 856 container_floating_resize_and_center(container);
791 if (old_parent) { 857 if (old_parent) {
858 if (set_focus) {
859 seat_set_raw_focus(seat, &old_parent->node);
860 seat_set_raw_focus(seat, &container->node);
861 }
792 container_reap_empty(old_parent); 862 container_reap_empty(old_parent);
793 } 863 }
794 } else { 864 } else {
@@ -800,19 +870,28 @@ void container_set_floating(struct sway_container *container, bool enable) {
800 struct sway_container *reference = 870 struct sway_container *reference =
801 seat_get_focus_inactive_tiling(seat, workspace); 871 seat_get_focus_inactive_tiling(seat, workspace);
802 if (reference) { 872 if (reference) {
803 container_add_sibling(reference, container, 1); 873 if (reference->view) {
804 container->width = reference->width; 874 container_add_sibling(reference, container, 1);
805 container->height = reference->height; 875 } else {
876 container_add_child(reference, container);
877 }
878 container->pending.width = reference->pending.width;
879 container->pending.height = reference->pending.height;
806 } else { 880 } else {
807 struct sway_container *other = 881 struct sway_container *other =
808 workspace_add_tiling(workspace, container); 882 workspace_add_tiling(workspace, container);
809 other->width = workspace->width; 883 other->pending.width = workspace->width;
810 other->height = workspace->height; 884 other->pending.height = workspace->height;
811 } 885 }
812 if (container->view) { 886 if (container->view) {
813 view_set_tiled(container->view, true); 887 view_set_tiled(container->view, true);
814 if (container->view->using_csd) { 888 if (container->view->using_csd) {
815 container->border = container->saved_border; 889 container->pending.border = container->saved_border;
890 if (container->view->xdg_decoration) {
891 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
892 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
893 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
894 }
816 } 895 }
817 } 896 }
818 container->width_fraction = 0; 897 container->width_fraction = 0;
@@ -834,22 +913,33 @@ void container_set_geometry_from_content(struct sway_container *con) {
834 size_t border_width = 0; 913 size_t border_width = 0;
835 size_t top = 0; 914 size_t top = 0;
836 915
837 if (con->border != B_CSD) { 916 if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) {
838 border_width = con->border_thickness * (con->border != B_NONE); 917 border_width = con->pending.border_thickness * (con->pending.border != B_NONE);
839 top = con->border == B_NORMAL ? 918 top = con->pending.border == B_NORMAL ?
840 container_titlebar_height() : border_width; 919 container_titlebar_height() : border_width;
841 } 920 }
842 921
843 con->x = con->content_x - border_width; 922 con->pending.x = con->pending.content_x - border_width;
844 con->y = con->content_y - top; 923 con->pending.y = con->pending.content_y - top;
845 con->width = con->content_width + border_width * 2; 924 con->pending.width = con->pending.content_width + border_width * 2;
846 con->height = top + con->content_height + border_width; 925 con->pending.height = top + con->pending.content_height + border_width;
847 node_set_dirty(&con->node); 926 node_set_dirty(&con->node);
848} 927}
849 928
850bool container_is_floating(struct sway_container *container) { 929bool container_is_floating(struct sway_container *container) {
851 if (!container->parent && container->workspace && 930 if (!container->pending.parent && container->pending.workspace &&
852 list_find(container->workspace->floating, container) != -1) { 931 list_find(container->pending.workspace->floating, container) != -1) {
932 return true;
933 }
934 if (container->scratchpad) {
935 return true;
936 }
937 return false;
938}
939
940bool container_is_current_floating(struct sway_container *container) {
941 if (!container->current.parent && container->current.workspace &&
942 list_find(container->current.workspace->floating, container) != -1) {
853 return true; 943 return true;
854 } 944 }
855 if (container->scratchpad) { 945 if (container->scratchpad) {
@@ -859,10 +949,10 @@ bool container_is_floating(struct sway_container *container) {
859} 949}
860 950
861void container_get_box(struct sway_container *container, struct wlr_box *box) { 951void container_get_box(struct sway_container *container, struct wlr_box *box) {
862 box->x = container->x; 952 box->x = container->pending.x;
863 box->y = container->y; 953 box->y = container->pending.y;
864 box->width = container->width; 954 box->width = container->pending.width;
865 box->height = container->height; 955 box->height = container->pending.height;
866} 956}
867 957
868/** 958/**
@@ -870,14 +960,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
870 */ 960 */
871void container_floating_translate(struct sway_container *con, 961void container_floating_translate(struct sway_container *con,
872 double x_amount, double y_amount) { 962 double x_amount, double y_amount) {
873 con->x += x_amount; 963 con->pending.x += x_amount;
874 con->y += y_amount; 964 con->pending.y += y_amount;
875 con->content_x += x_amount; 965 con->pending.content_x += x_amount;
876 con->content_y += y_amount; 966 con->pending.content_y += y_amount;
877 967
878 if (con->children) { 968 if (con->pending.children) {
879 for (int i = 0; i < con->children->length; ++i) { 969 for (int i = 0; i < con->pending.children->length; ++i) {
880 struct sway_container *child = con->children->items[i]; 970 struct sway_container *child = con->pending.children->items[i];
881 container_floating_translate(child, x_amount, y_amount); 971 container_floating_translate(child, x_amount, y_amount);
882 } 972 }
883 } 973 }
@@ -893,8 +983,8 @@ void container_floating_translate(struct sway_container *con,
893 * center. 983 * center.
894 */ 984 */
895struct sway_output *container_floating_find_output(struct sway_container *con) { 985struct sway_output *container_floating_find_output(struct sway_container *con) {
896 double center_x = con->x + con->width / 2; 986 double center_x = con->pending.x + con->pending.width / 2;
897 double center_y = con->y + con->height / 2; 987 double center_y = con->pending.y + con->pending.height / 2;
898 struct sway_output *closest_output = NULL; 988 struct sway_output *closest_output = NULL;
899 double closest_distance = DBL_MAX; 989 double closest_distance = DBL_MAX;
900 for (int i = 0; i < root->outputs->length; ++i) { 990 for (int i = 0; i < root->outputs->length; ++i) {
@@ -925,11 +1015,11 @@ void container_floating_move_to(struct sway_container *con,
925 "Expected a floating container")) { 1015 "Expected a floating container")) {
926 return; 1016 return;
927 } 1017 }
928 container_floating_translate(con, lx - con->x, ly - con->y); 1018 container_floating_translate(con, lx - con->pending.x, ly - con->pending.y);
929 if (container_is_scratchpad_hidden(con)) { 1019 if (container_is_scratchpad_hidden(con)) {
930 return; 1020 return;
931 } 1021 }
932 struct sway_workspace *old_workspace = con->workspace; 1022 struct sway_workspace *old_workspace = con->pending.workspace;
933 struct sway_output *new_output = container_floating_find_output(con); 1023 struct sway_output *new_output = container_floating_find_output(con);
934 if (!sway_assert(new_output, "Unable to find any output")) { 1024 if (!sway_assert(new_output, "Unable to find any output")) {
935 return; 1025 return;
@@ -951,10 +1041,10 @@ void container_floating_move_to_center(struct sway_container *con) {
951 "Expected a floating container")) { 1041 "Expected a floating container")) {
952 return; 1042 return;
953 } 1043 }
954 struct sway_workspace *ws = con->workspace; 1044 struct sway_workspace *ws = con->pending.workspace;
955 double new_lx = ws->x + (ws->width - con->width) / 2; 1045 double new_lx = ws->x + (ws->width - con->pending.width) / 2;
956 double new_ly = ws->y + (ws->height - con->height) / 2; 1046 double new_ly = ws->y + (ws->height - con->pending.height) / 2;
957 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 1047 container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y);
958} 1048}
959 1049
960static bool find_urgent_iterator(struct sway_container *con, void *data) { 1050static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -972,42 +1062,118 @@ void container_end_mouse_operation(struct sway_container *container) {
972 } 1062 }
973} 1063}
974 1064
975static void set_fullscreen_iterator(struct sway_container *con, void *data) { 1065static bool devid_from_fd(int fd, dev_t *devid) {
1066 struct stat stat;
1067 if (fstat(fd, &stat) != 0) {
1068 sway_log_errno(SWAY_ERROR, "fstat failed");
1069 return false;
1070 }
1071 *devid = stat.st_rdev;
1072 return true;
1073}
1074
1075static void set_fullscreen(struct sway_container *con, bool enable) {
976 if (!con->view) { 1076 if (!con->view) {
977 return; 1077 return;
978 } 1078 }
979 if (con->view->impl->set_fullscreen) { 1079 if (con->view->impl->set_fullscreen) {
980 bool *enable = data; 1080 con->view->impl->set_fullscreen(con->view, enable);
981 con->view->impl->set_fullscreen(con->view, *enable);
982 if (con->view->foreign_toplevel) { 1081 if (con->view->foreign_toplevel) {
983 wlr_foreign_toplevel_handle_v1_set_fullscreen( 1082 wlr_foreign_toplevel_handle_v1_set_fullscreen(
984 con->view->foreign_toplevel, *enable); 1083 con->view->foreign_toplevel, enable);
985 } 1084 }
986 } 1085 }
1086
1087 if (!server.linux_dmabuf_v1 || !con->view->surface) {
1088 return;
1089 }
1090 if (!enable) {
1091 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1092 con->view->surface, NULL);
1093 return;
1094 }
1095
1096 if (!con->pending.workspace || !con->pending.workspace->output) {
1097 return;
1098 }
1099
1100 struct sway_output *output = con->pending.workspace->output;
1101 struct wlr_output *wlr_output = output->wlr_output;
1102
1103 // TODO: add wlroots helpers for all of this stuff
1104
1105 const struct wlr_drm_format_set *renderer_formats =
1106 wlr_renderer_get_dmabuf_texture_formats(server.renderer);
1107 assert(renderer_formats);
1108
1109 int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
1110 int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
1111 if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
1112 return;
1113 }
1114
1115 dev_t render_dev, scanout_dev;
1116 if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
1117 !devid_from_fd(backend_drm_fd, &scanout_dev)) {
1118 return;
1119 }
1120
1121 const struct wlr_drm_format_set *output_formats =
1122 wlr_output_get_primary_formats(output->wlr_output,
1123 WLR_BUFFER_CAP_DMABUF);
1124 if (!output_formats) {
1125 return;
1126 }
1127
1128 struct wlr_drm_format_set scanout_formats = {0};
1129 if (!wlr_drm_format_set_intersect(&scanout_formats,
1130 output_formats, renderer_formats)) {
1131 return;
1132 }
1133
1134 struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
1135 {
1136 .target_device = scanout_dev,
1137 .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
1138 .formats = &scanout_formats,
1139 },
1140 {
1141 .target_device = render_dev,
1142 .formats = renderer_formats,
1143 },
1144 };
1145
1146 const struct wlr_linux_dmabuf_feedback_v1 feedback = {
1147 .main_device = render_dev,
1148 .tranches = tranches,
1149 .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
1150 };
1151 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1152 con->view->surface, &feedback);
1153
1154 wlr_drm_format_set_finish(&scanout_formats);
987} 1155}
988 1156
989static void container_fullscreen_workspace(struct sway_container *con) { 1157static void container_fullscreen_workspace(struct sway_container *con) {
990 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1158 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
991 "Expected a non-fullscreen container")) { 1159 "Expected a non-fullscreen container")) {
992 return; 1160 return;
993 } 1161 }
994 bool enable = true; 1162 set_fullscreen(con, true);
995 set_fullscreen_iterator(con, &enable); 1163 con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;
996 container_for_each_child(con, set_fullscreen_iterator, &enable);
997 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
998 1164
999 con->saved_x = con->x; 1165 con->saved_x = con->pending.x;
1000 con->saved_y = con->y; 1166 con->saved_y = con->pending.y;
1001 con->saved_width = con->width; 1167 con->saved_width = con->pending.width;
1002 con->saved_height = con->height; 1168 con->saved_height = con->pending.height;
1003 1169
1004 if (con->workspace) { 1170 if (con->pending.workspace) {
1005 con->workspace->fullscreen = con; 1171 con->pending.workspace->fullscreen = con;
1006 struct sway_seat *seat; 1172 struct sway_seat *seat;
1007 struct sway_workspace *focus_ws; 1173 struct sway_workspace *focus_ws;
1008 wl_list_for_each(seat, &server.input->seats, link) { 1174 wl_list_for_each(seat, &server.input->seats, link) {
1009 focus_ws = seat_get_focused_workspace(seat); 1175 focus_ws = seat_get_focused_workspace(seat);
1010 if (focus_ws == con->workspace) { 1176 if (focus_ws == con->pending.workspace) {
1011 seat_set_focus_container(seat, con); 1177 seat_set_focus_container(seat, con);
1012 } else { 1178 } else {
1013 struct sway_node *focus = 1179 struct sway_node *focus =
@@ -1023,19 +1189,17 @@ static void container_fullscreen_workspace(struct sway_container *con) {
1023} 1189}
1024 1190
1025static void container_fullscreen_global(struct sway_container *con) { 1191static void container_fullscreen_global(struct sway_container *con) {
1026 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1192 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
1027 "Expected a non-fullscreen container")) { 1193 "Expected a non-fullscreen container")) {
1028 return; 1194 return;
1029 } 1195 }
1030 bool enable = true; 1196 set_fullscreen(con, true);
1031 set_fullscreen_iterator(con, &enable);
1032 container_for_each_child(con, set_fullscreen_iterator, &enable);
1033 1197
1034 root->fullscreen_global = con; 1198 root->fullscreen_global = con;
1035 con->saved_x = con->x; 1199 con->saved_x = con->pending.x;
1036 con->saved_y = con->y; 1200 con->saved_y = con->pending.y;
1037 con->saved_width = con->width; 1201 con->saved_width = con->pending.width;
1038 con->saved_height = con->height; 1202 con->saved_height = con->pending.height;
1039 1203
1040 struct sway_seat *seat; 1204 struct sway_seat *seat;
1041 wl_list_for_each(seat, &server.input->seats, link) { 1205 wl_list_for_each(seat, &server.input->seats, link) {
@@ -1045,34 +1209,32 @@ static void container_fullscreen_global(struct sway_container *con) {
1045 } 1209 }
1046 } 1210 }
1047 1211
1048 con->fullscreen_mode = FULLSCREEN_GLOBAL; 1212 con->pending.fullscreen_mode = FULLSCREEN_GLOBAL;
1049 container_end_mouse_operation(con); 1213 container_end_mouse_operation(con);
1050 ipc_event_window(con, "fullscreen_mode"); 1214 ipc_event_window(con, "fullscreen_mode");
1051} 1215}
1052 1216
1053void container_fullscreen_disable(struct sway_container *con) { 1217void container_fullscreen_disable(struct sway_container *con) {
1054 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, 1218 if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,
1055 "Expected a fullscreen container")) { 1219 "Expected a fullscreen container")) {
1056 return; 1220 return;
1057 } 1221 }
1058 bool enable = false; 1222 set_fullscreen(con, false);
1059 set_fullscreen_iterator(con, &enable);
1060 container_for_each_child(con, set_fullscreen_iterator, &enable);
1061 1223
1062 if (container_is_floating(con)) { 1224 if (container_is_floating(con)) {
1063 con->x = con->saved_x; 1225 con->pending.x = con->saved_x;
1064 con->y = con->saved_y; 1226 con->pending.y = con->saved_y;
1065 con->width = con->saved_width; 1227 con->pending.width = con->saved_width;
1066 con->height = con->saved_height; 1228 con->pending.height = con->saved_height;
1067 } 1229 }
1068 1230
1069 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1231 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1070 if (con->workspace) { 1232 if (con->pending.workspace) {
1071 con->workspace->fullscreen = NULL; 1233 con->pending.workspace->fullscreen = NULL;
1072 if (container_is_floating(con)) { 1234 if (container_is_floating(con)) {
1073 struct sway_output *output = 1235 struct sway_output *output =
1074 container_floating_find_output(con); 1236 container_floating_find_output(con);
1075 if (con->workspace->output != output) { 1237 if (con->pending.workspace->output != output) {
1076 container_floating_move_to_center(con); 1238 container_floating_move_to_center(con);
1077 } 1239 }
1078 } 1240 }
@@ -1084,11 +1246,11 @@ void container_fullscreen_disable(struct sway_container *con) {
1084 // If the container was mapped as fullscreen and set as floating by 1246 // If the container was mapped as fullscreen and set as floating by
1085 // criteria, it needs to be reinitialized as floating to get the proper 1247 // criteria, it needs to be reinitialized as floating to get the proper
1086 // size and location 1248 // size and location
1087 if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { 1249 if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {
1088 container_floating_resize_and_center(con); 1250 container_floating_resize_and_center(con);
1089 } 1251 }
1090 1252
1091 con->fullscreen_mode = FULLSCREEN_NONE; 1253 con->pending.fullscreen_mode = FULLSCREEN_NONE;
1092 container_end_mouse_operation(con); 1254 container_end_mouse_operation(con);
1093 ipc_event_window(con, "fullscreen_mode"); 1255 ipc_event_window(con, "fullscreen_mode");
1094 1256
@@ -1106,7 +1268,7 @@ void container_fullscreen_disable(struct sway_container *con) {
1106 1268
1107void container_set_fullscreen(struct sway_container *con, 1269void container_set_fullscreen(struct sway_container *con,
1108 enum sway_fullscreen_mode mode) { 1270 enum sway_fullscreen_mode mode) {
1109 if (con->fullscreen_mode == mode) { 1271 if (con->pending.fullscreen_mode == mode) {
1110 return; 1272 return;
1111 } 1273 }
1112 1274
@@ -1118,8 +1280,8 @@ void container_set_fullscreen(struct sway_container *con,
1118 if (root->fullscreen_global) { 1280 if (root->fullscreen_global) {
1119 container_fullscreen_disable(root->fullscreen_global); 1281 container_fullscreen_disable(root->fullscreen_global);
1120 } 1282 }
1121 if (con->workspace && con->workspace->fullscreen) { 1283 if (con->pending.workspace && con->pending.workspace->fullscreen) {
1122 container_fullscreen_disable(con->workspace->fullscreen); 1284 container_fullscreen_disable(con->pending.workspace->fullscreen);
1123 } 1285 }
1124 container_fullscreen_workspace(con); 1286 container_fullscreen_workspace(con);
1125 break; 1287 break;
@@ -1127,7 +1289,7 @@ void container_set_fullscreen(struct sway_container *con,
1127 if (root->fullscreen_global) { 1289 if (root->fullscreen_global) {
1128 container_fullscreen_disable(root->fullscreen_global); 1290 container_fullscreen_disable(root->fullscreen_global);
1129 } 1291 }
1130 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1292 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1131 container_fullscreen_disable(con); 1293 container_fullscreen_disable(con);
1132 } 1294 }
1133 container_fullscreen_global(con); 1295 container_fullscreen_global(con);
@@ -1137,8 +1299,8 @@ void container_set_fullscreen(struct sway_container *con,
1137 1299
1138struct sway_container *container_toplevel_ancestor( 1300struct sway_container *container_toplevel_ancestor(
1139 struct sway_container *container) { 1301 struct sway_container *container) {
1140 while (container->parent) { 1302 while (container->pending.parent) {
1141 container = container->parent; 1303 container = container->pending.parent;
1142 } 1304 }
1143 1305
1144 return container; 1306 return container;
@@ -1150,10 +1312,10 @@ bool container_is_floating_or_child(struct sway_container *container) {
1150 1312
1151bool container_is_fullscreen_or_child(struct sway_container *container) { 1313bool container_is_fullscreen_or_child(struct sway_container *container) {
1152 do { 1314 do {
1153 if (container->fullscreen_mode) { 1315 if (container->pending.fullscreen_mode) {
1154 return true; 1316 return true;
1155 } 1317 }
1156 container = container->parent; 1318 container = container->pending.parent;
1157 } while (container); 1319 } while (container);
1158 1320
1159 return false; 1321 return false;
@@ -1226,11 +1388,11 @@ void container_discover_outputs(struct sway_container *con) {
1226} 1388}
1227 1389
1228enum sway_container_layout container_parent_layout(struct sway_container *con) { 1390enum sway_container_layout container_parent_layout(struct sway_container *con) {
1229 if (con->parent) { 1391 if (con->pending.parent) {
1230 return con->parent->layout; 1392 return con->pending.parent->pending.layout;
1231 } 1393 }
1232 if (con->workspace) { 1394 if (con->pending.workspace) {
1233 return con->workspace->layout; 1395 return con->pending.workspace->layout;
1234 } 1396 }
1235 return L_NONE; 1397 return L_NONE;
1236} 1398}
@@ -1244,16 +1406,16 @@ enum sway_container_layout container_current_parent_layout(
1244} 1406}
1245 1407
1246list_t *container_get_siblings(struct sway_container *container) { 1408list_t *container_get_siblings(struct sway_container *container) {
1247 if (container->parent) { 1409 if (container->pending.parent) {
1248 return container->parent->children; 1410 return container->pending.parent->pending.children;
1249 } 1411 }
1250 if (container_is_scratchpad_hidden(container)) { 1412 if (container_is_scratchpad_hidden(container)) {
1251 return NULL; 1413 return NULL;
1252 } 1414 }
1253 if (list_find(container->workspace->tiling, container) != -1) { 1415 if (list_find(container->pending.workspace->tiling, container) != -1) {
1254 return container->workspace->tiling; 1416 return container->pending.workspace->tiling;
1255 } 1417 }
1256 return container->workspace->floating; 1418 return container->pending.workspace->floating;
1257} 1419}
1258 1420
1259int container_sibling_index(struct sway_container *child) { 1421int container_sibling_index(struct sway_container *child) {
@@ -1268,30 +1430,30 @@ list_t *container_get_current_siblings(struct sway_container *container) {
1268} 1430}
1269 1431
1270void container_handle_fullscreen_reparent(struct sway_container *con) { 1432void container_handle_fullscreen_reparent(struct sway_container *con) {
1271 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || 1433 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1272 con->workspace->fullscreen == con) { 1434 con->pending.workspace->fullscreen == con) {
1273 return; 1435 return;
1274 } 1436 }
1275 if (con->workspace->fullscreen) { 1437 if (con->pending.workspace->fullscreen) {
1276 container_fullscreen_disable(con->workspace->fullscreen); 1438 container_fullscreen_disable(con->pending.workspace->fullscreen);
1277 } 1439 }
1278 con->workspace->fullscreen = con; 1440 con->pending.workspace->fullscreen = con;
1279 1441
1280 arrange_workspace(con->workspace); 1442 arrange_workspace(con->pending.workspace);
1281} 1443}
1282 1444
1283static void set_workspace(struct sway_container *container, void *data) { 1445static void set_workspace(struct sway_container *container, void *data) {
1284 container->workspace = container->parent->workspace; 1446 container->pending.workspace = container->pending.parent->pending.workspace;
1285} 1447}
1286 1448
1287void container_insert_child(struct sway_container *parent, 1449void container_insert_child(struct sway_container *parent,
1288 struct sway_container *child, int i) { 1450 struct sway_container *child, int i) {
1289 if (child->workspace) { 1451 if (child->pending.workspace) {
1290 container_detach(child); 1452 container_detach(child);
1291 } 1453 }
1292 list_insert(parent->children, i, child); 1454 list_insert(parent->pending.children, i, child);
1293 child->parent = parent; 1455 child->pending.parent = parent;
1294 child->workspace = parent->workspace; 1456 child->pending.workspace = parent->pending.workspace;
1295 container_for_each_child(child, set_workspace, NULL); 1457 container_for_each_child(child, set_workspace, NULL);
1296 container_handle_fullscreen_reparent(child); 1458 container_handle_fullscreen_reparent(child);
1297 container_update_representation(parent); 1459 container_update_representation(parent);
@@ -1299,14 +1461,14 @@ void container_insert_child(struct sway_container *parent,
1299 1461
1300void container_add_sibling(struct sway_container *fixed, 1462void container_add_sibling(struct sway_container *fixed,
1301 struct sway_container *active, bool after) { 1463 struct sway_container *active, bool after) {
1302 if (active->workspace) { 1464 if (active->pending.workspace) {
1303 container_detach(active); 1465 container_detach(active);
1304 } 1466 }
1305 list_t *siblings = container_get_siblings(fixed); 1467 list_t *siblings = container_get_siblings(fixed);
1306 int index = list_find(siblings, fixed); 1468 int index = list_find(siblings, fixed);
1307 list_insert(siblings, index + after, active); 1469 list_insert(siblings, index + after, active);
1308 active->parent = fixed->parent; 1470 active->pending.parent = fixed->pending.parent;
1309 active->workspace = fixed->workspace; 1471 active->pending.workspace = fixed->pending.workspace;
1310 container_for_each_child(active, set_workspace, NULL); 1472 container_for_each_child(active, set_workspace, NULL);
1311 container_handle_fullscreen_reparent(active); 1473 container_handle_fullscreen_reparent(active);
1312 container_update_representation(active); 1474 container_update_representation(active);
@@ -1314,17 +1476,13 @@ void container_add_sibling(struct sway_container *fixed,
1314 1476
1315void container_add_child(struct sway_container *parent, 1477void container_add_child(struct sway_container *parent,
1316 struct sway_container *child) { 1478 struct sway_container *child) {
1317 if (child->workspace) { 1479 if (child->pending.workspace) {
1318 container_detach(child); 1480 container_detach(child);
1319 } 1481 }
1320 list_add(parent->children, child); 1482 list_add(parent->pending.children, child);
1321 child->parent = parent; 1483 child->pending.parent = parent;
1322 child->workspace = parent->workspace; 1484 child->pending.workspace = parent->pending.workspace;
1323 container_for_each_child(child, set_workspace, NULL); 1485 container_for_each_child(child, set_workspace, NULL);
1324 bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE ||
1325 parent->fullscreen_mode != FULLSCREEN_NONE;
1326 set_fullscreen_iterator(child, &fullscreen);
1327 container_for_each_child(child, set_fullscreen_iterator, &fullscreen);
1328 container_handle_fullscreen_reparent(child); 1486 container_handle_fullscreen_reparent(child);
1329 container_update_representation(parent); 1487 container_update_representation(parent);
1330 node_set_dirty(&child->node); 1488 node_set_dirty(&child->node);
@@ -1332,15 +1490,15 @@ void container_add_child(struct sway_container *parent,
1332} 1490}
1333 1491
1334void container_detach(struct sway_container *child) { 1492void container_detach(struct sway_container *child) {
1335 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1493 if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1336 child->workspace->fullscreen = NULL; 1494 child->pending.workspace->fullscreen = NULL;
1337 } 1495 }
1338 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { 1496 if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1339 root->fullscreen_global = NULL; 1497 root->fullscreen_global = NULL;
1340 } 1498 }
1341 1499
1342 struct sway_container *old_parent = child->parent; 1500 struct sway_container *old_parent = child->pending.parent;
1343 struct sway_workspace *old_workspace = child->workspace; 1501 struct sway_workspace *old_workspace = child->pending.workspace;
1344 list_t *siblings = container_get_siblings(child); 1502 list_t *siblings = container_get_siblings(child);
1345 if (siblings) { 1503 if (siblings) {
1346 int index = list_find(siblings, child); 1504 int index = list_find(siblings, child);
@@ -1348,8 +1506,8 @@ void container_detach(struct sway_container *child) {
1348 list_del(siblings, index); 1506 list_del(siblings, index);
1349 } 1507 }
1350 } 1508 }
1351 child->parent = NULL; 1509 child->pending.parent = NULL;
1352 child->workspace = NULL; 1510 child->pending.workspace = NULL;
1353 container_for_each_child(child, set_workspace, NULL); 1511 container_for_each_child(child, set_workspace, NULL);
1354 1512
1355 if (old_parent) { 1513 if (old_parent) {
@@ -1364,18 +1522,18 @@ void container_detach(struct sway_container *child) {
1364 1522
1365void container_replace(struct sway_container *container, 1523void container_replace(struct sway_container *container,
1366 struct sway_container *replacement) { 1524 struct sway_container *replacement) {
1367 enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; 1525 enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode;
1368 bool scratchpad = container->scratchpad; 1526 bool scratchpad = container->scratchpad;
1369 struct sway_workspace *ws = NULL; 1527 struct sway_workspace *ws = NULL;
1370 if (fullscreen != FULLSCREEN_NONE) { 1528 if (fullscreen != FULLSCREEN_NONE) {
1371 container_fullscreen_disable(container); 1529 container_fullscreen_disable(container);
1372 } 1530 }
1373 if (scratchpad) { 1531 if (scratchpad) {
1374 ws = container->workspace; 1532 ws = container->pending.workspace;
1375 root_scratchpad_show(container); 1533 root_scratchpad_show(container);
1376 root_scratchpad_remove_container(container); 1534 root_scratchpad_remove_container(container);
1377 } 1535 }
1378 if (container->parent || container->workspace) { 1536 if (container->pending.parent || container->pending.workspace) {
1379 float width_fraction = container->width_fraction; 1537 float width_fraction = container->width_fraction;
1380 float height_fraction = container->height_fraction; 1538 float height_fraction = container->height_fraction;
1381 container_add_sibling(container, replacement, 1); 1539 container_add_sibling(container, replacement, 1);
@@ -1403,7 +1561,7 @@ struct sway_container *container_split(struct sway_container *child,
1403 enum sway_container_layout layout) { 1561 enum sway_container_layout layout) {
1404 // i3 doesn't split singleton H/V containers 1562 // i3 doesn't split singleton H/V containers
1405 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 1563 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354
1406 if (child->parent || child->workspace) { 1564 if (child->pending.parent || child->pending.workspace) {
1407 list_t *siblings = container_get_siblings(child); 1565 list_t *siblings = container_get_siblings(child);
1408 if (siblings->length == 1) { 1566 if (siblings->length == 1) {
1409 enum sway_container_layout current = container_parent_layout(child); 1567 enum sway_container_layout current = container_parent_layout(child);
@@ -1411,12 +1569,12 @@ struct sway_container *container_split(struct sway_container *child,
1411 current = L_NONE; 1569 current = L_NONE;
1412 } 1570 }
1413 if (current == L_HORIZ || current == L_VERT) { 1571 if (current == L_HORIZ || current == L_VERT) {
1414 if (child->parent) { 1572 if (child->pending.parent) {
1415 child->parent->layout = layout; 1573 child->pending.parent->pending.layout = layout;
1416 container_update_representation(child->parent); 1574 container_update_representation(child->pending.parent);
1417 } else { 1575 } else {
1418 child->workspace->layout = layout; 1576 child->pending.workspace->layout = layout;
1419 workspace_update_representation(child->workspace); 1577 workspace_update_representation(child->pending.workspace);
1420 } 1578 }
1421 return child; 1579 return child;
1422 } 1580 }
@@ -1429,25 +1587,25 @@ struct sway_container *container_split(struct sway_container *child,
1429 if (container_is_floating(child) && child->view) { 1587 if (container_is_floating(child) && child->view) {
1430 view_set_tiled(child->view, true); 1588 view_set_tiled(child->view, true);
1431 if (child->view->using_csd) { 1589 if (child->view->using_csd) {
1432 child->border = child->saved_border; 1590 child->pending.border = child->saved_border;
1433 } 1591 }
1434 } 1592 }
1435 1593
1436 struct sway_container *cont = container_create(NULL); 1594 struct sway_container *cont = container_create(NULL);
1437 cont->width = child->width; 1595 cont->pending.width = child->pending.width;
1438 cont->height = child->height; 1596 cont->pending.height = child->pending.height;
1439 cont->width_fraction = child->width_fraction; 1597 cont->width_fraction = child->width_fraction;
1440 cont->height_fraction = child->height_fraction; 1598 cont->height_fraction = child->height_fraction;
1441 cont->x = child->x; 1599 cont->pending.x = child->pending.x;
1442 cont->y = child->y; 1600 cont->pending.y = child->pending.y;
1443 cont->layout = layout; 1601 cont->pending.layout = layout;
1444 1602
1445 container_replace(child, cont); 1603 container_replace(child, cont);
1446 container_add_child(cont, child); 1604 container_add_child(cont, child);
1447 1605
1448 if (set_focus) { 1606 if (set_focus) {
1449 seat_set_raw_focus(seat, &cont->node); 1607 seat_set_raw_focus(seat, &cont->node);
1450 if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { 1608 if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1451 seat_set_focus(seat, &child->node); 1609 seat_set_focus(seat, &child->node);
1452 } else { 1610 } else {
1453 seat_set_raw_focus(seat, &child->node); 1611 seat_set_raw_focus(seat, &child->node);
@@ -1548,45 +1706,14 @@ static void update_marks_texture(struct sway_container *con,
1548 for (int i = 0; i < con->marks->length; ++i) { 1706 for (int i = 0; i < con->marks->length; ++i) {
1549 char *mark = con->marks->items[i]; 1707 char *mark = con->marks->items[i];
1550 if (mark[0] != '_') { 1708 if (mark[0] != '_') {
1551 sprintf(part, "[%s]", mark); 1709 snprintf(part, len + 1, "[%s]", mark);
1552 strcat(buffer, part); 1710 strcat(buffer, part);
1553 } 1711 }
1554 } 1712 }
1555 free(part); 1713 free(part);
1556 1714
1557 double scale = output->wlr_output->scale; 1715 render_titlebar_text_texture(output, con, texture, class, false, buffer);
1558 int width = 0;
1559 int height = con->title_height * scale;
1560
1561 cairo_t *c = cairo_create(NULL);
1562 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1563 "%s", buffer);
1564 cairo_destroy(c);
1565 1716
1566 cairo_surface_t *surface = cairo_image_surface_create(
1567 CAIRO_FORMAT_ARGB32, width, height);
1568 cairo_t *cairo = cairo_create(surface);
1569 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1570 class->background[2], class->background[3]);
1571 cairo_paint(cairo);
1572 PangoContext *pango = pango_cairo_create_context(cairo);
1573 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1574 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1575 class->text[2], class->text[3]);
1576 cairo_move_to(cairo, 0, 0);
1577
1578 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1579
1580 cairo_surface_flush(surface);
1581 unsigned char *data = cairo_image_surface_get_data(surface);
1582 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
1583 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1584 output->wlr_output->backend);
1585 *texture = wlr_texture_from_pixels(
1586 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
1587 cairo_surface_destroy(surface);
1588 g_object_unref(pango);
1589 cairo_destroy(cairo);
1590 free(buffer); 1717 free(buffer);
1591} 1718}
1592 1719
@@ -1602,25 +1729,27 @@ void container_update_marks_textures(struct sway_container *con) {
1602 &config->border_colors.unfocused); 1729 &config->border_colors.unfocused);
1603 update_marks_texture(con, &con->marks_urgent, 1730 update_marks_texture(con, &con->marks_urgent,
1604 &config->border_colors.urgent); 1731 &config->border_colors.urgent);
1732 update_marks_texture(con, &con->marks_focused_tab_title,
1733 &config->border_colors.focused_tab_title);
1605 container_damage_whole(con); 1734 container_damage_whole(con);
1606} 1735}
1607 1736
1608void container_raise_floating(struct sway_container *con) { 1737void container_raise_floating(struct sway_container *con) {
1609 // Bring container to front by putting it at the end of the floating list. 1738 // Bring container to front by putting it at the end of the floating list.
1610 struct sway_container *floater = container_toplevel_ancestor(con); 1739 struct sway_container *floater = container_toplevel_ancestor(con);
1611 if (container_is_floating(floater) && floater->workspace) { 1740 if (container_is_floating(floater) && floater->pending.workspace) {
1612 list_move_to_end(floater->workspace->floating, floater); 1741 list_move_to_end(floater->pending.workspace->floating, floater);
1613 node_set_dirty(&floater->workspace->node); 1742 node_set_dirty(&floater->pending.workspace->node);
1614 } 1743 }
1615} 1744}
1616 1745
1617bool container_is_scratchpad_hidden(struct sway_container *con) { 1746bool container_is_scratchpad_hidden(struct sway_container *con) {
1618 return con->scratchpad && !con->workspace; 1747 return con->scratchpad && !con->pending.workspace;
1619} 1748}
1620 1749
1621bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { 1750bool container_is_scratchpad_hidden_or_child(struct sway_container *con) {
1622 con = container_toplevel_ancestor(con); 1751 con = container_toplevel_ancestor(con);
1623 return con->scratchpad && !con->workspace; 1752 return con->scratchpad && !con->pending.workspace;
1624} 1753}
1625 1754
1626bool container_is_sticky(struct sway_container *con) { 1755bool container_is_sticky(struct sway_container *con) {
@@ -1648,39 +1777,39 @@ static bool is_parallel(enum sway_container_layout first,
1648static bool container_is_squashable(struct sway_container *con, 1777static bool container_is_squashable(struct sway_container *con,
1649 struct sway_container *child) { 1778 struct sway_container *child) {
1650 enum sway_container_layout gp_layout = container_parent_layout(con); 1779 enum sway_container_layout gp_layout = container_parent_layout(con);
1651 return (con->layout == L_HORIZ || con->layout == L_VERT) && 1780 return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) &&
1652 (child->layout == L_HORIZ || child->layout == L_VERT) && 1781 (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) &&
1653 !is_parallel(con->layout, child->layout) && 1782 !is_parallel(con->pending.layout, child->pending.layout) &&
1654 is_parallel(gp_layout, child->layout); 1783 is_parallel(gp_layout, child->pending.layout);
1655} 1784}
1656 1785
1657static void container_squash_children(struct sway_container *con) { 1786static void container_squash_children(struct sway_container *con) {
1658 for (int i = 0; i < con->children->length; i++) { 1787 for (int i = 0; i < con->pending.children->length; i++) {
1659 struct sway_container *child = con->children->items[i]; 1788 struct sway_container *child = con->pending.children->items[i];
1660 i += container_squash(child); 1789 i += container_squash(child);
1661 } 1790 }
1662} 1791}
1663 1792
1664int container_squash(struct sway_container *con) { 1793int container_squash(struct sway_container *con) {
1665 if (!con->children) { 1794 if (!con->pending.children) {
1666 return 0; 1795 return 0;
1667 } 1796 }
1668 if (con->children->length != 1) { 1797 if (con->pending.children->length != 1) {
1669 container_squash_children(con); 1798 container_squash_children(con);
1670 return 0; 1799 return 0;
1671 } 1800 }
1672 struct sway_container *child = con->children->items[0]; 1801 struct sway_container *child = con->pending.children->items[0];
1673 int idx = container_sibling_index(con); 1802 int idx = container_sibling_index(con);
1674 int change = 0; 1803 int change = 0;
1675 if (container_is_squashable(con, child)) { 1804 if (container_is_squashable(con, child)) {
1676 // con and child are a redundant H/V pair. Destroy them. 1805 // con and child are a redundant H/V pair. Destroy them.
1677 while (child->children->length) { 1806 while (child->pending.children->length) {
1678 struct sway_container *current = child->children->items[0]; 1807 struct sway_container *current = child->pending.children->items[0];
1679 container_detach(current); 1808 container_detach(current);
1680 if (con->parent) { 1809 if (con->pending.parent) {
1681 container_insert_child(con->parent, current, idx); 1810 container_insert_child(con->pending.parent, current, idx);
1682 } else { 1811 } else {
1683 workspace_insert_tiling_direct(con->workspace, current, idx); 1812 workspace_insert_tiling_direct(con->pending.workspace, current, idx);
1684 } 1813 }
1685 change++; 1814 change++;
1686 } 1815 }
diff --git a/sway/tree/node.c b/sway/tree/node.c
index ffa7f2cc..bc7e2aa5 100644
--- a/sway/tree/node.c
+++ b/sway/tree/node.c
@@ -75,7 +75,7 @@ void node_get_box(struct sway_node *node, struct wlr_box *box) {
75struct sway_output *node_get_output(struct sway_node *node) { 75struct sway_output *node_get_output(struct sway_node *node) {
76 switch (node->type) { 76 switch (node->type) {
77 case N_CONTAINER: { 77 case N_CONTAINER: {
78 struct sway_workspace *ws = node->sway_container->workspace; 78 struct sway_workspace *ws = node->sway_container->pending.workspace;
79 return ws ? ws->output : NULL; 79 return ws ? ws->output : NULL;
80 } 80 }
81 case N_WORKSPACE: 81 case N_WORKSPACE:
@@ -91,7 +91,7 @@ struct sway_output *node_get_output(struct sway_node *node) {
91enum sway_container_layout node_get_layout(struct sway_node *node) { 91enum sway_container_layout node_get_layout(struct sway_node *node) {
92 switch (node->type) { 92 switch (node->type) {
93 case N_CONTAINER: 93 case N_CONTAINER:
94 return node->sway_container->layout; 94 return node->sway_container->pending.layout;
95 case N_WORKSPACE: 95 case N_WORKSPACE:
96 return node->sway_workspace->layout; 96 return node->sway_workspace->layout;
97 case N_OUTPUT: 97 case N_OUTPUT:
@@ -105,11 +105,11 @@ struct sway_node *node_get_parent(struct sway_node *node) {
105 switch (node->type) { 105 switch (node->type) {
106 case N_CONTAINER: { 106 case N_CONTAINER: {
107 struct sway_container *con = node->sway_container; 107 struct sway_container *con = node->sway_container;
108 if (con->parent) { 108 if (con->pending.parent) {
109 return &con->parent->node; 109 return &con->pending.parent->node;
110 } 110 }
111 if (con->workspace) { 111 if (con->pending.workspace) {
112 return &con->workspace->node; 112 return &con->pending.workspace->node;
113 } 113 }
114 } 114 }
115 return NULL; 115 return NULL;
@@ -131,7 +131,7 @@ struct sway_node *node_get_parent(struct sway_node *node) {
131list_t *node_get_children(struct sway_node *node) { 131list_t *node_get_children(struct sway_node *node) {
132 switch (node->type) { 132 switch (node->type) {
133 case N_CONTAINER: 133 case N_CONTAINER:
134 return node->sway_container->children; 134 return node->sway_container->pending.children;
135 case N_WORKSPACE: 135 case N_WORKSPACE:
136 return node->sway_workspace->tiling; 136 return node->sway_workspace->tiling;
137 case N_OUTPUT: 137 case N_OUTPUT:
@@ -143,7 +143,7 @@ list_t *node_get_children(struct sway_node *node) {
143 143
144bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { 144bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
145 if (ancestor->type == N_ROOT && node->type == N_CONTAINER && 145 if (ancestor->type == N_ROOT && node->type == N_CONTAINER &&
146 node->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { 146 node->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
147 return true; 147 return true;
148 } 148 }
149 struct sway_node *parent = node_get_parent(node); 149 struct sway_node *parent = node_get_parent(node);
@@ -152,7 +152,7 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
152 return true; 152 return true;
153 } 153 }
154 if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && 154 if (ancestor->type == N_ROOT && parent->type == N_CONTAINER &&
155 parent->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { 155 parent->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
156 return true; 156 return true;
157 } 157 }
158 parent = node_get_parent(parent); 158 parent = node_get_parent(parent);
diff --git a/sway/tree/output.c b/sway/tree/output.c
index a8ae30f7..52826c91 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -56,8 +56,8 @@ static void restore_workspaces(struct sway_output *output) {
56 } 56 }
57 57
58 // Saved workspaces 58 // Saved workspaces
59 while (root->noop_output->workspaces->length) { 59 while (root->fallback_output->workspaces->length) {
60 struct sway_workspace *ws = root->noop_output->workspaces->items[0]; 60 struct sway_workspace *ws = root->fallback_output->workspaces->items[0];
61 workspace_detach(ws); 61 workspace_detach(ws);
62 output_add_workspace(output, ws); 62 output_add_workspace(output, ws);
63 63
@@ -70,13 +70,13 @@ static void restore_workspaces(struct sway_output *output) {
70 // floater re-centered 70 // floater re-centered
71 for (int i = 0; i < ws->floating->length; i++) { 71 for (int i = 0; i < ws->floating->length; i++) {
72 struct sway_container *floater = ws->floating->items[i]; 72 struct sway_container *floater = ws->floating->items[i];
73 if (floater->width == 0 || floater->height == 0 || 73 if (floater->pending.width == 0 || floater->pending.height == 0 ||
74 floater->width > output->width || 74 floater->pending.width > output->width ||
75 floater->height > output->height || 75 floater->pending.height > output->height ||
76 floater->x > output->lx + output->width || 76 floater->pending.x > output->lx + output->width ||
77 floater->y > output->ly + output->height || 77 floater->pending.y > output->ly + output->height ||
78 floater->x + floater->width < output->lx || 78 floater->pending.x + floater->pending.width < output->lx ||
79 floater->y + floater->height < output->ly) { 79 floater->pending.y + floater->pending.height < output->ly) {
80 container_floating_resize_and_center(floater); 80 container_floating_resize_and_center(floater);
81 } 81 }
82 } 82 }
@@ -95,7 +95,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
95 output->detected_subpixel = wlr_output->subpixel; 95 output->detected_subpixel = wlr_output->subpixel;
96 output->scale_filter = SCALE_FILTER_NEAREST; 96 output->scale_filter = SCALE_FILTER_NEAREST;
97 97
98 wl_signal_init(&output->events.destroy); 98 wl_signal_init(&output->events.disable);
99 99
100 wl_list_insert(&root->all_outputs, &output->link); 100 wl_list_insert(&root->all_outputs, &output->link);
101 101
@@ -192,7 +192,7 @@ static void output_evacuate(struct sway_output *output) {
192 new_output = fallback_output; 192 new_output = fallback_output;
193 } 193 }
194 if (!new_output) { 194 if (!new_output) {
195 new_output = root->noop_output; 195 new_output = root->fallback_output;
196 } 196 }
197 197
198 struct sway_workspace *new_output_ws = 198 struct sway_workspace *new_output_ws =
@@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) {
262 } 262 }
263 263
264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); 264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
265 wl_signal_emit(&output->events.destroy, output); 265 wl_signal_emit(&output->events.disable, output);
266 266
267 output_evacuate(output); 267 output_evacuate(output);
268 268
@@ -286,13 +286,10 @@ void output_begin_destroy(struct sway_output *output) {
286 return; 286 return;
287 } 287 }
288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); 288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
289 wl_signal_emit(&output->node.events.destroy, &output->node);
289 290
290 output->node.destroying = true; 291 output->node.destroying = true;
291 node_set_dirty(&output->node); 292 node_set_dirty(&output->node);
292
293 wl_list_remove(&output->link);
294 output->wlr_output->data = NULL;
295 output->wlr_output = NULL;
296} 293}
297 294
298struct sway_output *output_from_wlr_output(struct wlr_output *output) { 295struct sway_output *output_from_wlr_output(struct wlr_output *output) {
@@ -304,10 +301,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
304 if (!sway_assert(direction, "got invalid direction: %d", direction)) { 301 if (!sway_assert(direction, "got invalid direction: %d", direction)) {
305 return NULL; 302 return NULL;
306 } 303 }
307 struct wlr_box *output_box = 304 struct wlr_box output_box;
308 wlr_output_layout_get_box(root->output_layout, reference->wlr_output); 305 wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box);
309 int lx = output_box->x + output_box->width / 2; 306 int lx = output_box.x + output_box.width / 2;
310 int ly = output_box->y + output_box->height / 2; 307 int ly = output_box.y + output_box.height / 2;
311 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( 308 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
312 root->output_layout, direction, reference->wlr_output, lx, ly); 309 root->output_layout, direction, reference->wlr_output, lx, ly);
313 if (!wlr_adjacent) { 310 if (!wlr_adjacent) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index ebd185ec..8508e9eb 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -59,11 +59,11 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works
59 return; 59 return;
60 } 60 }
61 61
62 struct sway_container *parent = con->parent; 62 struct sway_container *parent = con->pending.parent;
63 struct sway_workspace *workspace = con->workspace; 63 struct sway_workspace *workspace = con->pending.workspace;
64 64
65 // Clear the fullscreen mode when sending to the scratchpad 65 // Clear the fullscreen mode when sending to the scratchpad
66 if (con->fullscreen_mode != FULLSCREEN_NONE) { 66 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
67 container_fullscreen_disable(con); 67 container_fullscreen_disable(con);
68 } 68 }
69 69
@@ -117,7 +117,7 @@ void root_scratchpad_show(struct sway_container *con) {
117 sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); 117 sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on");
118 return; 118 return;
119 } 119 }
120 struct sway_workspace *old_ws = con->workspace; 120 struct sway_workspace *old_ws = con->pending.workspace;
121 121
122 // If the current con or any of its parents are in fullscreen mode, we 122 // If the current con or any of its parents are in fullscreen mode, we
123 // first need to disable it before showing the scratchpad con. 123 // first need to disable it before showing the scratchpad con.
@@ -134,15 +134,15 @@ void root_scratchpad_show(struct sway_container *con) {
134 workspace_consider_destroy(old_ws); 134 workspace_consider_destroy(old_ws);
135 } else { 135 } else {
136 // Act on the ancestor of scratchpad hidden split containers 136 // Act on the ancestor of scratchpad hidden split containers
137 while (con->parent) { 137 while (con->pending.parent) {
138 con = con->parent; 138 con = con->pending.parent;
139 } 139 }
140 } 140 }
141 workspace_add_floating(new_ws, con); 141 workspace_add_floating(new_ws, con);
142 142
143 // Make sure the container's center point overlaps this workspace 143 // Make sure the container's center point overlaps this workspace
144 double center_lx = con->x + con->width / 2; 144 double center_lx = con->pending.x + con->pending.width / 2;
145 double center_ly = con->y + con->height / 2; 145 double center_ly = con->pending.y + con->pending.height / 2;
146 146
147 struct wlr_box workspace_box; 147 struct wlr_box workspace_box;
148 workspace_get_box(new_ws, &workspace_box); 148 workspace_get_box(new_ws, &workspace_box);
@@ -155,7 +155,7 @@ void root_scratchpad_show(struct sway_container *con) {
155} 155}
156 156
157static void disable_fullscreen(struct sway_container *con, void *data) { 157static void disable_fullscreen(struct sway_container *con, void *data) {
158 if (con->fullscreen_mode != FULLSCREEN_NONE) { 158 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
159 container_fullscreen_disable(con); 159 container_fullscreen_disable(con);
160 } 160 }
161} 161}
@@ -163,9 +163,9 @@ static void disable_fullscreen(struct sway_container *con, void *data) {
163void root_scratchpad_hide(struct sway_container *con) { 163void root_scratchpad_hide(struct sway_container *con) {
164 struct sway_seat *seat = input_manager_current_seat(); 164 struct sway_seat *seat = input_manager_current_seat();
165 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); 165 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
166 struct sway_workspace *ws = con->workspace; 166 struct sway_workspace *ws = con->pending.workspace;
167 167
168 if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { 168 if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) {
169 // If the container was made fullscreen global while in the scratchpad, 169 // If the container was made fullscreen global while in the scratchpad,
170 // it should be shown until fullscreen has been disabled 170 // it should be shown until fullscreen has been disabled
171 return; 171 return;
@@ -209,7 +209,7 @@ static pid_t get_parent_pid(pid_t child) {
209 FILE *stat = NULL; 209 FILE *stat = NULL;
210 size_t buf_size = 0; 210 size_t buf_size = 0;
211 211
212 sprintf(file_name, "/proc/%d/stat", child); 212 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
213 213
214 if ((stat = fopen(file_name, "r"))) { 214 if ((stat = fopen(file_name, "r"))) {
215 if (getline(&buffer, &buf_size, stat) != -1) { 215 if (getline(&buffer, &buf_size, stat) != -1) {
@@ -270,7 +270,16 @@ found:
270 sway_log(SWAY_DEBUG, 270 sway_log(SWAY_DEBUG,
271 "Creating workspace %s for pid %d because it disappeared", 271 "Creating workspace %s for pid %d because it disappeared",
272 pw->workspace, pid); 272 pw->workspace, pid);
273 ws = workspace_create(pw->output, pw->workspace); 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);
274 } 283 }
275 284
276 pid_workspace_destroy(pw); 285 pid_workspace_destroy(pw);
@@ -365,8 +374,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
365 } 374 }
366 375
367 // Saved workspaces 376 // Saved workspaces
368 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 377 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
369 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 378 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
370 workspace_for_each_container(ws, f, data); 379 workspace_for_each_container(ws, f, data);
371 } 380 }
372} 381}
@@ -418,8 +427,8 @@ struct sway_container *root_find_container(
418 } 427 }
419 428
420 // Saved workspaces 429 // Saved workspaces
421 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 430 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
422 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 431 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
423 if ((result = workspace_find_container(ws, test, data))) { 432 if ((result = workspace_find_container(ws, test, data))) {
424 return result; 433 return result;
425 } 434 }
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7afcdf31..7d9e038d 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -6,6 +6,7 @@
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_output_layout.h> 7#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 8#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_subcompositor.h>
9#include <wlr/types/wlr_xdg_decoration_v1.h> 10#include <wlr/types/wlr_xdg_decoration_v1.h>
10#include "config.h" 11#include "config.h"
11#if HAVE_XWAYLAND 12#if HAVE_XWAYLAND
@@ -56,6 +57,7 @@ void view_destroy(struct sway_view *view) {
56 "(might have a pending transaction?)")) { 57 "(might have a pending transaction?)")) {
57 return; 58 return;
58 } 59 }
60 wl_list_remove(&view->events.unmap.listener_list);
59 if (!wl_list_empty(&view->saved_buffers)) { 61 if (!wl_list_empty(&view->saved_buffers)) {
60 view_remove_saved_buffer(view); 62 view_remove_saved_buffer(view);
61 } 63 }
@@ -206,7 +208,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) {
206 } else { 208 } else {
207 only_visible = true; 209 only_visible = true;
208 } 210 }
209 con = con->parent; 211 con = con->pending.parent;
210 } 212 }
211 return only_visible; 213 return only_visible;
212} 214}
@@ -222,72 +224,73 @@ static bool view_is_only_visible(struct sway_view *view) {
222 } 224 }
223 } 225 }
224 226
225 con = con->parent; 227 con = con->pending.parent;
226 } 228 }
227 229
228 return true; 230 return true;
229} 231}
230 232
231static bool gaps_to_edge(struct sway_view *view) { 233static bool gaps_to_edge(struct sway_view *view) {
232 struct side_gaps gaps = view->container->workspace->current_gaps; 234 struct side_gaps gaps = view->container->pending.workspace->current_gaps;
233 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; 235 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0;
234} 236}
235 237
236void view_autoconfigure(struct sway_view *view) { 238void view_autoconfigure(struct sway_view *view) {
237 struct sway_container *con = view->container; 239 struct sway_container *con = view->container;
238 struct sway_workspace *ws = con->workspace; 240 struct sway_workspace *ws = con->pending.workspace;
239 241
240 if (container_is_scratchpad_hidden(con) && 242 if (container_is_scratchpad_hidden(con) &&
241 con->fullscreen_mode != FULLSCREEN_GLOBAL) { 243 con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
242 return; 244 return;
243 } 245 }
244 struct sway_output *output = ws ? ws->output : NULL; 246 struct sway_output *output = ws ? ws->output : NULL;
245 247
246 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 248 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
247 con->content_x = output->lx; 249 con->pending.content_x = output->lx;
248 con->content_y = output->ly; 250 con->pending.content_y = output->ly;
249 con->content_width = output->width; 251 con->pending.content_width = output->width;
250 con->content_height = output->height; 252 con->pending.content_height = output->height;
251 return; 253 return;
252 } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 254 } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
253 con->content_x = root->x; 255 con->pending.content_x = root->x;
254 con->content_y = root->y; 256 con->pending.content_y = root->y;
255 con->content_width = root->width; 257 con->pending.content_width = root->width;
256 con->content_height = root->height; 258 con->pending.content_height = root->height;
257 return; 259 return;
258 } 260 }
259 261
260 con->border_top = con->border_bottom = true; 262 con->pending.border_top = con->pending.border_bottom = true;
261 con->border_left = con->border_right = true; 263 con->pending.border_left = con->pending.border_right = true;
262 double y_offset = 0; 264 double y_offset = 0;
263 265
264 if (!container_is_floating(con) && ws) { 266 if (!container_is_floating_or_child(con) && ws) {
265 if (config->hide_edge_borders == E_BOTH 267 if (config->hide_edge_borders == E_BOTH
266 || config->hide_edge_borders == E_VERTICAL) { 268 || config->hide_edge_borders == E_VERTICAL) {
267 con->border_left = con->x != ws->x; 269 con->pending.border_left = con->pending.x != ws->x;
268 int right_x = con->x + con->width; 270 int right_x = con->pending.x + con->pending.width;
269 con->border_right = right_x != ws->x + ws->width; 271 con->pending.border_right = right_x != ws->x + ws->width;
270 } 272 }
271 273
272 if (config->hide_edge_borders == E_BOTH 274 if (config->hide_edge_borders == E_BOTH
273 || config->hide_edge_borders == E_HORIZONTAL) { 275 || config->hide_edge_borders == E_HORIZONTAL) {
274 con->border_top = con->y != ws->y; 276 con->pending.border_top = con->pending.y != ws->y;
275 int bottom_y = con->y + con->height; 277 int bottom_y = con->pending.y + con->pending.height;
276 con->border_bottom = bottom_y != ws->y + ws->height; 278 con->pending.border_bottom = bottom_y != ws->y + ws->height;
277 } 279 }
278 280
279 bool smart = config->hide_edge_borders_smart == ESMART_ON || 281 bool smart = config->hide_edge_borders_smart == ESMART_ON ||
280 (config->hide_edge_borders_smart == ESMART_NO_GAPS && 282 (config->hide_edge_borders_smart == ESMART_NO_GAPS &&
281 !gaps_to_edge(view)); 283 !gaps_to_edge(view));
282 if (smart) { 284 if (smart) {
283 bool show_border = container_is_floating_or_child(con) || 285 bool show_border = !view_is_only_visible(view);
284 !view_is_only_visible(view); 286 con->pending.border_left &= show_border;
285 con->border_left &= show_border; 287 con->pending.border_right &= show_border;
286 con->border_right &= show_border; 288 con->pending.border_top &= show_border;
287 con->border_top &= show_border; 289 con->pending.border_bottom &= show_border;
288 con->border_bottom &= show_border;
289 } 290 }
291 }
290 292
293 if (!container_is_floating(con)) {
291 // In a tabbed or stacked container, the container's y is the top of the 294 // In a tabbed or stacked container, the container's y is the top of the
292 // title area. We have to offset the surface y by the height of the title, 295 // title area. We have to offset the surface y by the height of the title,
293 // bar, and disable any top border because we'll always have the title bar. 296 // bar, and disable any top border because we'll always have the title bar.
@@ -298,56 +301,56 @@ void view_autoconfigure(struct sway_view *view) {
298 enum sway_container_layout layout = container_parent_layout(con); 301 enum sway_container_layout layout = container_parent_layout(con);
299 if (layout == L_TABBED) { 302 if (layout == L_TABBED) {
300 y_offset = container_titlebar_height(); 303 y_offset = container_titlebar_height();
301 con->border_top = false; 304 con->pending.border_top = false;
302 } else if (layout == L_STACKED) { 305 } else if (layout == L_STACKED) {
303 y_offset = container_titlebar_height() * siblings->length; 306 y_offset = container_titlebar_height() * siblings->length;
304 con->border_top = false; 307 con->pending.border_top = false;
305 } 308 }
306 } 309 }
307 } 310 }
308 311
309 double x, y, width, height; 312 double x, y, width, height;
310 switch (con->border) { 313 switch (con->pending.border) {
311 default: 314 default:
312 case B_CSD: 315 case B_CSD:
313 case B_NONE: 316 case B_NONE:
314 x = con->x; 317 x = con->pending.x;
315 y = con->y + y_offset; 318 y = con->pending.y + y_offset;
316 width = con->width; 319 width = con->pending.width;
317 height = con->height - y_offset; 320 height = con->pending.height - y_offset;
318 break; 321 break;
319 case B_PIXEL: 322 case B_PIXEL:
320 x = con->x + con->border_thickness * con->border_left; 323 x = con->pending.x + con->pending.border_thickness * con->pending.border_left;
321 y = con->y + con->border_thickness * con->border_top + y_offset; 324 y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset;
322 width = con->width 325 width = con->pending.width
323 - con->border_thickness * con->border_left 326 - con->pending.border_thickness * con->pending.border_left
324 - con->border_thickness * con->border_right; 327 - con->pending.border_thickness * con->pending.border_right;
325 height = con->height - y_offset 328 height = con->pending.height - y_offset
326 - con->border_thickness * con->border_top 329 - con->pending.border_thickness * con->pending.border_top
327 - con->border_thickness * con->border_bottom; 330 - con->pending.border_thickness * con->pending.border_bottom;
328 break; 331 break;
329 case B_NORMAL: 332 case B_NORMAL:
330 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border 333 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
331 x = con->x + con->border_thickness * con->border_left; 334 x = con->pending.x + con->pending.border_thickness * con->pending.border_left;
332 width = con->width 335 width = con->pending.width
333 - con->border_thickness * con->border_left 336 - con->pending.border_thickness * con->pending.border_left
334 - con->border_thickness * con->border_right; 337 - con->pending.border_thickness * con->pending.border_right;
335 if (y_offset) { 338 if (y_offset) {
336 y = con->y + y_offset; 339 y = con->pending.y + y_offset;
337 height = con->height - y_offset 340 height = con->pending.height - y_offset
338 - con->border_thickness * con->border_bottom; 341 - con->pending.border_thickness * con->pending.border_bottom;
339 } else { 342 } else {
340 y = con->y + container_titlebar_height(); 343 y = con->pending.y + container_titlebar_height();
341 height = con->height - container_titlebar_height() 344 height = con->pending.height - container_titlebar_height()
342 - con->border_thickness * con->border_bottom; 345 - con->pending.border_thickness * con->pending.border_bottom;
343 } 346 }
344 break; 347 break;
345 } 348 }
346 349
347 con->content_x = x; 350 con->pending.content_x = x;
348 con->content_y = y; 351 con->pending.content_y = y;
349 con->content_width = width; 352 con->pending.content_width = width;
350 con->content_height = height; 353 con->pending.content_height = height;
351} 354}
352 355
353void view_set_activated(struct sway_view *view, bool activated) { 356void view_set_activated(struct sway_view *view, bool activated) {
@@ -361,7 +364,7 @@ void view_set_activated(struct sway_view *view, bool activated) {
361} 364}
362 365
363void view_request_activate(struct sway_view *view) { 366void view_request_activate(struct sway_view *view) {
364 struct sway_workspace *ws = view->container->workspace; 367 struct sway_workspace *ws = view->container->pending.workspace;
365 if (!ws) { // hidden scratchpad container 368 if (!ws) { // hidden scratchpad container
366 return; 369 return;
367 } 370 }
@@ -401,13 +404,13 @@ void view_set_csd_from_server(struct sway_view *view, bool enabled) {
401void view_update_csd_from_client(struct sway_view *view, bool enabled) { 404void view_update_csd_from_client(struct sway_view *view, bool enabled) {
402 sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); 405 sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled);
403 struct sway_container *con = view->container; 406 struct sway_container *con = view->container;
404 if (enabled && con && con->border != B_CSD) { 407 if (enabled && con && con->pending.border != B_CSD) {
405 con->saved_border = con->border; 408 con->saved_border = con->pending.border;
406 if (container_is_floating(con)) { 409 if (container_is_floating(con)) {
407 con->border = B_CSD; 410 con->pending.border = B_CSD;
408 } 411 }
409 } else if (!enabled && con && con->border == B_CSD) { 412 } else if (!enabled && con && con->pending.border == B_CSD) {
410 con->border = con->saved_border; 413 con->pending.border = con->saved_border;
411 } 414 }
412 view->using_csd = enabled; 415 view->using_csd = enabled;
413} 416}
@@ -465,6 +468,9 @@ static void view_subsurface_create(struct sway_view *view,
465static void view_init_subsurfaces(struct sway_view *view, 468static void view_init_subsurfaces(struct sway_view *view,
466 struct wlr_surface *surface); 469 struct wlr_surface *surface);
467 470
471static void view_child_init_subsurfaces(struct sway_view_child *view_child,
472 struct wlr_surface *surface);
473
468static void view_handle_surface_new_subsurface(struct wl_listener *listener, 474static void view_handle_surface_new_subsurface(struct wl_listener *listener,
469 void *data) { 475 void *data) {
470 struct sway_view *view = 476 struct sway_view *view =
@@ -577,7 +583,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
577 if (node && node->type == N_WORKSPACE) { 583 if (node && node->type == N_WORKSPACE) {
578 return node->sway_workspace; 584 return node->sway_workspace;
579 } else if (node && node->type == N_CONTAINER) { 585 } else if (node && node->type == N_CONTAINER) {
580 return node->sway_container->workspace; 586 return node->sway_container->pending.workspace;
581 } 587 }
582 588
583 // When there's no outputs connected, the above should match a workspace on 589 // When there's no outputs connected, the above should match a workspace on
@@ -590,12 +596,17 @@ static bool should_focus(struct sway_view *view) {
590 struct sway_seat *seat = input_manager_current_seat(); 596 struct sway_seat *seat = input_manager_current_seat();
591 struct sway_container *prev_con = seat_get_focused_container(seat); 597 struct sway_container *prev_con = seat_get_focused_container(seat);
592 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); 598 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
593 struct sway_workspace *map_ws = view->container->workspace; 599 struct sway_workspace *map_ws = view->container->pending.workspace;
594 600
595 if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { 601 if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
596 return true; 602 return true;
597 } 603 }
598 604
605 // View opened "under" fullscreen view should not be given focus.
606 if (root->fullscreen_global || !map_ws || map_ws->fullscreen) {
607 return false;
608 }
609
599 // Views can only take focus if they are mapped into the active workspace 610 // Views can only take focus if they are mapped into the active workspace
600 if (prev_ws != map_ws) { 611 if (prev_ws != map_ws) {
601 return false; 612 return false;
@@ -603,9 +614,9 @@ static bool should_focus(struct sway_view *view) {
603 614
604 // If the view is the only one in the focused workspace, it'll get focus 615 // If the view is the only one in the focused workspace, it'll get focus
605 // regardless of any no_focus criteria. 616 // regardless of any no_focus criteria.
606 if (!view->container->parent && !prev_con) { 617 if (!view->container->pending.parent && !prev_con) {
607 size_t num_children = view->container->workspace->tiling->length + 618 size_t num_children = view->container->pending.workspace->tiling->length +
608 view->container->workspace->floating->length; 619 view->container->pending.workspace->floating->length;
609 if (num_children == 1) { 620 if (num_children == 1) {
610 return true; 621 return true;
611 } 622 }
@@ -635,6 +646,7 @@ static void handle_foreign_activate_request(
635 break; 646 break;
636 } 647 }
637 } 648 }
649 transaction_commit_dirty();
638} 650}
639 651
640static void handle_foreign_fullscreen_request( 652static void handle_foreign_fullscreen_request(
@@ -645,9 +657,21 @@ static void handle_foreign_fullscreen_request(
645 657
646 // Match fullscreen command behavior for scratchpad hidden views 658 // Match fullscreen command behavior for scratchpad hidden views
647 struct sway_container *container = view->container; 659 struct sway_container *container = view->container;
648 if (!container->workspace) { 660 if (!container->pending.workspace) {
649 while (container->parent) { 661 while (container->pending.parent) {
650 container = container->parent; 662 container = container->pending.parent;
663 }
664 }
665
666 if (event->fullscreen && event->output && event->output->data) {
667 struct sway_output *output = event->output->data;
668 struct sway_workspace *ws = output_get_active_workspace(output);
669 if (ws && !container_is_scratchpad_hidden(view->container)) {
670 if (container_is_floating(view->container)) {
671 workspace_add_floating(ws, view->container);
672 } else {
673 workspace_add_tiling(ws, view->container);
674 }
651 } 675 }
652 } 676 }
653 677
@@ -656,12 +680,13 @@ static void handle_foreign_fullscreen_request(
656 if (event->fullscreen) { 680 if (event->fullscreen) {
657 arrange_root(); 681 arrange_root();
658 } else { 682 } else {
659 if (container->parent) { 683 if (container->pending.parent) {
660 arrange_container(container->parent); 684 arrange_container(container->pending.parent);
661 } else if (container->workspace) { 685 } else if (container->pending.workspace) {
662 arrange_workspace(container->workspace); 686 arrange_workspace(container->pending.workspace);
663 } 687 }
664 } 688 }
689 transaction_commit_dirty();
665} 690}
666 691
667static void handle_foreign_close_request( 692static void handle_foreign_close_request(
@@ -705,10 +730,29 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
705 } 730 }
706 731
707 struct sway_seat *seat = input_manager_current_seat(); 732 struct sway_seat *seat = input_manager_current_seat();
708 struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) 733 struct sway_node *node =
709 : seat_get_focus_inactive(seat, &root->node); 734 seat_get_focus_inactive(seat, ws ? &ws->node : &root->node);
710 struct sway_container *target_sibling = node->type == N_CONTAINER ? 735 struct sway_container *target_sibling = NULL;
711 node->sway_container : NULL; 736 if (node && node->type == N_CONTAINER) {
737 if (container_is_floating(node->sway_container)) {
738 // If we're about to launch the view into the floating container, then
739 // launch it as a tiled view instead.
740 if (ws) {
741 target_sibling = seat_get_focus_inactive_tiling(seat, ws);
742 if (target_sibling) {
743 struct sway_container *con =
744 seat_get_focus_inactive_view(seat, &target_sibling->node);
745 if (con) {
746 target_sibling = con;
747 }
748 }
749 } else {
750 ws = seat_get_last_known_workspace(seat);
751 }
752 } else {
753 target_sibling = node->sway_container;
754 }
755 }
712 756
713 view->foreign_toplevel = 757 view->foreign_toplevel =
714 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 758 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
@@ -725,13 +769,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
725 wl_signal_add(&view->foreign_toplevel->events.destroy, 769 wl_signal_add(&view->foreign_toplevel->events.destroy,
726 &view->foreign_destroy); 770 &view->foreign_destroy);
727 771
728 // If we're about to launch the view into the floating container, then
729 // launch it as a tiled view in the root of the workspace instead.
730 if (target_sibling && container_is_floating(target_sibling)) {
731 target_sibling = NULL;
732 ws = seat_get_last_known_workspace(seat);
733 }
734
735 struct sway_container *container = view->container; 772 struct sway_container *container = view->container;
736 if (target_sibling) { 773 if (target_sibling) {
737 container_add_sibling(target_sibling, container, 1); 774 container_add_sibling(target_sibling, container, 1);
@@ -742,7 +779,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
742 779
743 view_init_subsurfaces(view, wlr_surface); 780 view_init_subsurfaces(view, wlr_surface);
744 wl_signal_add(&wlr_surface->events.new_subsurface, 781 wl_signal_add(&wlr_surface->events.new_subsurface,
745 &view->surface_new_subsurface); 782 &view->surface_new_subsurface);
746 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; 783 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
747 784
748 if (decoration) { 785 if (decoration) {
@@ -750,20 +787,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
750 } 787 }
751 788
752 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 789 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
753 view->container->border = config->floating_border; 790 view->container->pending.border = config->floating_border;
754 view->container->border_thickness = config->floating_border_thickness; 791 view->container->pending.border_thickness = config->floating_border_thickness;
755 container_set_floating(view->container, true); 792 container_set_floating(view->container, true);
756 } else { 793 } else {
757 view->container->border = config->border; 794 view->container->pending.border = config->border;
758 view->container->border_thickness = config->border_thickness; 795 view->container->pending.border_thickness = config->border_thickness;
759 view_set_tiled(view, true); 796 view_set_tiled(view, true);
760 } 797 }
761 798
762 if (config->popup_during_fullscreen == POPUP_LEAVE && 799 if (config->popup_during_fullscreen == POPUP_LEAVE &&
763 container->workspace && 800 container->pending.workspace &&
764 container->workspace->fullscreen && 801 container->pending.workspace->fullscreen &&
765 container->workspace->fullscreen->view) { 802 container->pending.workspace->fullscreen->view) {
766 struct sway_container *fs = container->workspace->fullscreen; 803 struct sway_container *fs = container->pending.workspace->fullscreen;
767 if (view_is_transient_for(view, fs->view)) { 804 if (view_is_transient_for(view, fs->view)) {
768 container_set_fullscreen(fs, false); 805 container_set_fullscreen(fs, false);
769 } 806 }
@@ -774,12 +811,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
774 811
775 if (fullscreen) { 812 if (fullscreen) {
776 container_set_fullscreen(view->container, true); 813 container_set_fullscreen(view->container, true);
777 arrange_workspace(view->container->workspace); 814 arrange_workspace(view->container->pending.workspace);
778 } else { 815 } else {
779 if (container->parent) { 816 if (container->pending.parent) {
780 arrange_container(container->parent); 817 arrange_container(container->pending.parent);
781 } else if (container->workspace) { 818 } else if (container->pending.workspace) {
782 arrange_workspace(container->workspace); 819 arrange_workspace(container->pending.workspace);
783 } 820 }
784 } 821 }
785 822
@@ -790,9 +827,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
790#if HAVE_XWAYLAND 827#if HAVE_XWAYLAND
791 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 828 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
792 struct wlr_xwayland_surface *xsurface = 829 struct wlr_xwayland_surface *xsurface =
793 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 830 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
794 set_focus = (wlr_xwayland_icccm_input_model(xsurface) != 831 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
795 WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; 832 WLR_ICCCM_INPUT_MODEL_NONE;
796 } 833 }
797#endif 834#endif
798 835
@@ -803,11 +840,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
803 const char *app_id; 840 const char *app_id;
804 const char *class; 841 const char *class;
805 if ((app_id = view_get_app_id(view)) != NULL) { 842 if ((app_id = view_get_app_id(view)) != NULL) {
806 wlr_foreign_toplevel_handle_v1_set_app_id( 843 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);
807 view->foreign_toplevel, app_id);
808 } else if ((class = view_get_class(view)) != NULL) { 844 } else if ((class = view_get_class(view)) != NULL) {
809 wlr_foreign_toplevel_handle_v1_set_app_id( 845 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class);
810 view->foreign_toplevel, class);
811 } 846 }
812} 847}
813 848
@@ -826,8 +861,8 @@ void view_unmap(struct sway_view *view) {
826 view->foreign_toplevel = NULL; 861 view->foreign_toplevel = NULL;
827 } 862 }
828 863
829 struct sway_container *parent = view->container->parent; 864 struct sway_container *parent = view->container->pending.parent;
830 struct sway_workspace *ws = view->container->workspace; 865 struct sway_workspace *ws = view->container->pending.workspace;
831 container_begin_destroy(view->container); 866 container_begin_destroy(view->container);
832 if (parent) { 867 if (parent) {
833 container_reap_empty(parent); 868 container_reap_empty(parent);
@@ -860,47 +895,38 @@ void view_unmap(struct sway_view *view) {
860 view->surface = NULL; 895 view->surface = NULL;
861} 896}
862 897
863void view_update_size(struct sway_view *view, int width, int height) { 898void view_update_size(struct sway_view *view) {
864 struct sway_container *con = view->container; 899 struct sway_container *con = view->container;
900 con->pending.content_width = view->geometry.width;
901 con->pending.content_height = view->geometry.height;
902 container_set_geometry_from_content(con);
903}
865 904
866 if (container_is_floating(con)) { 905void view_center_surface(struct sway_view *view) {
867 con->content_width = width; 906 struct sway_container *con = view->container;
868 con->content_height = height; 907 // We always center the current coordinates rather than the next, as the
869 container_set_geometry_from_content(con); 908 // geometry immediately affects the currently active rendering.
870 } else { 909 con->surface_x = fmax(con->current.content_x, con->current.content_x +
871 con->surface_x = con->content_x + (con->content_width - width) / 2; 910 (con->current.content_width - view->geometry.width) / 2);
872 con->surface_y = con->content_y + (con->content_height - height) / 2; 911 con->surface_y = fmax(con->current.content_y, con->current.content_y +
873 con->surface_x = fmax(con->surface_x, con->content_x); 912 (con->current.content_height - view->geometry.height) / 2);
874 con->surface_y = fmax(con->surface_y, con->content_y);
875 }
876} 913}
877 914
878static const struct sway_view_child_impl subsurface_impl; 915static const struct sway_view_child_impl subsurface_impl;
879 916
880static void subsurface_get_root_coords(struct sway_view_child *child, 917static void subsurface_get_view_coords(struct sway_view_child *child,
881 int *root_sx, int *root_sy) { 918 int *sx, int *sy) {
882 struct wlr_surface *surface = child->surface; 919 struct wlr_surface *surface = child->surface;
883 *root_sx = -child->view->geometry.x;
884 *root_sy = -child->view->geometry.y;
885
886 if (child->parent && child->parent->impl && 920 if (child->parent && child->parent->impl &&
887 child->parent->impl->get_root_coords) { 921 child->parent->impl->get_view_coords) {
888 int sx, sy; 922 child->parent->impl->get_view_coords(child->parent, sx, sy);
889 child->parent->impl->get_root_coords(child->parent, &sx, &sy);
890 *root_sx += sx;
891 *root_sy += sy;
892 } else { 923 } else {
893 while (surface && wlr_surface_is_subsurface(surface)) { 924 *sx = *sy = 0;
894 struct wlr_subsurface *subsurface =
895 wlr_subsurface_from_wlr_surface(surface);
896 if (subsurface == NULL) {
897 break;
898 }
899 *root_sx += subsurface->current.x;
900 *root_sy += subsurface->current.y;
901 surface = subsurface->parent;
902 }
903 } 925 }
926 struct wlr_subsurface *subsurface =
927 wlr_subsurface_from_wlr_surface(surface);
928 *sx += subsurface->current.x;
929 *sy += subsurface->current.y;
904} 930}
905 931
906static void subsurface_destroy(struct sway_view_child *child) { 932static void subsurface_destroy(struct sway_view_child *child) {
@@ -914,7 +940,7 @@ static void subsurface_destroy(struct sway_view_child *child) {
914} 940}
915 941
916static const struct sway_view_child_impl subsurface_impl = { 942static const struct sway_view_child_impl subsurface_impl = {
917 .get_root_coords = subsurface_get_root_coords, 943 .get_view_coords = subsurface_get_view_coords,
918 .destroy = subsurface_destroy, 944 .destroy = subsurface_destroy,
919}; 945};
920 946
@@ -968,15 +994,27 @@ static void view_child_subsurface_create(struct sway_view_child *child,
968 view_child_damage(&subsurface->child, true); 994 view_child_damage(&subsurface->child, true);
969} 995}
970 996
997static bool view_child_is_mapped(struct sway_view_child *child) {
998 while (child) {
999 if (!child->mapped) {
1000 return false;
1001 }
1002 child = child->parent;
1003 }
1004 return true;
1005}
1006
971static void view_child_damage(struct sway_view_child *child, bool whole) { 1007static void view_child_damage(struct sway_view_child *child, bool whole) {
972 if (!child || !child->mapped || !child->view || !child->view->container) { 1008 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
973 return; 1009 return;
974 } 1010 }
975 int sx, sy; 1011 int sx, sy;
976 child->impl->get_root_coords(child, &sx, &sy); 1012 child->impl->get_view_coords(child, &sx, &sy);
977 desktop_damage_surface(child->surface, 1013 desktop_damage_surface(child->surface,
978 child->view->container->content_x + sx, 1014 child->view->container->pending.content_x -
979 child->view->container->content_y + sy, whole); 1015 child->view->geometry.x + sx,
1016 child->view->container->pending.content_y -
1017 child->view->geometry.y + sy, whole);
980} 1018}
981 1019
982static void view_child_handle_surface_commit(struct wl_listener *listener, 1020static void view_child_handle_surface_commit(struct wl_listener *listener,
@@ -1004,11 +1042,29 @@ static void view_child_handle_surface_destroy(struct wl_listener *listener,
1004static void view_init_subsurfaces(struct sway_view *view, 1042static void view_init_subsurfaces(struct sway_view *view,
1005 struct wlr_surface *surface) { 1043 struct wlr_surface *surface) {
1006 struct wlr_subsurface *subsurface; 1044 struct wlr_subsurface *subsurface;
1007 wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { 1045 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1046 current.link) {
1047 view_subsurface_create(view, subsurface);
1048 }
1049 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1050 current.link) {
1008 view_subsurface_create(view, subsurface); 1051 view_subsurface_create(view, subsurface);
1009 } 1052 }
1010} 1053}
1011 1054
1055static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1056 struct wlr_surface *surface) {
1057 struct wlr_subsurface *subsurface;
1058 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1059 current.link) {
1060 view_child_subsurface_create(view_child, subsurface);
1061 }
1062 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1063 current.link) {
1064 view_child_subsurface_create(view_child, subsurface);
1065 }
1066}
1067
1012static void view_child_handle_surface_map(struct wl_listener *listener, 1068static void view_child_handle_surface_map(struct wl_listener *listener,
1013 void *data) { 1069 void *data) {
1014 struct sway_view_child *child = 1070 struct sway_view_child *child =
@@ -1059,16 +1115,19 @@ void view_child_init(struct sway_view_child *child,
1059 wl_signal_add(&view->events.unmap, &child->view_unmap); 1115 wl_signal_add(&view->events.unmap, &child->view_unmap);
1060 child->view_unmap.notify = view_child_handle_view_unmap; 1116 child->view_unmap.notify = view_child_handle_view_unmap;
1061 1117
1062 struct sway_workspace *workspace = child->view->container->workspace; 1118 struct sway_container *container = child->view->container;
1063 if (workspace) { 1119 if (container != NULL) {
1064 wlr_surface_send_enter(child->surface, workspace->output->wlr_output); 1120 struct sway_workspace *workspace = container->pending.workspace;
1121 if (workspace) {
1122 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1123 }
1065 } 1124 }
1066 1125
1067 view_init_subsurfaces(child->view, surface); 1126 view_child_init_subsurfaces(child, surface);
1068} 1127}
1069 1128
1070void view_child_destroy(struct sway_view_child *child) { 1129void view_child_destroy(struct sway_view_child *child) {
1071 if (child->mapped && child->view->container != NULL) { 1130 if (view_child_is_mapped(child) && child->view->container != NULL) {
1072 view_child_damage(child, true); 1131 view_child_damage(child, true);
1073 } 1132 }
1074 1133
@@ -1081,6 +1140,9 @@ void view_child_destroy(struct sway_view_child *child) {
1081 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { 1140 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1082 wl_list_remove(&subchild->link); 1141 wl_list_remove(&subchild->link);
1083 subchild->parent = NULL; 1142 subchild->parent = NULL;
1143 // The subchild lost its parent link, so it cannot see that the parent
1144 // is unmapped. Unmap it directly.
1145 subchild->mapped = false;
1084 } 1146 }
1085 1147
1086 wl_list_remove(&child->surface_commit.link); 1148 wl_list_remove(&child->surface_commit.link);
@@ -1101,18 +1163,27 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1101 if (wlr_surface_is_xdg_surface(wlr_surface)) { 1163 if (wlr_surface_is_xdg_surface(wlr_surface)) {
1102 struct wlr_xdg_surface *xdg_surface = 1164 struct wlr_xdg_surface *xdg_surface =
1103 wlr_xdg_surface_from_wlr_surface(wlr_surface); 1165 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1166 if (xdg_surface == NULL) {
1167 return NULL;
1168 }
1104 return view_from_wlr_xdg_surface(xdg_surface); 1169 return view_from_wlr_xdg_surface(xdg_surface);
1105 } 1170 }
1106#if HAVE_XWAYLAND 1171#if HAVE_XWAYLAND
1107 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 1172 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
1108 struct wlr_xwayland_surface *xsurface = 1173 struct wlr_xwayland_surface *xsurface =
1109 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 1174 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1175 if (xsurface == NULL) {
1176 return NULL;
1177 }
1110 return view_from_wlr_xwayland_surface(xsurface); 1178 return view_from_wlr_xwayland_surface(xsurface);
1111 } 1179 }
1112#endif 1180#endif
1113 if (wlr_surface_is_subsurface(wlr_surface)) { 1181 if (wlr_surface_is_subsurface(wlr_surface)) {
1114 struct wlr_subsurface *subsurface = 1182 struct wlr_subsurface *subsurface =
1115 wlr_subsurface_from_wlr_surface(wlr_surface); 1183 wlr_subsurface_from_wlr_surface(wlr_surface);
1184 if (subsurface == NULL) {
1185 return NULL;
1186 }
1116 return view_from_wlr_surface(subsurface->parent); 1187 return view_from_wlr_surface(subsurface->parent);
1117 } 1188 }
1118 if (wlr_surface_is_layer_surface(wlr_surface)) { 1189 if (wlr_surface_is_layer_surface(wlr_surface)) {
@@ -1225,8 +1296,6 @@ void view_update_title(struct sway_view *view, bool force) {
1225 view->container->title = NULL; 1296 view->container->title = NULL;
1226 view->container->formatted_title = NULL; 1297 view->container->formatted_title = NULL;
1227 } 1298 }
1228 container_calculate_title_height(view->container);
1229 config_update_font_height(false);
1230 1299
1231 // Update title after the global font height is updated 1300 // Update title after the global font height is updated
1232 container_update_title_textures(view->container); 1301 container_update_title_textures(view->container);
@@ -1242,15 +1311,15 @@ bool view_is_visible(struct sway_view *view) {
1242 if (view->container->node.destroying) { 1311 if (view->container->node.destroying) {
1243 return false; 1312 return false;
1244 } 1313 }
1245 struct sway_workspace *workspace = view->container->workspace; 1314 struct sway_workspace *workspace = view->container->pending.workspace;
1246 if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { 1315 if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
1247 bool fs_global_descendant = false; 1316 bool fs_global_descendant = false;
1248 struct sway_container *parent = view->container->parent; 1317 struct sway_container *parent = view->container->pending.parent;
1249 while (parent) { 1318 while (parent) {
1250 if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { 1319 if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1251 fs_global_descendant = true; 1320 fs_global_descendant = true;
1252 } 1321 }
1253 parent = parent->parent; 1322 parent = parent->pending.parent;
1254 } 1323 }
1255 if (!fs_global_descendant) { 1324 if (!fs_global_descendant) {
1256 return false; 1325 return false;
@@ -1268,13 +1337,13 @@ bool view_is_visible(struct sway_view *view) {
1268 enum sway_container_layout layout = container_parent_layout(con); 1337 enum sway_container_layout layout = container_parent_layout(con);
1269 if ((layout == L_TABBED || layout == L_STACKED) 1338 if ((layout == L_TABBED || layout == L_STACKED)
1270 && !container_is_floating(con)) { 1339 && !container_is_floating(con)) {
1271 struct sway_node *parent = con->parent ? 1340 struct sway_node *parent = con->pending.parent ?
1272 &con->parent->node : &con->workspace->node; 1341 &con->pending.parent->node : &con->pending.workspace->node;
1273 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1342 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
1274 return false; 1343 return false;
1275 } 1344 }
1276 } 1345 }
1277 con = con->parent; 1346 con = con->pending.parent;
1278 } 1347 }
1279 // Check view isn't hidden by another fullscreen view 1348 // Check view isn't hidden by another fullscreen view
1280 struct sway_container *fs = root->fullscreen_global ? 1349 struct sway_container *fs = root->fullscreen_global ?
@@ -1308,7 +1377,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1308 ipc_event_window(view->container, "urgent"); 1377 ipc_event_window(view->container, "urgent");
1309 1378
1310 if (!container_is_scratchpad_hidden(view->container)) { 1379 if (!container_is_scratchpad_hidden(view->container)) {
1311 workspace_detect_urgent(view->container->workspace); 1380 workspace_detect_urgent(view->container->pending.workspace);
1312 } 1381 }
1313} 1382}
1314 1383
@@ -1338,11 +1407,11 @@ static void view_save_buffer_iterator(struct wlr_surface *surface,
1338 saved_buffer->buffer = surface->buffer; 1407 saved_buffer->buffer = surface->buffer;
1339 saved_buffer->width = surface->current.width; 1408 saved_buffer->width = surface->current.width;
1340 saved_buffer->height = surface->current.height; 1409 saved_buffer->height = surface->current.height;
1341 saved_buffer->x = sx; 1410 saved_buffer->x = view->container->surface_x + sx;
1342 saved_buffer->y = sy; 1411 saved_buffer->y = view->container->surface_y + sy;
1343 saved_buffer->transform = surface->current.transform; 1412 saved_buffer->transform = surface->current.transform;
1344 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); 1413 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1345 wl_list_insert(&view->saved_buffers, &saved_buffer->link); 1414 wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
1346 } 1415 }
1347} 1416}
1348 1417
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 921b7d19..c84320bd 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -48,10 +48,10 @@ struct sway_output *workspace_get_initial_output(const char *name) {
48 if (focus && focus->type == N_WORKSPACE) { 48 if (focus && focus->type == N_WORKSPACE) {
49 return focus->sway_workspace->output; 49 return focus->sway_workspace->output;
50 } else if (focus && focus->type == N_CONTAINER) { 50 } else if (focus && focus->type == N_CONTAINER) {
51 return focus->sway_container->workspace->output; 51 return focus->sway_container->pending.workspace->output;
52 } 52 }
53 // Fallback to the first output or noop output for headless 53 // Fallback to the first output or the headless output
54 return root->outputs->length ? root->outputs->items[0] : root->noop_output; 54 return root->outputs->length ? root->outputs->items[0] : root->fallback_output;
55} 55}
56 56
57struct sway_workspace *workspace_create(struct sway_output *output, 57struct sway_workspace *workspace_create(struct sway_output *output,
@@ -222,10 +222,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
222 // not a command about workspaces 222 // not a command about workspaces
223 if (strcmp(_target, "next") == 0 || 223 if (strcmp(_target, "next") == 0 ||
224 strcmp(_target, "prev") == 0 || 224 strcmp(_target, "prev") == 0 ||
225 strncmp(_target, "next_on_output", 225 strcmp(_target, "next_on_output") == 0 ||
226 strlen("next_on_output")) == 0 || 226 strcmp(_target, "prev_on_output") == 0 ||
227 strncmp(_target, "prev_on_output",
228 strlen("next_on_output")) == 0 ||
229 strcmp(_target, "number") == 0 || 227 strcmp(_target, "number") == 0 ||
230 strcmp(_target, "back_and_forth") == 0 || 228 strcmp(_target, "back_and_forth") == 0 ||
231 strcmp(_target, "current") == 0) { 229 strcmp(_target, "current") == 0) {
@@ -363,11 +361,11 @@ struct sway_workspace *workspace_by_name(const char *name) {
363 if (current && strcmp(name, "prev") == 0) { 361 if (current && strcmp(name, "prev") == 0) {
364 return workspace_prev(current); 362 return workspace_prev(current);
365 } else if (current && strcmp(name, "prev_on_output") == 0) { 363 } else if (current && strcmp(name, "prev_on_output") == 0) {
366 return workspace_output_prev(current, false); 364 return workspace_output_prev(current);
367 } else if (current && strcmp(name, "next") == 0) { 365 } else if (current && strcmp(name, "next") == 0) {
368 return workspace_next(current); 366 return workspace_next(current);
369 } else if (current && strcmp(name, "next_on_output") == 0) { 367 } else if (current && strcmp(name, "next_on_output") == 0) {
370 return workspace_output_next(current, false); 368 return workspace_output_next(current);
371 } else if (strcmp(name, "current") == 0) { 369 } else if (strcmp(name, "current") == 0) {
372 return current; 370 return current;
373 } else if (strcasecmp(name, "back_and_forth") == 0) { 371 } else if (strcasecmp(name, "back_and_forth") == 0) {
@@ -530,7 +528,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) {
530 * otherwise the next one is returned. 528 * otherwise the next one is returned.
531 */ 529 */
532static struct sway_workspace *workspace_output_prev_next_impl( 530static struct sway_workspace *workspace_output_prev_next_impl(
533 struct sway_output *output, int dir, bool create) { 531 struct sway_output *output, int dir) {
534 struct sway_seat *seat = input_manager_current_seat(); 532 struct sway_seat *seat = input_manager_current_seat();
535 struct sway_workspace *workspace = seat_get_focused_workspace(seat); 533 struct sway_workspace *workspace = seat_get_focused_workspace(seat);
536 if (!workspace) { 534 if (!workspace) {
@@ -540,46 +538,43 @@ static struct sway_workspace *workspace_output_prev_next_impl(
540 } 538 }
541 539
542 int index = list_find(output->workspaces, workspace); 540 int index = list_find(output->workspaces, workspace);
543 if (!workspace_is_empty(workspace) && create &&
544 (index + dir < 0 || index + dir == output->workspaces->length)) {
545 struct sway_output *output = workspace->output;
546 char *next = workspace_next_name(output->wlr_output->name);
547 workspace_create(output, next);
548 free(next);
549 }
550 size_t new_index = wrap(index + dir, output->workspaces->length); 541 size_t new_index = wrap(index + dir, output->workspaces->length);
551 return output->workspaces->items[new_index]; 542 return output->workspaces->items[new_index];
552} 543}
553 544
554struct sway_workspace *workspace_output_next( 545
555 struct sway_workspace *current, bool create) { 546struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
556 return workspace_output_prev_next_impl(current->output, 1, create); 547 return workspace_output_prev_next_impl(current->output, 1);
557} 548}
558 549
559struct sway_workspace *workspace_output_prev( 550struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
560 struct sway_workspace *current, bool create) { 551 return workspace_output_prev_next_impl(current->output, -1);
561 return workspace_output_prev_next_impl(current->output, -1, create);
562} 552}
563 553
564bool workspace_switch(struct sway_workspace *workspace, 554struct sway_workspace *workspace_auto_back_and_forth(
565 bool no_auto_back_and_forth) { 555 struct sway_workspace *workspace) {
566 struct sway_seat *seat = input_manager_current_seat(); 556 struct sway_seat *seat = input_manager_current_seat();
567 struct sway_workspace *active_ws = NULL; 557 struct sway_workspace *active_ws = NULL;
568 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); 558 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
569 if (focus && focus->type == N_WORKSPACE) { 559 if (focus && focus->type == N_WORKSPACE) {
570 active_ws = focus->sway_workspace; 560 active_ws = focus->sway_workspace;
571 } else if (focus && focus->type == N_CONTAINER) { 561 } else if (focus && focus->type == N_CONTAINER) {
572 active_ws = focus->sway_container->workspace; 562 active_ws = focus->sway_container->pending.workspace;
573 } 563 }
574 564
575 if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws 565 if (config->auto_back_and_forth && active_ws && active_ws == workspace &&
576 && active_ws == workspace && seat->prev_workspace_name) { 566 seat->prev_workspace_name) {
577 struct sway_workspace *new_ws = 567 struct sway_workspace *new_ws =
578 workspace_by_name(seat->prev_workspace_name); 568 workspace_by_name(seat->prev_workspace_name);
579 workspace = new_ws ? 569 workspace = new_ws ?
580 new_ws : 570 new_ws :
581 workspace_create(NULL, seat->prev_workspace_name); 571 workspace_create(NULL, seat->prev_workspace_name);
582 } 572 }
573 return workspace;
574}
575
576bool workspace_switch(struct sway_workspace *workspace) {
577 struct sway_seat *seat = input_manager_current_seat();
583 578
584 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", 579 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
585 workspace, workspace->name); 580 workspace, workspace->name);
@@ -736,13 +731,13 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws,
736} 731}
737 732
738static void set_workspace(struct sway_container *container, void *data) { 733static void set_workspace(struct sway_container *container, void *data) {
739 container->workspace = container->parent->workspace; 734 container->pending.workspace = container->pending.parent->pending.workspace;
740} 735}
741 736
742static void workspace_attach_tiling(struct sway_workspace *ws, 737static void workspace_attach_tiling(struct sway_workspace *ws,
743 struct sway_container *con) { 738 struct sway_container *con) {
744 list_add(ws->tiling, con); 739 list_add(ws->tiling, con);
745 con->workspace = ws; 740 con->pending.workspace = ws;
746 container_for_each_child(con, set_workspace, NULL); 741 container_for_each_child(con, set_workspace, NULL);
747 container_handle_fullscreen_reparent(con); 742 container_handle_fullscreen_reparent(con);
748 workspace_update_representation(ws); 743 workspace_update_representation(ws);
@@ -753,7 +748,7 @@ static void workspace_attach_tiling(struct sway_workspace *ws,
753struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { 748struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
754 struct sway_container *fs = ws->fullscreen; 749 struct sway_container *fs = ws->fullscreen;
755 struct sway_container *middle = container_create(NULL); 750 struct sway_container *middle = container_create(NULL);
756 middle->layout = ws->layout; 751 middle->pending.layout = ws->layout;
757 while (ws->tiling->length) { 752 while (ws->tiling->length) {
758 struct sway_container *child = ws->tiling->items[0]; 753 struct sway_container *child = ws->tiling->items[0];
759 container_detach(child); 754 container_detach(child);
@@ -771,9 +766,9 @@ void workspace_unwrap_children(struct sway_workspace *ws,
771 return; 766 return;
772 } 767 }
773 768
774 ws->layout = wrap->layout; 769 ws->layout = wrap->pending.layout;
775 while (wrap->children->length) { 770 while (wrap->pending.children->length) {
776 struct sway_container *child = wrap->children->items[0]; 771 struct sway_container *child = wrap->pending.children->items[0];
777 container_detach(child); 772 container_detach(child);
778 workspace_add_tiling(ws, child); 773 workspace_add_tiling(ws, child);
779 } 774 }
@@ -793,14 +788,18 @@ void workspace_detach(struct sway_workspace *workspace) {
793 788
794struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, 789struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
795 struct sway_container *con) { 790 struct sway_container *con) {
796 if (con->workspace) { 791 if (con->pending.workspace) {
792 struct sway_container *old_parent = con->pending.parent;
797 container_detach(con); 793 container_detach(con);
794 if (old_parent) {
795 container_reap_empty(old_parent);
796 }
798 } 797 }
799 if (config->default_layout != L_NONE) { 798 if (config->default_layout != L_NONE) {
800 con = container_split(con, config->default_layout); 799 con = container_split(con, config->default_layout);
801 } 800 }
802 list_add(workspace->tiling, con); 801 list_add(workspace->tiling, con);
803 con->workspace = workspace; 802 con->pending.workspace = workspace;
804 container_for_each_child(con, set_workspace, NULL); 803 container_for_each_child(con, set_workspace, NULL);
805 container_handle_fullscreen_reparent(con); 804 container_handle_fullscreen_reparent(con);
806 workspace_update_representation(workspace); 805 workspace_update_representation(workspace);
@@ -811,11 +810,11 @@ struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
811 810
812void workspace_add_floating(struct sway_workspace *workspace, 811void workspace_add_floating(struct sway_workspace *workspace,
813 struct sway_container *con) { 812 struct sway_container *con) {
814 if (con->workspace) { 813 if (con->pending.workspace) {
815 container_detach(con); 814 container_detach(con);
816 } 815 }
817 list_add(workspace->floating, con); 816 list_add(workspace->floating, con);
818 con->workspace = workspace; 817 con->pending.workspace = workspace;
819 container_for_each_child(con, set_workspace, NULL); 818 container_for_each_child(con, set_workspace, NULL);
820 container_handle_fullscreen_reparent(con); 819 container_handle_fullscreen_reparent(con);
821 node_set_dirty(&workspace->node); 820 node_set_dirty(&workspace->node);
@@ -825,7 +824,7 @@ void workspace_add_floating(struct sway_workspace *workspace,
825void workspace_insert_tiling_direct(struct sway_workspace *workspace, 824void workspace_insert_tiling_direct(struct sway_workspace *workspace,
826 struct sway_container *con, int index) { 825 struct sway_container *con, int index) {
827 list_insert(workspace->tiling, index, con); 826 list_insert(workspace->tiling, index, con);
828 con->workspace = workspace; 827 con->pending.workspace = workspace;
829 container_for_each_child(con, set_workspace, NULL); 828 container_for_each_child(con, set_workspace, NULL);
830 container_handle_fullscreen_reparent(con); 829 container_handle_fullscreen_reparent(con);
831 workspace_update_representation(workspace); 830 workspace_update_representation(workspace);
@@ -835,7 +834,7 @@ void workspace_insert_tiling_direct(struct sway_workspace *workspace,
835 834
836struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, 835struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
837 struct sway_container *con, int index) { 836 struct sway_container *con, int index) {
838 if (con->workspace) { 837 if (con->pending.workspace) {
839 container_detach(con); 838 container_detach(con);
840 } 839 }
841 if (config->default_layout != L_NONE) { 840 if (config->default_layout != L_NONE) {
@@ -845,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
845 return con; 844 return con;
846} 845}
847 846
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
848void workspace_add_gaps(struct sway_workspace *ws) { 857void workspace_add_gaps(struct sway_workspace *ws) {
849 if (config->smart_gaps) { 858 if (config->smart_gaps == SMART_GAPS_ON
850 struct sway_seat *seat = input_manager_get_default_seat(); 859 && workspace_has_single_visible_container(ws)) {
851 struct sway_container *focus = 860 ws->current_gaps.top = 0;
852 seat_get_focus_inactive_tiling(seat, ws); 861 ws->current_gaps.right = 0;
853 if (focus && !focus->view) { 862 ws->current_gaps.bottom = 0;
854 focus = seat_get_focus_inactive_view(seat, &focus->node); 863 ws->current_gaps.left = 0;
855 } 864 return;
856 if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { 865 }
857 ws->current_gaps.top = 0; 866
858 ws->current_gaps.right = 0; 867 if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER
859 ws->current_gaps.bottom = 0; 868 && !workspace_has_single_visible_container(ws)) {
860 ws->current_gaps.left = 0; 869 ws->current_gaps.top = 0;
861 return; 870 ws->current_gaps.right = 0;
862 } 871 ws->current_gaps.bottom = 0;
872 ws->current_gaps.left = 0;
873 } else {
874 ws->current_gaps = ws->gaps_outer;
863 } 875 }
864 876
865 ws->current_gaps = ws->gaps_outer;
866 // Add inner gaps and make sure we don't turn out negative 877 // Add inner gaps and make sure we don't turn out negative
867 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); 878 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);
868 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); 879 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);
@@ -905,7 +916,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace,
905 enum sway_container_layout old_layout = workspace->layout; 916 enum sway_container_layout old_layout = workspace->layout;
906 struct sway_container *middle = workspace_wrap_children(workspace); 917 struct sway_container *middle = workspace_wrap_children(workspace);
907 workspace->layout = layout; 918 workspace->layout = layout;
908 middle->layout = old_layout; 919 middle->pending.layout = old_layout;
909 920
910 struct sway_seat *seat; 921 struct sway_seat *seat;
911 wl_list_for_each(seat, &server.input->seats, link) { 922 wl_list_for_each(seat, &server.input->seats, link) {
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c
new file mode 100644
index 00000000..6c70c785
--- /dev/null
+++ b/sway/xdg_activation_v1.c
@@ -0,0 +1,20 @@
1#include <wlr/types/wlr_xdg_activation_v1.h>
2#include "sway/tree/view.h"
3
4void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
5 void *data) {
6 const struct wlr_xdg_activation_v1_request_activate_event *event = data;
7
8 if (!wlr_surface_is_xdg_surface(event->surface)) {
9 return;
10 }
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;
15 if (!xdg_surface->mapped || view == NULL) {
16 return;
17 }
18
19 view_request_activate(view);
20}
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c
index e7c3ea73..ec9e8d68 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,8 +23,32 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener,
23 void *data) { 23 void *data) {
24 struct sway_xdg_decoration *deco = 24 struct sway_xdg_decoration *deco =
25 wl_container_of(listener, deco, request_mode); 25 wl_container_of(listener, deco, request_mode);
26 struct sway_view *view = deco->view;
27 enum wlr_xdg_toplevel_decoration_v1_mode mode =
28 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
29 enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
30 deco->wlr_xdg_decoration->requested_mode;
31
32 bool floating;
33 if (view->container) {
34 floating = container_is_floating(view->container);
35 bool csd = false;
36 csd = client_mode ==
37 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
38 view_update_csd_from_client(view, csd);
39 arrange_container(view->container);
40 transaction_commit_dirty();
41 } else {
42 floating = view->impl->wants_floating &&
43 view->impl->wants_floating(view);
44 }
45
46 if (floating && client_mode) {
47 mode = client_mode;
48 }
49
26 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, 50 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
27 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); 51 mode);
28} 52}
29 53
30void handle_xdg_decoration(struct wl_listener *listener, void *data) { 54void handle_xdg_decoration(struct wl_listener *listener, void *data) {
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 231c1ad7..5e4ebd97 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -51,10 +51,6 @@ static void swaybar_output_free(struct swaybar_output *output) {
51 if (output->surface != NULL) { 51 if (output->surface != NULL) {
52 wl_surface_destroy(output->surface); 52 wl_surface_destroy(output->surface);
53 } 53 }
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); 54 wl_output_destroy(output->output);
59 destroy_buffer(&output->buffers[0]); 55 destroy_buffer(&output->buffers[0]);
60 destroy_buffer(&output->buffers[1]); 56 destroy_buffer(&output->buffers[1]);
@@ -90,7 +86,7 @@ static void layer_surface_closed(void *_output,
90 swaybar_output_free(output); 86 swaybar_output_free(output);
91} 87}
92 88
93struct zwlr_layer_surface_v1_listener layer_surface_listener = { 89static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
94 .configure = layer_surface_configure, 90 .configure = layer_surface_configure,
95 .closed = layer_surface_closed, 91 .closed = layer_surface_closed,
96}; 92};
@@ -114,10 +110,9 @@ static void add_layer_surface(struct swaybar_output *output) {
114 110
115 if (overlay) { 111 if (overlay) {
116 // Empty input region 112 // Empty input region
117 output->input_region = wl_compositor_create_region(bar->compositor); 113 struct wl_region *region = wl_compositor_create_region(bar->compositor);
118 assert(output->input_region); 114 wl_surface_set_input_region(output->surface, region);
119 115 wl_region_destroy(region);
120 wl_surface_set_input_region(output->surface, output->input_region);
121 } 116 }
122 117
123 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); 118 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position);
@@ -172,7 +167,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
172 if (bar->status) { 167 if (bar->status) {
173 sway_log(SWAY_DEBUG, "Sending %s signal to status command", 168 sway_log(SWAY_DEBUG, "Sending %s signal to status command",
174 visible ? "cont" : "stop"); 169 visible ? "cont" : "stop");
175 kill(bar->status->pid, visible ? 170 kill(-bar->status->pid, visible ?
176 bar->status->cont_signal : bar->status->stop_signal); 171 bar->status->cont_signal : bar->status->stop_signal);
177 } 172 }
178 } 173 }
@@ -230,7 +225,7 @@ static void output_scale(void *data, struct wl_output *wl_output,
230 } 225 }
231} 226}
232 227
233struct wl_output_listener output_listener = { 228static const struct wl_output_listener output_listener = {
234 .geometry = output_geometry, 229 .geometry = output_geometry,
235 .mode = output_mode, 230 .mode = output_mode,
236 .done = output_done, 231 .done = output_done,
@@ -307,7 +302,7 @@ static void xdg_output_handle_description(void *data,
307 } 302 }
308} 303}
309 304
310struct zxdg_output_v1_listener xdg_output_listener = { 305static const struct zxdg_output_v1_listener xdg_output_listener = {
311 .logical_position = xdg_output_handle_logical_position, 306 .logical_position = xdg_output_handle_logical_position,
312 .logical_size = xdg_output_handle_logical_size, 307 .logical_size = xdg_output_handle_logical_size,
313 .done = xdg_output_handle_done, 308 .done = xdg_output_handle_done,
@@ -461,13 +456,28 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
461 456
462static void display_in(int fd, short mask, void *data) { 457static void display_in(int fd, short mask, void *data) {
463 struct swaybar *bar = data; 458 struct swaybar *bar = data;
459 if (mask & (POLLHUP | POLLERR)) {
460 if (mask & POLLERR) {
461 sway_log(SWAY_ERROR, "Wayland display poll error");
462 }
463 bar->running = false;
464 return;
465 }
464 if (wl_display_dispatch(bar->display) == -1) { 466 if (wl_display_dispatch(bar->display) == -1) {
467 sway_log(SWAY_ERROR, "wl_display_dispatch failed");
465 bar->running = false; 468 bar->running = false;
466 } 469 }
467} 470}
468 471
469static void ipc_in(int fd, short mask, void *data) { 472static void ipc_in(int fd, short mask, void *data) {
470 struct swaybar *bar = data; 473 struct swaybar *bar = data;
474 if (mask & (POLLHUP | POLLERR)) {
475 if (mask & POLLERR) {
476 sway_log(SWAY_ERROR, "IPC poll error");
477 }
478 bar->running = false;
479 return;
480 }
471 if (handle_ipc_readable(bar)) { 481 if (handle_ipc_readable(bar)) {
472 set_bar_dirty(bar); 482 set_bar_dirty(bar);
473 } 483 }
diff --git a/swaybar/config.c b/swaybar/config.c
index abedaec0..5e828773 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -26,7 +26,7 @@ struct swaybar_config *init_config(void) {
26 config->status_command = NULL; 26 config->status_command = NULL;
27 config->pango_markup = false; 27 config->pango_markup = false;
28 config->position = parse_position("bottom"); 28 config->position = parse_position("bottom");
29 config->font = strdup("monospace 10"); 29 config->font_description = pango_font_description_from_string("monospace 10");
30 config->mode = strdup("dock"); 30 config->mode = strdup("dock");
31 config->hidden_state = strdup("hide"); 31 config->hidden_state = strdup("hide");
32 config->sep_symbol = NULL; 32 config->sep_symbol = NULL;
@@ -105,7 +105,7 @@ void free_tray_binding(struct tray_binding *binding) {
105 105
106void free_config(struct swaybar_config *config) { 106void free_config(struct swaybar_config *config) {
107 free(config->status_command); 107 free(config->status_command);
108 free(config->font); 108 pango_font_description_free(config->font_description);
109 free(config->mode); 109 free(config->mode);
110 free(config->hidden_state); 110 free(config->hidden_state);
111 free(config->sep_symbol); 111 free(config->sep_symbol);
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 4bcd5843..6d00befb 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -28,6 +28,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
28 } 28 }
29} 29}
30 30
31static bool i3bar_parse_json_color(json_object *json, uint32_t *color) {
32 if (!json) {
33 return false;
34 }
35
36 const char *hexstring = json_object_get_string(json);
37 bool color_set = parse_color(hexstring, color);
38 if (!color_set) {
39 sway_log(SWAY_ERROR, "Ignoring invalid block hexadecimal color string: %s", hexstring);
40 }
41 return color_set;
42}
43
31static void i3bar_parse_json(struct status_line *status, 44static void i3bar_parse_json(struct status_line *status,
32 struct json_object *json_array) { 45 struct json_object *json_array) {
33 struct i3bar_block *block, *tmp; 46 struct i3bar_block *block, *tmp;
@@ -68,13 +81,7 @@ static void i3bar_parse_json(struct status_line *status,
68 strdup(json_object_get_string(full_text)) : NULL; 81 strdup(json_object_get_string(full_text)) : NULL;
69 block->short_text = short_text ? 82 block->short_text = short_text ?
70 strdup(json_object_get_string(short_text)) : NULL; 83 strdup(json_object_get_string(short_text)) : NULL;
71 if (color) { 84 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) { 85 if (min_width) {
79 json_type type = json_object_get_type(min_width); 86 json_type type = json_object_get_type(min_width);
80 if (type == json_type_int) { 87 if (type == json_type_int) {
@@ -100,14 +107,8 @@ static void i3bar_parse_json(struct status_line *status,
100 block->separator_block_width = separator_block_width ? 107 block->separator_block_width = separator_block_width ?
101 json_object_get_int(separator_block_width) : 9; 108 json_object_get_int(separator_block_width) : 9;
102 // Airblader features 109 // Airblader features
103 const char *hex = background ? json_object_get_string(background) : NULL; 110 i3bar_parse_json_color(background, &block->background);
104 if (hex && !parse_color(hex, &block->background)) { 111 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; 112 block->border_top = border_top ? json_object_get_int(border_top) : 1;
112 block->border_bottom = border_bottom ? 113 block->border_bottom = border_bottom ?
113 json_object_get_int(border_bottom) : 1; 114 json_object_get_int(border_bottom) : 1;
diff --git a/swaybar/input.c b/swaybar/input.c
index 4fe6dd93..c8c8f0d4 100644
--- a/swaybar/input.c
+++ b/swaybar/input.c
@@ -101,6 +101,8 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
101 wl_fixed_t surface_x, wl_fixed_t surface_y) { 101 wl_fixed_t surface_x, wl_fixed_t surface_y) {
102 struct swaybar_seat *seat = data; 102 struct swaybar_seat *seat = data;
103 struct swaybar_pointer *pointer = &seat->pointer; 103 struct swaybar_pointer *pointer = &seat->pointer;
104 seat->pointer.x = wl_fixed_to_double(surface_x);
105 seat->pointer.y = wl_fixed_to_double(surface_y);
104 pointer->serial = serial; 106 pointer->serial = serial;
105 struct swaybar_output *output; 107 struct swaybar_output *output;
106 wl_list_for_each(output, &seat->bar->outputs, link) { 108 wl_list_for_each(output, &seat->bar->outputs, link) {
@@ -140,13 +142,11 @@ static bool check_bindings(struct swaybar *bar, uint32_t button,
140 142
141static bool process_hotspots(struct swaybar_output *output, 143static bool process_hotspots(struct swaybar_output *output,
142 double x, double y, uint32_t button) { 144 double x, double y, uint32_t button) {
143 double px = x * output->scale;
144 double py = y * output->scale;
145 struct swaybar_hotspot *hotspot; 145 struct swaybar_hotspot *hotspot;
146 wl_list_for_each(hotspot, &output->hotspots, link) { 146 wl_list_for_each(hotspot, &output->hotspots, link) {
147 if (px >= hotspot->x && py >= hotspot->y 147 if (x >= hotspot->x && y >= hotspot->y
148 && px < hotspot->x + hotspot->width 148 && x < hotspot->x + hotspot->width
149 && py < hotspot->y + hotspot->height) { 149 && y < hotspot->y + hotspot->height) {
150 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 150 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
151 button, hotspot->data)) { 151 button, hotspot->data)) {
152 return true; 152 return true;
@@ -339,7 +339,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
339 seat->axis[axis].discrete_steps += abs(discrete); 339 seat->axis[axis].discrete_steps += abs(discrete);
340} 340}
341 341
342static struct wl_pointer_listener pointer_listener = { 342static const struct wl_pointer_listener pointer_listener = {
343 .enter = wl_pointer_enter, 343 .enter = wl_pointer_enter,
344 .leave = wl_pointer_leave, 344 .leave = wl_pointer_leave,
345 .motion = wl_pointer_motion, 345 .motion = wl_pointer_motion,
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 6bbe9408..9d81a9fb 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -147,8 +147,10 @@ static bool ipc_parse_config(
147 147
148 json_object *font = json_object_object_get(bar_config, "font"); 148 json_object *font = json_object_object_get(bar_config, "font");
149 if (font) { 149 if (font) {
150 free(config->font); 150 pango_font_description_free(config->font_description);
151 config->font = parse_font(json_object_get_string(font)); 151 char *font_value = parse_font(json_object_get_string(font));
152 config->font_description = pango_font_description_from_string(font_value);
153 free(font_value);
152 } 154 }
153 155
154 json_object *gaps = json_object_object_get(bar_config, "gaps"); 156 json_object *gaps = json_object_object_get(bar_config, "gaps");
@@ -485,8 +487,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload,
485 destroy_layer_surface(output); 487 destroy_layer_surface(output);
486 wl_list_remove(&output->link); 488 wl_list_remove(&output->link);
487 wl_list_insert(&bar->unused_outputs, &output->link); 489 wl_list_insert(&bar->unused_outputs, &output->link);
488 } else if (!oldcfg->font || !newcfg->font || 490 } 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 491 output->height = 0; // force update height
491 } 492 }
492 } 493 }
@@ -547,9 +548,23 @@ bool handle_ipc_readable(struct swaybar *bar) {
547 return false; 548 return false;
548 } 549 }
549 550
550 json_object *result = json_tokener_parse(resp->payload); 551 // The default depth of 32 is too small to represent some nested layouts, but
551 if (!result) { 552 // we can't pass INT_MAX here because json-c (as of this writing) prefaults
552 sway_log(SWAY_ERROR, "failed to parse payload as json"); 553 // all the memory for its stack.
554 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
555 if (!tok) {
556 sway_log_errno(SWAY_ERROR, "failed to create tokener");
557 free_ipc_response(resp);
558 return false;
559 }
560
561 json_object *result = json_tokener_parse_ex(tok, resp->payload, -1);
562 enum json_tokener_error err = json_tokener_get_error(tok);
563 json_tokener_free(tok);
564
565 if (err != json_tokener_success) {
566 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
567 json_tokener_error_desc(err));
553 free_ipc_response(resp); 568 free_ipc_response(resp);
554 return false; 569 return false;
555 } 570 }
diff --git a/swaybar/main.c b/swaybar/main.c
index 5c36d66b..a44c1e63 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -18,7 +18,7 @@ int main(int argc, char **argv) {
18 char *socket_path = NULL; 18 char *socket_path = NULL;
19 bool debug = false; 19 bool debug = false;
20 20
21 static struct option long_options[] = { 21 static const struct option long_options[] = {
22 {"help", no_argument, NULL, 'h'}, 22 {"help", no_argument, NULL, 'h'},
23 {"version", no_argument, NULL, 'v'}, 23 {"version", no_argument, NULL, 'v'},
24 {"socket", required_argument, NULL, 's'}, 24 {"socket", required_argument, NULL, 's'},
diff --git a/swaybar/render.c b/swaybar/render.c
index df066622..a878805e 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -5,7 +5,7 @@
5#include <stdlib.h> 5#include <stdlib.h>
6#include <stdint.h> 6#include <stdint.h>
7#include <string.h> 7#include <string.h>
8#include "cairo.h" 8#include "cairo_util.h"
9#include "pango.h" 9#include "pango.h"
10#include "pool-buffer.h" 10#include "pool-buffer.h"
11#include "swaybar/bar.h" 11#include "swaybar/bar.h"
@@ -14,6 +14,7 @@
14#include "swaybar/ipc.h" 14#include "swaybar/ipc.h"
15#include "swaybar/render.h" 15#include "swaybar/render.h"
16#include "swaybar/status_line.h" 16#include "swaybar/status_line.h"
17#include "log.h"
17#if HAVE_TRAY 18#if HAVE_TRAY
18#include "swaybar/tray/tray.h" 19#include "swaybar/tray/tray.h"
19#endif 20#endif
@@ -23,28 +24,51 @@ static const int WS_HORIZONTAL_PADDING = 5;
23static const double WS_VERTICAL_PADDING = 1.5; 24static const double WS_VERTICAL_PADDING = 1.5;
24static const double BORDER_WIDTH = 1; 25static const double BORDER_WIDTH = 1;
25 26
26static uint32_t render_status_line_error(cairo_t *cairo, 27struct render_context {
27 struct swaybar_output *output, double *x) { 28 cairo_t *cairo;
29 struct swaybar_output *output;
30 cairo_font_options_t *textaa_sharp;
31 cairo_font_options_t *textaa_safe;
32 uint32_t background_color;
33};
34
35static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {
36 uint32_t salpha = fontcolor & 0xFF;
37 uint32_t balpha = ctx->background_color & 0xFF;
38
39 // Subpixel antialiasing requires blend be done in cairo, not compositor
40 cairo_font_options_t *fo = salpha == balpha ?
41 ctx->textaa_sharp : ctx->textaa_safe;
42 cairo_set_font_options(ctx->cairo, fo);
43
44 // Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE'
45 cairo_operator_t op = salpha == 0xFF ?
46 CAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE;
47 cairo_set_operator(ctx->cairo, op);
48}
49
50static uint32_t render_status_line_error(struct render_context *ctx, double *x) {
51 struct swaybar_output *output = ctx->output;
28 const char *error = output->bar->status->text; 52 const char *error = output->bar->status->text;
29 if (!error) { 53 if (!error) {
30 return 0; 54 return 0;
31 } 55 }
32 56
33 uint32_t height = output->height * output->scale; 57 uint32_t height = output->height;
34 58
59 cairo_t *cairo = ctx->cairo;
35 cairo_set_source_u32(cairo, 0xFF0000FF); 60 cairo_set_source_u32(cairo, 0xFF0000FF);
36 61
37 int margin = 3 * output->scale; 62 int margin = 3;
38 double ws_vertical_padding = 63 double ws_vertical_padding = output->bar->config->status_padding;
39 output->bar->config->status_padding * output->scale;
40 64
41 char *font = output->bar->config->font; 65 PangoFontDescription *font = output->bar->config->font_description;
42 int text_width, text_height; 66 int text_width, text_height;
43 get_text_size(cairo, font, &text_width, &text_height, NULL, 67 get_text_size(cairo, font, &text_width, &text_height, NULL,
44 output->scale, false, "%s", error); 68 1, false, "%s", error);
45 69
46 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 70 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
47 uint32_t ideal_surface_height = ideal_height / output->scale; 71 uint32_t ideal_surface_height = ideal_height;
48 if (!output->bar->config->height && 72 if (!output->bar->config->height &&
49 output->height < ideal_surface_height) { 73 output->height < ideal_surface_height) {
50 return ideal_surface_height; 74 return ideal_surface_height;
@@ -53,42 +77,45 @@ static uint32_t render_status_line_error(cairo_t *cairo,
53 77
54 double text_y = height / 2.0 - text_height / 2.0; 78 double text_y = height / 2.0 - text_height / 2.0;
55 cairo_move_to(cairo, *x, (int)floor(text_y)); 79 cairo_move_to(cairo, *x, (int)floor(text_y));
56 pango_printf(cairo, font, output->scale, false, "%s", error); 80 choose_text_aa_mode(ctx, 0xFF0000FF);
81 render_text(cairo, font, 1, false, "%s", error);
57 *x -= margin; 82 *x -= margin;
58 return output->height; 83 return output->height;
59} 84}
60 85
61static uint32_t render_status_line_text(cairo_t *cairo, 86static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
62 struct swaybar_output *output, double *x) { 87 struct swaybar_output *output = ctx->output;
63 const char *text = output->bar->status->text; 88 const char *text = output->bar->status->text;
64 if (!text) { 89 if (!text) {
65 return 0; 90 return 0;
66 } 91 }
67 92
93 cairo_t *cairo = ctx->cairo;
68 struct swaybar_config *config = output->bar->config; 94 struct swaybar_config *config = output->bar->config;
69 cairo_set_source_u32(cairo, output->focused ? 95 uint32_t fontcolor = output->focused ?
70 config->colors.focused_statusline : config->colors.statusline); 96 config->colors.focused_statusline : config->colors.statusline;
97 cairo_set_source_u32(cairo, fontcolor);
71 98
72 int text_width, text_height; 99 int text_width, text_height;
73 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 100 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
74 output->scale, config->pango_markup, "%s", text); 101 1, config->pango_markup, "%s", text);
75 102
76 double ws_vertical_padding = config->status_padding * output->scale; 103 double ws_vertical_padding = config->status_padding;
77 int margin = 3 * output->scale; 104 int margin = 3;
78 105
79 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 106 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
80 uint32_t ideal_surface_height = ideal_height / output->scale; 107 uint32_t ideal_surface_height = ideal_height;
81 if (!output->bar->config->height && 108 if (!output->bar->config->height &&
82 output->height < ideal_surface_height) { 109 output->height < ideal_surface_height) {
83 return ideal_surface_height; 110 return ideal_surface_height;
84 } 111 }
85 112
86 *x -= text_width + margin; 113 *x -= text_width + margin;
87 uint32_t height = output->height * output->scale; 114 uint32_t height = output->height;
88 double text_y = height / 2.0 - text_height / 2.0; 115 double text_y = height / 2.0 - text_height / 2.0;
89 cairo_move_to(cairo, *x, (int)floor(text_y)); 116 cairo_move_to(cairo, *x, (int)floor(text_y));
90 pango_printf(cairo, config->font, output->scale, 117 choose_text_aa_mode(ctx, fontcolor);
91 config->pango_markup, "%s", text); 118 render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text);
92 *x -= margin; 119 *x -= margin;
93 return output->height; 120 return output->height;
94} 121}
@@ -96,6 +123,7 @@ static uint32_t render_status_line_text(cairo_t *cairo,
96static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, 123static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
97 double x, double y, double width, double height) { 124 double x, double y, double width, double height) {
98 cairo_save(cairo); 125 cairo_save(cairo);
126 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
99 cairo_set_source_u32(cairo, color); 127 cairo_set_source_u32(cairo, color);
100 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 128 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
101 cairo_rectangle(cairo, x, y, width, height); 129 cairo_rectangle(cairo, x, y, width, height);
@@ -109,6 +137,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
109 render_sharp_rectangle(cairo, color, x, y, width, height); 137 render_sharp_rectangle(cairo, color, x, y, width, height);
110 } else { 138 } else {
111 cairo_save(cairo); 139 cairo_save(cairo);
140 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
112 cairo_set_source_u32(cairo, color); 141 cairo_set_source_u32(cairo, color);
113 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 142 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
114 if (width == 1) { 143 if (width == 1) {
@@ -135,10 +164,10 @@ static enum hotspot_event_handling block_hotspot_callback(
135 struct i3bar_block *block = data; 164 struct i3bar_block *block = data;
136 struct status_line *status = output->bar->status; 165 struct status_line *status = output->bar->status;
137 return i3bar_block_send_click(status, block, x, y, 166 return i3bar_block_send_click(status, block, x, y,
138 x - (double)hotspot->x / output->scale, 167 x - (double)hotspot->x,
139 y - (double)hotspot->y / output->scale, 168 y - (double)hotspot->y,
140 (double)hotspot->width / output->scale, 169 (double)hotspot->width,
141 (double)hotspot->height / output->scale, 170 (double)hotspot->height,
142 output->scale, button); 171 output->scale, button);
143} 172}
144 173
@@ -146,9 +175,8 @@ static void i3bar_block_unref_callback(void *data) {
146 i3bar_block_unref(data); 175 i3bar_block_unref(data);
147} 176}
148 177
149static uint32_t render_status_block(cairo_t *cairo, 178static uint32_t render_status_block(struct render_context *ctx,
150 struct swaybar_output *output, struct i3bar_block *block, double *x, 179 struct i3bar_block *block, double *x, bool edge, bool use_short_text) {
151 bool edge, bool use_short_text) {
152 if (!block->full_text || !*block->full_text) { 180 if (!block->full_text || !*block->full_text) {
153 return 0; 181 return 0;
154 } 182 }
@@ -158,20 +186,21 @@ static uint32_t render_status_block(cairo_t *cairo,
158 text = block->short_text; 186 text = block->short_text;
159 } 187 }
160 188
189 cairo_t *cairo = ctx->cairo;
190 struct swaybar_output *output = ctx->output;
161 struct swaybar_config *config = output->bar->config; 191 struct swaybar_config *config = output->bar->config;
162
163 int text_width, text_height; 192 int text_width, text_height;
164 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 193 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
165 output->scale, block->markup, "%s", text); 194 block->markup, "%s", text);
166 195
167 int margin = 3 * output->scale; 196 int margin = 3;
168 double ws_vertical_padding = config->status_padding * output->scale; 197 double ws_vertical_padding = config->status_padding;
169 198
170 int width = text_width; 199 int width = text_width;
171 if (block->min_width_str) { 200 if (block->min_width_str) {
172 int w; 201 int w;
173 get_text_size(cairo, config->font, &w, NULL, NULL, 202 get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup,
174 output->scale, block->markup, "%s", block->min_width_str); 203 "%s", block->min_width_str);
175 block->min_width = w; 204 block->min_width = w;
176 } 205 }
177 if (width < block->min_width) { 206 if (width < block->min_width) {
@@ -180,30 +209,30 @@ static uint32_t render_status_block(cairo_t *cairo,
180 209
181 double block_width = width; 210 double block_width = width;
182 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 211 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
183 uint32_t ideal_surface_height = ideal_height / output->scale; 212 uint32_t ideal_surface_height = ideal_height;
184 if (!output->bar->config->height && 213 if (!output->bar->config->height &&
185 output->height < ideal_surface_height) { 214 output->height < ideal_surface_height) {
186 return ideal_surface_height; 215 return ideal_surface_height;
187 } 216 }
188 217
189 *x -= width; 218 *x -= width;
190 if ((block->border || block->urgent) && block->border_left > 0) { 219 if ((block->border_set || block->urgent) && block->border_left > 0) {
191 *x -= (block->border_left * output->scale + margin); 220 *x -= (block->border_left + margin);
192 block_width += block->border_left * output->scale + margin; 221 block_width += block->border_left + margin;
193 } 222 }
194 if ((block->border || block->urgent) && block->border_right > 0) { 223 if ((block->border_set || block->urgent) && block->border_right > 0) {
195 *x -= (block->border_right * output->scale + margin); 224 *x -= (block->border_right + margin);
196 block_width += block->border_right * output->scale + margin; 225 block_width += block->border_right + margin;
197 } 226 }
198 227
199 int sep_width, sep_height; 228 int sep_width, sep_height;
200 int sep_block_width = block->separator_block_width; 229 int sep_block_width = block->separator_block_width;
201 if (!edge) { 230 if (!edge) {
202 if (config->sep_symbol) { 231 if (config->sep_symbol) {
203 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 232 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
204 output->scale, false, "%s", config->sep_symbol); 233 1, false, "%s", config->sep_symbol);
205 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 234 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
206 uint32_t _ideal_surface_height = _ideal_height / output->scale; 235 uint32_t _ideal_surface_height = _ideal_height;
207 if (!output->bar->config->height && 236 if (!output->bar->config->height &&
208 output->height < _ideal_surface_height) { 237 output->height < _ideal_surface_height) {
209 return _ideal_surface_height; 238 return _ideal_surface_height;
@@ -214,10 +243,10 @@ static uint32_t render_status_block(cairo_t *cairo,
214 } 243 }
215 *x -= sep_block_width; 244 *x -= sep_block_width;
216 } else if (config->status_edge_padding) { 245 } else if (config->status_edge_padding) {
217 *x -= config->status_edge_padding * output->scale; 246 *x -= config->status_edge_padding;
218 } 247 }
219 248
220 uint32_t height = output->height * output->scale; 249 uint32_t height = output->height;
221 if (output->bar->status->click_events) { 250 if (output->bar->status->click_events) {
222 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 251 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
223 hotspot->x = *x; 252 hotspot->x = *x;
@@ -240,23 +269,26 @@ static uint32_t render_status_block(cairo_t *cairo,
240 if (bg_color) { 269 if (bg_color) {
241 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, 270 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
242 block_width, render_height); 271 block_width, render_height);
272 ctx->background_color = bg_color;
243 } 273 }
244 274
245 uint32_t border_color = block->urgent 275 uint32_t border_color = block->urgent
246 ? config->colors.urgent_workspace.border : block->border; 276 ? config->colors.urgent_workspace.border : block->border;
247 if (border_color && block->border_top > 0) { 277 if (block->border_set || block->urgent) {
248 render_sharp_line(cairo, border_color, x_pos, y_pos, 278 if (block->border_top > 0) {
249 block_width, block->border_top * output->scale); 279 render_sharp_line(cairo, border_color, x_pos, y_pos,
250 } 280 block_width, block->border_top);
251 if (border_color && block->border_bottom > 0) { 281 }
252 render_sharp_line(cairo, border_color, x_pos, 282 if (block->border_bottom > 0) {
253 y_pos + render_height - block->border_bottom * output->scale, 283 render_sharp_line(cairo, border_color, x_pos,
254 block_width, block->border_bottom * output->scale); 284 y_pos + render_height - block->border_bottom,
255 } 285 block_width, block->border_bottom);
256 if (border_color && block->border_left > 0) { 286 }
257 render_sharp_line(cairo, border_color, x_pos, y_pos, 287 if (block->border_left > 0) {
258 block->border_left * output->scale, render_height); 288 render_sharp_line(cairo, border_color, x_pos, y_pos,
259 x_pos += block->border_left * output->scale + margin; 289 block->border_left, render_height);
290 }
291 x_pos += block->border_left + margin;
260 } 292 }
261 293
262 double offset = 0; 294 double offset = 0;
@@ -274,30 +306,35 @@ static uint32_t render_status_block(cairo_t *cairo,
274 color = block->color_set ? block->color : color; 306 color = block->color_set ? block->color : color;
275 color = block->urgent ? config->colors.urgent_workspace.text : color; 307 color = block->urgent ? config->colors.urgent_workspace.text : color;
276 cairo_set_source_u32(cairo, color); 308 cairo_set_source_u32(cairo, color);
277 pango_printf(cairo, config->font, output->scale, 309 choose_text_aa_mode(ctx, color);
278 block->markup, "%s", text); 310 render_text(cairo, config->font_description, 1, block->markup, "%s", text);
279 x_pos += width; 311 x_pos += width;
280 312
281 if (block->border && block->border_right > 0) { 313 if (block->border_set || block->urgent) {
282 x_pos += margin; 314 x_pos += margin;
283 render_sharp_line(cairo, border_color, x_pos, y_pos, 315 if (block->border_right > 0) {
284 block->border_right * output->scale, render_height); 316 render_sharp_line(cairo, border_color, x_pos, y_pos,
285 x_pos += block->border_right * output->scale; 317 block->border_right, render_height);
318 }
319 x_pos += block->border_right;
286 } 320 }
287 321
288 if (!edge && block->separator) { 322 if (!edge && block->separator) {
289 if (output->focused) { 323 if (output->focused) {
290 cairo_set_source_u32(cairo, config->colors.focused_separator); 324 color = config->colors.focused_separator;
291 } else { 325 } else {
292 cairo_set_source_u32(cairo, config->colors.separator); 326 color = config->colors.separator;
293 } 327 }
328 cairo_set_source_u32(cairo, color);
294 if (config->sep_symbol) { 329 if (config->sep_symbol) {
295 offset = x_pos + (sep_block_width - sep_width) / 2; 330 offset = x_pos + (sep_block_width - sep_width) / 2;
296 double sep_y = height / 2.0 - sep_height / 2.0; 331 double sep_y = height / 2.0 - sep_height / 2.0;
297 cairo_move_to(cairo, offset, (int)floor(sep_y)); 332 cairo_move_to(cairo, offset, (int)floor(sep_y));
298 pango_printf(cairo, config->font, output->scale, false, 333 choose_text_aa_mode(ctx, color);
334 render_text(cairo, config->font_description, 1, false,
299 "%s", config->sep_symbol); 335 "%s", config->sep_symbol);
300 } else { 336 } else {
337 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
301 cairo_set_line_width(cairo, 1); 338 cairo_set_line_width(cairo, 1);
302 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); 339 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
303 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); 340 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
@@ -317,18 +354,18 @@ static void predict_status_block_pos(cairo_t *cairo,
317 struct swaybar_config *config = output->bar->config; 354 struct swaybar_config *config = output->bar->config;
318 355
319 int text_width, text_height; 356 int text_width, text_height;
320 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 357 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
321 output->scale, block->markup, "%s", block->full_text); 358 block->markup, "%s", block->full_text);
322 359
323 int margin = 3 * output->scale; 360 int margin = 3;
324 double ws_vertical_padding = config->status_padding * output->scale; 361 double ws_vertical_padding = config->status_padding;
325 362
326 int width = text_width; 363 int width = text_width;
327 364
328 if (block->min_width_str) { 365 if (block->min_width_str) {
329 int w; 366 int w;
330 get_text_size(cairo, config->font, &w, NULL, NULL, 367 get_text_size(cairo, config->font_description, &w, NULL, NULL,
331 output->scale, block->markup, "%s", block->min_width_str); 368 1, block->markup, "%s", block->min_width_str);
332 block->min_width = w; 369 block->min_width = w;
333 } 370 }
334 if (width < block->min_width) { 371 if (width < block->min_width) {
@@ -336,28 +373,28 @@ static void predict_status_block_pos(cairo_t *cairo,
336 } 373 }
337 374
338 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 375 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
339 uint32_t ideal_surface_height = ideal_height / output->scale; 376 uint32_t ideal_surface_height = ideal_height;
340 if (!output->bar->config->height && 377 if (!output->bar->config->height &&
341 output->height < ideal_surface_height) { 378 output->height < ideal_surface_height) {
342 return; 379 return;
343 } 380 }
344 381
345 *x -= width; 382 *x -= width;
346 if ((block->border || block->urgent) && block->border_left > 0) { 383 if ((block->border_set || block->urgent) && block->border_left > 0) {
347 *x -= (block->border_left * output->scale + margin); 384 *x -= (block->border_left + margin);
348 } 385 }
349 if ((block->border || block->urgent) && block->border_right > 0) { 386 if ((block->border_set || block->urgent) && block->border_right > 0) {
350 *x -= (block->border_right * output->scale + margin); 387 *x -= (block->border_right + margin);
351 } 388 }
352 389
353 int sep_width, sep_height; 390 int sep_width, sep_height;
354 int sep_block_width = block->separator_block_width; 391 int sep_block_width = block->separator_block_width;
355 if (!edge) { 392 if (!edge) {
356 if (config->sep_symbol) { 393 if (config->sep_symbol) {
357 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 394 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
358 output->scale, false, "%s", config->sep_symbol); 395 1, false, "%s", config->sep_symbol);
359 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 396 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
360 uint32_t _ideal_surface_height = _ideal_height / output->scale; 397 uint32_t _ideal_surface_height = _ideal_height;
361 if (!output->bar->config->height && 398 if (!output->bar->config->height &&
362 output->height < _ideal_surface_height) { 399 output->height < _ideal_surface_height) {
363 return; 400 return;
@@ -368,13 +405,13 @@ static void predict_status_block_pos(cairo_t *cairo,
368 } 405 }
369 *x -= sep_block_width; 406 *x -= sep_block_width;
370 } else if (config->status_edge_padding) { 407 } else if (config->status_edge_padding) {
371 *x -= config->status_edge_padding * output->scale; 408 *x -= config->status_edge_padding;
372 } 409 }
373} 410}
374 411
375static double predict_status_line_pos(cairo_t *cairo, 412static double predict_status_line_pos(cairo_t *cairo,
376 struct swaybar_output *output, double x) { 413 struct swaybar_output *output, double x) {
377 bool edge = x == output->width * output->scale; 414 bool edge = x == output->width;
378 struct i3bar_block *block; 415 struct i3bar_block *block;
379 wl_list_for_each(block, &output->bar->status->blocks, link) { 416 wl_list_for_each(block, &output->bar->status->blocks, link) {
380 predict_status_block_pos(cairo, output, block, &x, edge); 417 predict_status_block_pos(cairo, output, block, &x, edge);
@@ -389,24 +426,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo,
389 struct swaybar_config *config = output->bar->config; 426 struct swaybar_config *config = output->bar->config;
390 427
391 int text_width, text_height; 428 int text_width, text_height;
392 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 429 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
393 output->scale, config->pango_markup, "%s", ws->label); 430 config->pango_markup, "%s", ws->label);
394 431
395 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 432 int ws_vertical_padding = WS_VERTICAL_PADDING;
396 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 433 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
397 int border_width = BORDER_WIDTH * output->scale; 434 int border_width = BORDER_WIDTH;
398 435
399 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 436 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
400 + border_width * 2; 437 + border_width * 2;
401 uint32_t ideal_surface_height = ideal_height / output->scale; 438 uint32_t ideal_surface_height = ideal_height;
402 if (!output->bar->config->height && 439 if (!output->bar->config->height &&
403 output->height < ideal_surface_height) { 440 output->height < ideal_surface_height) {
404 return 0; 441 return 0;
405 } 442 }
406 443
407 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 444 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
408 if (width < config->workspace_min_width * output->scale) { 445 if (width < config->workspace_min_width) {
409 width = config->workspace_min_width * output->scale; 446 width = config->workspace_min_width;
410 } 447 }
411 return width; 448 return width;
412} 449}
@@ -437,39 +474,40 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
437 } 474 }
438 475
439 int text_width, text_height; 476 int text_width, text_height;
440 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 477 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
441 output->scale, output->bar->mode_pango_markup, 478 1, output->bar->mode_pango_markup,
442 "%s", mode); 479 "%s", mode);
443 480
444 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 481 int ws_vertical_padding = WS_VERTICAL_PADDING;
445 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 482 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
446 int border_width = BORDER_WIDTH * output->scale; 483 int border_width = BORDER_WIDTH;
447 484
448 uint32_t ideal_height = text_height + ws_vertical_padding * 2 485 uint32_t ideal_height = text_height + ws_vertical_padding * 2
449 + border_width * 2; 486 + border_width * 2;
450 uint32_t ideal_surface_height = ideal_height / output->scale; 487 uint32_t ideal_surface_height = ideal_height;
451 if (!output->bar->config->height && 488 if (!output->bar->config->height &&
452 output->height < ideal_surface_height) { 489 output->height < ideal_surface_height) {
453 return 0; 490 return 0;
454 } 491 }
455 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 492 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
456 if (width < config->workspace_min_width * output->scale) { 493 if (width < config->workspace_min_width) {
457 width = config->workspace_min_width * output->scale; 494 width = config->workspace_min_width;
458 } 495 }
459 return width; 496 return width;
460} 497}
461 498
462static uint32_t render_status_line_i3bar(cairo_t *cairo, 499static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
463 struct swaybar_output *output, double *x) { 500 struct swaybar_output *output = ctx->output;
464 uint32_t max_height = 0; 501 uint32_t max_height = 0;
465 bool edge = *x == output->width * output->scale; 502 bool edge = *x == output->width;
466 struct i3bar_block *block; 503 struct i3bar_block *block;
467 bool use_short_text = false; 504 bool use_short_text = false;
468 505
506 cairo_t *cairo = ctx->cairo;
469 double reserved_width = 507 double reserved_width =
470 predict_workspace_buttons_length(cairo, output) + 508 predict_workspace_buttons_length(cairo, output) +
471 predict_binding_mode_indicator_length(cairo, output) + 509 predict_binding_mode_indicator_length(cairo, output) +
472 3 * output->scale; // require a bit of space for margin 510 3; // require a bit of space for margin
473 511
474 double predicted_full_pos = 512 double predicted_full_pos =
475 predict_status_line_pos(cairo, output, *x); 513 predict_status_line_pos(cairo, output, *x);
@@ -479,7 +517,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
479 } 517 }
480 518
481 wl_list_for_each(block, &output->bar->status->blocks, link) { 519 wl_list_for_each(block, &output->bar->status->blocks, link) {
482 uint32_t h = render_status_block(cairo, output, block, x, edge, 520 uint32_t h = render_status_block(ctx, block, x, edge,
483 use_short_text); 521 use_short_text);
484 max_height = h > max_height ? h : max_height; 522 max_height = h > max_height ? h : max_height;
485 edge = false; 523 edge = false;
@@ -487,53 +525,56 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
487 return max_height; 525 return max_height;
488} 526}
489 527
490static uint32_t render_status_line(cairo_t *cairo, 528static uint32_t render_status_line(struct render_context *ctx, double *x) {
491 struct swaybar_output *output, double *x) { 529 struct status_line *status = ctx->output->bar->status;
492 struct status_line *status = output->bar->status;
493 switch (status->protocol) { 530 switch (status->protocol) {
494 case PROTOCOL_ERROR: 531 case PROTOCOL_ERROR:
495 return render_status_line_error(cairo, output, x); 532 return render_status_line_error(ctx, x);
496 case PROTOCOL_TEXT: 533 case PROTOCOL_TEXT:
497 return render_status_line_text(cairo, output, x); 534 return render_status_line_text(ctx, x);
498 case PROTOCOL_I3BAR: 535 case PROTOCOL_I3BAR:
499 return render_status_line_i3bar(cairo, output, x); 536 return render_status_line_i3bar(ctx, x);
500 case PROTOCOL_UNDEF: 537 case PROTOCOL_UNDEF:
501 return 0; 538 return 0;
502 } 539 }
503 return 0; 540 return 0;
504} 541}
505 542
506static uint32_t render_binding_mode_indicator(cairo_t *cairo, 543static uint32_t render_binding_mode_indicator(struct render_context *ctx,
507 struct swaybar_output *output, double x) { 544 double x) {
545 struct swaybar_output *output = ctx->output;
508 const char *mode = output->bar->mode; 546 const char *mode = output->bar->mode;
509 if (!mode) { 547 if (!mode) {
510 return 0; 548 return 0;
511 } 549 }
512 550
551 cairo_t *cairo = ctx->cairo;
513 struct swaybar_config *config = output->bar->config; 552 struct swaybar_config *config = output->bar->config;
514 int text_width, text_height; 553 int text_width, text_height;
515 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 554 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
516 output->scale, output->bar->mode_pango_markup, 555 1, output->bar->mode_pango_markup,
517 "%s", mode); 556 "%s", mode);
518 557
519 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 558 int ws_vertical_padding = WS_VERTICAL_PADDING;
520 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 559 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
521 int border_width = BORDER_WIDTH * output->scale; 560 int border_width = BORDER_WIDTH;
522 561
523 uint32_t ideal_height = text_height + ws_vertical_padding * 2 562 uint32_t ideal_height = text_height + ws_vertical_padding * 2
524 + border_width * 2; 563 + border_width * 2;
525 uint32_t ideal_surface_height = ideal_height / output->scale; 564 uint32_t ideal_surface_height = ideal_height;
526 if (!output->bar->config->height && 565 if (!output->bar->config->height &&
527 output->height < ideal_surface_height) { 566 output->height < ideal_surface_height) {
528 return ideal_surface_height; 567 return ideal_surface_height;
529 } 568 }
530 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 569 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
531 if (width < config->workspace_min_width * output->scale) { 570 if (width < config->workspace_min_width) {
532 width = config->workspace_min_width * output->scale; 571 width = config->workspace_min_width;
533 } 572 }
534 573
535 uint32_t height = output->height * output->scale; 574 uint32_t height = output->height;
575 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
536 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 576 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
577 ctx->background_color = config->colors.binding_mode.background;
537 cairo_rectangle(cairo, x, 0, width, height); 578 cairo_rectangle(cairo, x, 0, width, height);
538 cairo_fill(cairo); 579 cairo_fill(cairo);
539 580
@@ -550,8 +591,9 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
550 double text_y = height / 2.0 - text_height / 2.0; 591 double text_y = height / 2.0 - text_height / 2.0;
551 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 592 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
552 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 593 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
553 pango_printf(cairo, config->font, output->scale, 594 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
554 output->bar->mode_pango_markup, "%s", mode); 595 render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup,
596 "%s", mode);
555 return output->height; 597 return output->height;
556} 598}
557 599
@@ -565,9 +607,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(
565 return HOTSPOT_IGNORE; 607 return HOTSPOT_IGNORE;
566} 608}
567 609
568static uint32_t render_workspace_button(cairo_t *cairo, 610static uint32_t render_workspace_button(struct render_context *ctx,
569 struct swaybar_output *output,
570 struct swaybar_workspace *ws, double *x) { 611 struct swaybar_workspace *ws, double *x) {
612 struct swaybar_output *output = ctx->output;
571 struct swaybar_config *config = output->bar->config; 613 struct swaybar_config *config = output->bar->config;
572 struct box_colors box_colors; 614 struct box_colors box_colors;
573 if (ws->urgent) { 615 if (ws->urgent) {
@@ -580,30 +622,33 @@ static uint32_t render_workspace_button(cairo_t *cairo,
580 box_colors = config->colors.inactive_workspace; 622 box_colors = config->colors.inactive_workspace;
581 } 623 }
582 624
583 uint32_t height = output->height * output->scale; 625 uint32_t height = output->height;
584 626
627 cairo_t *cairo = ctx->cairo;
585 int text_width, text_height; 628 int text_width, text_height;
586 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 629 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
587 output->scale, config->pango_markup, "%s", ws->label); 630 1, config->pango_markup, "%s", ws->label);
588 631
589 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 632 int ws_vertical_padding = WS_VERTICAL_PADDING;
590 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 633 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
591 int border_width = BORDER_WIDTH * output->scale; 634 int border_width = BORDER_WIDTH;
592 635
593 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 636 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
594 + border_width * 2; 637 + border_width * 2;
595 uint32_t ideal_surface_height = ideal_height / output->scale; 638 uint32_t ideal_surface_height = ideal_height;
596 if (!output->bar->config->height && 639 if (!output->bar->config->height &&
597 output->height < ideal_surface_height) { 640 output->height < ideal_surface_height) {
598 return ideal_surface_height; 641 return ideal_surface_height;
599 } 642 }
600 643
601 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 644 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
602 if (width < config->workspace_min_width * output->scale) { 645 if (width < config->workspace_min_width) {
603 width = config->workspace_min_width * output->scale; 646 width = config->workspace_min_width;
604 } 647 }
605 648
649 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
606 cairo_set_source_u32(cairo, box_colors.background); 650 cairo_set_source_u32(cairo, box_colors.background);
651 ctx->background_color = box_colors.background;
607 cairo_rectangle(cairo, *x, 0, width, height); 652 cairo_rectangle(cairo, *x, 0, width, height);
608 cairo_fill(cairo); 653 cairo_fill(cairo);
609 654
@@ -620,7 +665,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
620 double text_y = height / 2.0 - text_height / 2.0; 665 double text_y = height / 2.0 - text_height / 2.0;
621 cairo_set_source_u32(cairo, box_colors.text); 666 cairo_set_source_u32(cairo, box_colors.text);
622 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 667 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
623 pango_printf(cairo, config->font, output->scale, config->pango_markup, 668 choose_text_aa_mode(ctx, box_colors.text);
669 render_text(cairo, config->font_description, 1, config->pango_markup,
624 "%s", ws->label); 670 "%s", ws->label);
625 671
626 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 672 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
@@ -637,20 +683,24 @@ static uint32_t render_workspace_button(cairo_t *cairo,
637 return output->height; 683 return output->height;
638} 684}
639 685
640static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { 686static uint32_t render_to_cairo(struct render_context *ctx) {
687 cairo_t *cairo = ctx->cairo;
688 struct swaybar_output *output = ctx->output;
641 struct swaybar *bar = output->bar; 689 struct swaybar *bar = output->bar;
642 struct swaybar_config *config = bar->config; 690 struct swaybar_config *config = bar->config;
643 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 691 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
644 if (output->focused) { 692 if (output->focused) {
645 cairo_set_source_u32(cairo, config->colors.focused_background); 693 ctx->background_color = config->colors.focused_background;
646 } else { 694 } else {
647 cairo_set_source_u32(cairo, config->colors.background); 695 ctx->background_color = config->colors.background;
648 } 696 }
697
698 cairo_set_source_u32(cairo, ctx->background_color);
649 cairo_paint(cairo); 699 cairo_paint(cairo);
650 700
651 int th; 701 int th;
652 get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); 702 get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, "");
653 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; 703 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4);
654 /* 704 /*
655 * Each render_* function takes the actual height of the bar, and returns 705 * Each render_* function takes the actual height of the bar, and returns
656 * the ideal height. If the actual height is too short, the render function 706 * the ideal height. If the actual height is too short, the render function
@@ -658,7 +708,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
658 * height is too tall, the render function should adapt its drawing to 708 * height is too tall, the render function should adapt its drawing to
659 * utilize the available space. 709 * utilize the available space.
660 */ 710 */
661 double x = output->width * output->scale; 711 double x = output->width;
662#if HAVE_TRAY 712#if HAVE_TRAY
663 if (bar->tray) { 713 if (bar->tray) {
664 uint32_t h = render_tray(cairo, output, &x); 714 uint32_t h = render_tray(cairo, output, &x);
@@ -666,19 +716,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
666 } 716 }
667#endif 717#endif
668 if (bar->status) { 718 if (bar->status) {
669 uint32_t h = render_status_line(cairo, output, &x); 719 uint32_t h = render_status_line(ctx, &x);
670 max_height = h > max_height ? h : max_height; 720 max_height = h > max_height ? h : max_height;
671 } 721 }
672 x = 0; 722 x = 0;
673 if (config->workspace_buttons) { 723 if (config->workspace_buttons) {
674 struct swaybar_workspace *ws; 724 struct swaybar_workspace *ws;
675 wl_list_for_each(ws, &output->workspaces, link) { 725 wl_list_for_each(ws, &output->workspaces, link) {
676 uint32_t h = render_workspace_button(cairo, output, ws, &x); 726 uint32_t h = render_workspace_button(ctx, ws, &x);
677 max_height = h > max_height ? h : max_height; 727 max_height = h > max_height ? h : max_height;
678 } 728 }
679 } 729 }
680 if (config->binding_mode_indicator) { 730 if (config->binding_mode_indicator) {
681 uint32_t h = render_binding_mode_indicator(cairo, output, x); 731 uint32_t h = render_binding_mode_indicator(ctx, x);
682 max_height = h > max_height ? h : max_height; 732 max_height = h > max_height ? h : max_height;
683 } 733 }
684 734
@@ -708,26 +758,36 @@ void render_frame(struct swaybar_output *output) {
708 758
709 free_hotspots(&output->hotspots); 759 free_hotspots(&output->hotspots);
710 760
761 struct render_context ctx = { 0 };
762 ctx.output = output;
763
711 cairo_surface_t *recorder = cairo_recording_surface_create( 764 cairo_surface_t *recorder = cairo_recording_surface_create(
712 CAIRO_CONTENT_COLOR_ALPHA, NULL); 765 CAIRO_CONTENT_COLOR_ALPHA, NULL);
713 cairo_t *cairo = cairo_create(recorder); 766 cairo_t *cairo = cairo_create(recorder);
767 cairo_scale(cairo, output->scale, output->scale);
714 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 768 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
769 ctx.cairo = cairo;
770
715 cairo_font_options_t *fo = cairo_font_options_create(); 771 cairo_font_options_t *fo = cairo_font_options_create();
716 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); 772 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
773 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
774 ctx.textaa_safe = fo;
717 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 775 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
718 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 776 ctx.textaa_sharp = ctx.textaa_safe;
719 } else { 777 } else {
778 fo = cairo_font_options_create();
779 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
720 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 780 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
721 cairo_font_options_set_subpixel_order(fo, 781 cairo_font_options_set_subpixel_order(fo,
722 to_cairo_subpixel_order(output->subpixel)); 782 to_cairo_subpixel_order(output->subpixel));
783 ctx.textaa_sharp = fo;
723 } 784 }
724 cairo_set_font_options(cairo, fo); 785
725 cairo_font_options_destroy(fo);
726 cairo_save(cairo); 786 cairo_save(cairo);
727 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 787 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
728 cairo_paint(cairo); 788 cairo_paint(cairo);
729 cairo_restore(cairo); 789 cairo_restore(cairo);
730 uint32_t height = render_to_cairo(cairo, output); 790 uint32_t height = render_to_cairo(&ctx);
731 int config_height = output->bar->config->height; 791 int config_height = output->bar->config->height;
732 if (config_height > 0) { 792 if (config_height > 0) {
733 height = config_height; 793 height = config_height;
@@ -753,9 +813,7 @@ void render_frame(struct swaybar_output *output) {
753 output->width * output->scale, 813 output->width * output->scale,
754 output->height * output->scale); 814 output->height * output->scale);
755 if (!output->current_buffer) { 815 if (!output->current_buffer) {
756 cairo_surface_destroy(recorder); 816 goto cleanup;
757 cairo_destroy(cairo);
758 return;
759 } 817 }
760 cairo_t *shm = output->current_buffer->cairo; 818 cairo_t *shm = output->current_buffer->cairo;
761 819
@@ -773,12 +831,27 @@ void render_frame(struct swaybar_output *output) {
773 wl_surface_damage(output->surface, 0, 0, 831 wl_surface_damage(output->surface, 0, 0,
774 output->width, output->height); 832 output->width, output->height);
775 833
834 uint32_t bg_alpha = ctx.background_color & 0xFF;
835 if (bg_alpha == 0xFF) {
836 struct wl_region *region =
837 wl_compositor_create_region(output->bar->compositor);
838 wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
839 wl_surface_set_opaque_region(output->surface, region);
840 wl_region_destroy(region);
841 }
842
776 struct wl_callback *frame_callback = wl_surface_frame(output->surface); 843 struct wl_callback *frame_callback = wl_surface_frame(output->surface);
777 wl_callback_add_listener(frame_callback, &output_frame_listener, output); 844 wl_callback_add_listener(frame_callback, &output_frame_listener, output);
778 output->frame_scheduled = true; 845 output->frame_scheduled = true;
779 846
780 wl_surface_commit(output->surface); 847 wl_surface_commit(output->surface);
781 } 848 }
849
850cleanup:
851 if (ctx.textaa_sharp != ctx.textaa_safe) {
852 cairo_font_options_destroy(ctx.textaa_sharp);
853 }
854 cairo_font_options_destroy(ctx.textaa_safe);
782 cairo_surface_destroy(recorder); 855 cairo_surface_destroy(recorder);
783 cairo_destroy(cairo); 856 cairo_destroy(cairo);
784} 857}
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index ecd91032..2e9bb7f1 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -117,11 +117,11 @@ bool status_handle_readable(struct status_line *status) {
117 status->text = status->buffer; 117 status->text = status->buffer;
118 // intentional fall-through 118 // intentional fall-through
119 case PROTOCOL_TEXT: 119 case PROTOCOL_TEXT:
120 errno = 0;
121 while (true) { 120 while (true) {
122 if (status->buffer[read_bytes - 1] == '\n') { 121 if (status->buffer[read_bytes - 1] == '\n') {
123 status->buffer[read_bytes - 1] = '\0'; 122 status->buffer[read_bytes - 1] = '\0';
124 } 123 }
124 errno = 0;
125 read_bytes = getline(&status->buffer, 125 read_bytes = getline(&status->buffer,
126 &status->buffer_size, status->read); 126 &status->buffer_size, status->read);
127 if (errno == EAGAIN) { 127 if (errno == EAGAIN) {
@@ -157,7 +157,12 @@ struct status_line *status_line_init(char *cmd) {
157 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " 157 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before "
158 " starting `status-command`; WAYLAND_SOCKET should not be set"); 158 " starting `status-command`; WAYLAND_SOCKET should not be set");
159 status->pid = fork(); 159 status->pid = fork();
160 if (status->pid == 0) { 160 if (status->pid < 0) {
161 sway_log_errno(SWAY_ERROR, "fork failed");
162 exit(1);
163 } else if (status->pid == 0) {
164 setpgid(0, 0);
165
161 dup2(pipe_read_fd[1], STDOUT_FILENO); 166 dup2(pipe_read_fd[1], STDOUT_FILENO);
162 close(pipe_read_fd[0]); 167 close(pipe_read_fd[0]);
163 close(pipe_read_fd[1]); 168 close(pipe_read_fd[1]);
@@ -185,8 +190,8 @@ struct status_line *status_line_init(char *cmd) {
185 190
186void status_line_free(struct status_line *status) { 191void status_line_free(struct status_line *status) {
187 status_line_close_fds(status); 192 status_line_close_fds(status);
188 kill(status->pid, status->cont_signal); 193 kill(-status->pid, status->cont_signal);
189 kill(status->pid, SIGTERM); 194 kill(-status->pid, SIGTERM);
190 waitpid(status->pid, NULL, 0); 195 waitpid(status->pid, NULL, 0);
191 if (status->protocol == PROTOCOL_I3BAR) { 196 if (status->protocol == PROTOCOL_I3BAR) {
192 struct i3bar_block *block, *tmp; 197 struct i3bar_block *block, *tmp;
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index a5660f62..0cb5ee9d 100644
--- a/swaybar/tray/item.c
+++ b/swaybar/tray/item.c
@@ -13,7 +13,7 @@
13#include "swaybar/tray/item.h" 13#include "swaybar/tray/item.h"
14#include "swaybar/tray/tray.h" 14#include "swaybar/tray/tray.h"
15#include "background-image.h" 15#include "background-image.h"
16#include "cairo.h" 16#include "cairo_util.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
19#include "wlr-layer-shell-unstable-v1-client-protocol.h" 19#include "wlr-layer-shell-unstable-v1-client-protocol.h"
@@ -118,8 +118,13 @@ static int get_property_callback(sd_bus_message *msg, void *data,
118 118
119 int ret; 119 int ret;
120 if (sd_bus_message_is_method_error(msg, NULL)) { 120 if (sd_bus_message_is_method_error(msg, NULL)) {
121 sway_log(SWAY_ERROR, "%s %s: %s", sni->watcher_id, prop, 121 const sd_bus_error *err = sd_bus_message_get_error(msg);
122 sd_bus_message_get_error(msg)->message); 122 sway_log_importance_t log_lv = SWAY_ERROR;
123 if ((!strcmp(prop, "IconThemePath")) &&
124 (!strcmp(err->name, SD_BUS_ERROR_UNKNOWN_PROPERTY))) {
125 log_lv = SWAY_DEBUG;
126 }
127 sway_log(log_lv, "%s %s: %s", sni->watcher_id, prop, err->message);
123 ret = sd_bus_message_get_errno(msg); 128 ret = sd_bus_message_get_errno(msg);
124 goto cleanup; 129 goto cleanup;
125 } 130 }
@@ -461,6 +466,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
461 sni->target_size = target_size; 466 sni->target_size = target_size;
462 } 467 }
463 468
469 // Passive
470 if (sni->status && sni->status[0] == 'P') {
471 return 0;
472 }
473
464 int icon_size; 474 int icon_size;
465 cairo_surface_t *icon; 475 cairo_surface_t *icon;
466 if (sni->icon) { 476 if (sni->icon) {
@@ -488,24 +498,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
488 cairo_destroy(cairo_icon); 498 cairo_destroy(cairo_icon);
489 } 499 }
490 500
491 int padded_size = icon_size + 2*padding; 501 double descaled_padding = (double)padding / output->scale;
492 *x -= padded_size; 502 double descaled_icon_size = (double)icon_size / output->scale;
493 int y = floor((height - padded_size) / 2.0); 503
504 int size = descaled_icon_size + 2 * descaled_padding;
505 *x -= size;
506 int icon_y = floor((output->height - size) / 2.0);
494 507
495 cairo_operator_t op = cairo_get_operator(cairo); 508 cairo_operator_t op = cairo_get_operator(cairo);
496 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 509 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
497 cairo_set_source_surface(cairo, icon, *x + padding, y + padding); 510
498 cairo_rectangle(cairo, *x, y, padded_size, padded_size); 511 cairo_matrix_t scale_matrix;
512 cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon);
513 // TODO: check cairo_pattern_status for "ENOMEM"
514 cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale);
515 cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding));
516 cairo_pattern_set_matrix(icon_pattern, &scale_matrix);
517 cairo_set_source(cairo, icon_pattern);
518 cairo_rectangle(cairo, *x, icon_y, size, size);
499 cairo_fill(cairo); 519 cairo_fill(cairo);
520
500 cairo_set_operator(cairo, op); 521 cairo_set_operator(cairo, op);
501 522
523 cairo_pattern_destroy(icon_pattern);
502 cairo_surface_destroy(icon); 524 cairo_surface_destroy(icon);
503 525
504 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 526 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
505 hotspot->x = *x; 527 hotspot->x = *x;
506 hotspot->y = 0; 528 hotspot->y = 0;
507 hotspot->width = height; 529 hotspot->width = size;
508 hotspot->height = height; 530 hotspot->height = output->height;
509 hotspot->callback = icon_hotspot_callback; 531 hotspot->callback = icon_hotspot_callback;
510 hotspot->destroy = free; 532 hotspot->destroy = free;
511 hotspot->data = strdup(sni->watcher_id); 533 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/swaymsg/main.c b/swaymsg/main.c
index 60536e48..c08406e2 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -1,4 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2
3#include <limits.h>
2#include <stdio.h> 4#include <stdio.h>
3#include <stdlib.h> 5#include <stdlib.h>
4#include <string.h> 6#include <string.h>
@@ -286,28 +288,83 @@ static void pretty_print_config(json_object *c) {
286 printf("%s\n", json_object_get_string(config)); 288 printf("%s\n", json_object_get_string(config));
287} 289}
288 290
289static void pretty_print(int type, json_object *resp) { 291static void pretty_print_tree(json_object *obj, int indent) {
290 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && 292 for (int i = 0; i < indent; i++) {
291 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && 293 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 } 294 }
298 295
299 if (type == IPC_SEND_TICK) { 296 int id = json_object_get_int(json_object_object_get(obj, "id"));
300 return; 297 const char *name = json_object_get_string(json_object_object_get(obj, "name"));
298 const char *type = json_object_get_string(json_object_object_get(obj, "type"));
299 const char *shell = json_object_get_string(json_object_object_get(obj, "shell"));
300
301 printf("#%d: %s \"%s\"", id, type, name);
302
303 if (shell != NULL) {
304 int pid = json_object_get_int(json_object_object_get(obj, "pid"));
305 const char *app_id = json_object_get_string(json_object_object_get(obj, "app_id"));
306 json_object *window_props_obj = json_object_object_get(obj, "window_properties");
307 const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance"));
308 const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class"));
309 int x11_id = json_object_get_int(json_object_object_get(obj, "window"));
310
311 printf(" (%s, pid: %d", shell, pid);
312 if (app_id != NULL) {
313 printf(", app_id: \"%s\"", app_id);
314 }
315 if (instance != NULL) {
316 printf(", instance: \"%s\"", instance);
317 }
318 if (class != NULL) {
319 printf(", class: \"%s\"", class);
320 }
321 if (x11_id != 0) {
322 printf(", X11 window: 0x%X", x11_id);
323 }
324 printf(")");
301 } 325 }
302 326
303 if (type == IPC_GET_VERSION) { 327 printf("\n");
304 pretty_print_version(resp); 328
305 return; 329 json_object *nodes_obj = json_object_object_get(obj, "nodes");
330 size_t len = json_object_array_length(nodes_obj);
331 for (size_t i = 0; i < len; i++) {
332 pretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1);
306 } 333 }
307 334
308 if (type == IPC_GET_CONFIG) { 335 json_object *floating_nodes_obj;
336 json_bool floating_nodes = json_object_object_get_ex(obj, "floating_nodes", &floating_nodes_obj);
337 if (floating_nodes) {
338 size_t len = json_object_array_length(floating_nodes_obj);
339 for (size_t i = 0; i < len; i++) {
340 pretty_print_tree(json_object_array_get_idx(floating_nodes_obj, i), indent + 1);
341 }
342 }
343}
344
345static void pretty_print(int type, json_object *resp) {
346 switch (type) {
347 case IPC_SEND_TICK:
348 return;
349 case IPC_GET_VERSION:
350 pretty_print_version(resp);
351 return;
352 case IPC_GET_CONFIG:
309 pretty_print_config(resp); 353 pretty_print_config(resp);
310 return; 354 return;
355 case IPC_GET_TREE:
356 pretty_print_tree(resp, 0);
357 return;
358 case IPC_COMMAND:
359 case IPC_GET_WORKSPACES:
360 case IPC_GET_INPUTS:
361 case IPC_GET_OUTPUTS:
362 case IPC_GET_SEATS:
363 break;
364 default:
365 printf("%s\n", json_object_to_json_string_ext(resp,
366 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
367 return;
311 } 368 }
312 369
313 json_object *obj; 370 json_object *obj;
@@ -343,7 +400,7 @@ int main(int argc, char **argv) {
343 400
344 sway_log_init(SWAY_INFO, NULL); 401 sway_log_init(SWAY_INFO, NULL);
345 402
346 static struct option long_options[] = { 403 static const struct option long_options[] = {
347 {"help", no_argument, NULL, 'h'}, 404 {"help", no_argument, NULL, 'h'},
348 {"monitor", no_argument, NULL, 'm'}, 405 {"monitor", no_argument, NULL, 'm'},
349 {"pretty", no_argument, NULL, 'p'}, 406 {"pretty", no_argument, NULL, 'p'},
@@ -480,12 +537,20 @@ int main(int argc, char **argv) {
480 char *resp = ipc_single_command(socketfd, type, command, &len); 537 char *resp = ipc_single_command(socketfd, type, command, &len);
481 538
482 // pretty print the json 539 // pretty print the json
483 json_object *obj = json_tokener_parse(resp); 540 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
484 if (obj == NULL) { 541 if (tok == NULL) {
542 if (quiet) {
543 exit(EXIT_FAILURE);
544 }
545 sway_abort("failed allocating json_tokener");
546 }
547 json_object *obj = json_tokener_parse_ex(tok, resp, -1);
548 enum json_tokener_error err = json_tokener_get_error(tok);
549 json_tokener_free(tok);
550 if (obj == NULL || err != json_tokener_success) {
485 if (!quiet) { 551 if (!quiet) {
486 fprintf(stderr, "ERROR: Could not parse json response from ipc. " 552 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
487 "This is a bug in sway."); 553 json_tokener_error_desc(err));
488 printf("%s\n", resp);
489 } 554 }
490 ret = 1; 555 ret = 1;
491 } else { 556 } else {
@@ -517,13 +582,22 @@ int main(int argc, char **argv) {
517 break; 582 break;
518 } 583 }
519 584
520 json_object *obj = json_tokener_parse(reply->payload); 585 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
521 if (obj == NULL) { 586 if (tok == NULL) {
587 if (quiet) {
588 exit(EXIT_FAILURE);
589 }
590 sway_abort("failed allocating json_tokener");
591 }
592 json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1);
593 enum json_tokener_error err = json_tokener_get_error(tok);
594 json_tokener_free(tok);
595 if (obj == NULL || err != json_tokener_success) {
522 if (!quiet) { 596 if (!quiet) {
523 fprintf(stderr, "ERROR: Could not parse json response from" 597 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
524 " ipc. This is a bug in sway."); 598 json_tokener_error_desc(err));
525 ret = 1;
526 } 599 }
600 ret = 1;
527 break; 601 break;
528 } else if (quiet) { 602 } else if (quiet) {
529 json_object_put(obj); 603 json_object_put(obj);
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index b69013b5..24a9d6c9 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.
diff --git a/swaynag/config.c b/swaynag/config.c
index ca7f4eb2..a0bf3197 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -11,24 +11,40 @@
11#include "util.h" 11#include "util.h"
12#include "wlr-layer-shell-unstable-v1-client-protocol.h" 12#include "wlr-layer-shell-unstable-v1-client-protocol.h"
13 13
14static char *read_from_stdin(void) { 14static char *read_and_trim_stdin(void) {
15 char *buffer = NULL; 15 char *buffer = NULL, *line = NULL;
16 size_t buffer_len = 0; 16 size_t buffer_len = 0, line_size = 0;
17 char *line = NULL; 17 while (1) {
18 size_t line_size = 0; 18 ssize_t nread = getline(&line, &line_size, stdin);
19 ssize_t nread; 19 if (nread == -1) {
20 while ((nread = getline(&line, &line_size, stdin)) != -1) { 20 if (feof(stdin)) {
21 break;
22 } else {
23 perror("getline");
24 goto freeline;
25 }
26 }
21 buffer = realloc(buffer, buffer_len + nread + 1); 27 buffer = realloc(buffer, buffer_len + nread + 1);
22 snprintf(&buffer[buffer_len], nread + 1, "%s", line); 28 if (!buffer) {
29 perror("realloc");
30 goto freebuf;
31 }
32 memcpy(&buffer[buffer_len], line, nread + 1);
23 buffer_len += nread; 33 buffer_len += nread;
24 } 34 }
25 free(line); 35 free(line);
26 36
27 while (buffer && buffer[buffer_len - 1] == '\n') { 37 while (buffer_len && buffer[buffer_len - 1] == '\n') {
28 buffer[--buffer_len] = '\0'; 38 buffer[--buffer_len] = '\0';
29 } 39 }
30 40
31 return buffer; 41 return buffer;
42
43freeline:
44 free(line);
45freebuf:
46 free(buffer);
47 return NULL;
32} 48}
33 49
34int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, 50int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
@@ -51,7 +67,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
51 TO_PADDING_BTN, 67 TO_PADDING_BTN,
52 }; 68 };
53 69
54 static struct option opts[] = { 70 static const struct option opts[] = {
55 {"button", required_argument, NULL, 'b'}, 71 {"button", required_argument, NULL, 'b'},
56 {"button-no-terminal", required_argument, NULL, 'B'}, 72 {"button-no-terminal", required_argument, NULL, 'B'},
57 {"button-dismiss", required_argument, NULL, 'z'}, 73 {"button-dismiss", required_argument, NULL, 'z'},
@@ -59,6 +75,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
59 {"config", required_argument, NULL, 'c'}, 75 {"config", required_argument, NULL, 'c'},
60 {"debug", no_argument, NULL, 'd'}, 76 {"debug", no_argument, NULL, 'd'},
61 {"edge", required_argument, NULL, 'e'}, 77 {"edge", required_argument, NULL, 'e'},
78 {"layer", required_argument, NULL, 'y'},
62 {"font", required_argument, NULL, 'f'}, 79 {"font", required_argument, NULL, 'f'},
63 {"help", no_argument, NULL, 'h'}, 80 {"help", no_argument, NULL, 'h'},
64 {"detailed-message", no_argument, NULL, 'l'}, 81 {"detailed-message", no_argument, NULL, 'l'},
@@ -104,6 +121,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
104 " -c, --config <path> Path to config file.\n" 121 " -c, --config <path> Path to config file.\n"
105 " -d, --debug Enable debugging.\n" 122 " -d, --debug Enable debugging.\n"
106 " -e, --edge top|bottom Set the edge to use.\n" 123 " -e, --edge top|bottom Set the edge to use.\n"
124 " -y, --layer overlay|top|bottom|background\n"
125 " Set the layer to use.\n"
107 " -f, --font <font> Set the font to use.\n" 126 " -f, --font <font> Set the font to use.\n"
108 " -h, --help Show help message and quit.\n" 127 " -h, --help Show help message and quit.\n"
109 " -l, --detailed-message Read a detailed message from stdin.\n" 128 " -l, --detailed-message Read a detailed message from stdin.\n"
@@ -133,7 +152,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
133 152
134 optind = 1; 153 optind = 1;
135 while (1) { 154 while (1) {
136 int c = getopt_long(argc, argv, "b:B:z:Z:c:de:f:hlL:m:o:s:t:v", opts, NULL); 155 int c = getopt_long(argc, argv, "b:B:z:Z:c:de:y:f:hlL:m:o:s:t:v", opts, NULL);
137 if (c == -1) { 156 if (c == -1) {
138 break; 157 break;
139 } 158 }
@@ -147,8 +166,11 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
147 fprintf(stderr, "Missing action for button %s\n", optarg); 166 fprintf(stderr, "Missing action for button %s\n", optarg);
148 return EXIT_FAILURE; 167 return EXIT_FAILURE;
149 } 168 }
150 struct swaynag_button *button; 169 struct swaynag_button *button = calloc(sizeof(struct swaynag_button), 1);
151 button = calloc(sizeof(struct swaynag_button), 1); 170 if (!button) {
171 perror("calloc");
172 return EXIT_FAILURE;
173 }
152 button->text = strdup(optarg); 174 button->text = strdup(optarg);
153 button->type = SWAYNAG_ACTION_COMMAND; 175 button->type = SWAYNAG_ACTION_COMMAND;
154 button->action = strdup(argv[optind]); 176 button->action = strdup(argv[optind]);
@@ -184,24 +206,47 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
184 } 206 }
185 } 207 }
186 break; 208 break;
209 case 'y': // Layer
210 if (type) {
211 if (strcmp(optarg, "background") == 0) {
212 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
213 } else if (strcmp(optarg, "bottom") == 0) {
214 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
215 } else if (strcmp(optarg, "top") == 0) {
216 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
217 } else if (strcmp(optarg, "overlay") == 0) {
218 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
219 } else {
220 fprintf(stderr, "Invalid layer: %s\n"
221 "Usage: --layer overlay|top|bottom|background\n",
222 optarg);
223 return EXIT_FAILURE;
224 }
225 }
226 break;
187 case 'f': // Font 227 case 'f': // Font
188 if (type) { 228 if (type) {
189 free(type->font); 229 free(type->font);
230 pango_font_description_free(type->font_description);
190 type->font = strdup(optarg); 231 type->font = strdup(optarg);
232 type->font_description = pango_font_description_from_string(type->font);
191 } 233 }
192 break; 234 break;
193 case 'l': // Detailed Message 235 case 'l': // Detailed Message
194 if (swaynag) { 236 if (swaynag) {
195 free(swaynag->details.message); 237 free(swaynag->details.message);
196 swaynag->details.message = read_from_stdin(); 238 swaynag->details.message = read_and_trim_stdin();
239 if (!swaynag->details.message) {
240 return EXIT_FAILURE;
241 }
197 swaynag->details.button_up.text = strdup("â–²"); 242 swaynag->details.button_up.text = strdup("â–²");
198 swaynag->details.button_down.text = strdup("â–¼"); 243 swaynag->details.button_down.text = strdup("â–¼");
199 } 244 }
200 break; 245 break;
201 case 'L': // Detailed Button Text 246 case 'L': // Detailed Button Text
202 if (swaynag) { 247 if (swaynag) {
203 free(swaynag->details.button_details->text); 248 free(swaynag->details.button_details.text);
204 swaynag->details.button_details->text = strdup(optarg); 249 swaynag->details.button_details.text = strdup(optarg);
205 } 250 }
206 break; 251 break;
207 case 'm': // Message 252 case 'm': // Message
@@ -218,8 +263,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
218 break; 263 break;
219 case 's': // Dismiss Button Text 264 case 's': // Dismiss Button Text
220 if (swaynag) { 265 if (swaynag) {
221 struct swaynag_button *button_close; 266 struct swaynag_button *button_close = swaynag->buttons->items[0];
222 button_close = swaynag->buttons->items[0];
223 free(button_close->text); 267 free(button_close->text);
224 button_close->text = strdup(optarg); 268 button_close->text = strdup(optarg);
225 } 269 }
@@ -378,23 +422,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {
378 422
379 if (line[0] == '[') { 423 if (line[0] == '[') {
380 char *close = strchr(line, ']'); 424 char *close = strchr(line, ']');
381 if (!close) { 425 if (!close || close != &line[nread - 2] || nread <= 3) {
382 fprintf(stderr, "Closing bracket not found on line %d\n", 426 fprintf(stderr, "Line %d is malformed\n", line_number);
383 line_number);
384 result = 1; 427 result = 1;
385 break; 428 break;
386 } 429 }
387 char *name = calloc(1, close - line); 430 *close = '\0';
388 strncat(name, line + 1, close - line - 1); 431 type = swaynag_type_get(types, &line[1]);
389 type = swaynag_type_get(types, name);
390 if (!type) { 432 if (!type) {
391 type = swaynag_type_new(name); 433 type = swaynag_type_new(&line[1]);
392 list_add(types, type); 434 list_add(types, type);
393 } 435 }
394 free(name);
395 } else { 436 } else {
396 char *flag = malloc(sizeof(char) * (nread + 3)); 437 char *flag = malloc(nread + 3);
397 sprintf(flag, "--%s", line); 438 if (!flag) {
439 perror("calloc");
440 return EXIT_FAILURE;
441 }
442 snprintf(flag, nread + 3, "--%s", line);
398 char *argv[] = {"swaynag", flag}; 443 char *argv[] = {"swaynag", flag};
399 result = swaynag_parse_options(2, argv, swaynag, types, type, 444 result = swaynag_parse_options(2, argv, swaynag, types, type,
400 NULL, NULL); 445 NULL, NULL);
diff --git a/swaynag/main.c b/swaynag/main.c
index 88007818..2ce37831 100644
--- a/swaynag/main.c
+++ b/swaynag/main.c
@@ -20,33 +20,25 @@ void sway_terminate(int code) {
20} 20}
21 21
22int main(int argc, char **argv) { 22int main(int argc, char **argv) {
23 int exit_code = EXIT_SUCCESS; 23 int status = EXIT_SUCCESS;
24 24
25 list_t *types = create_list(); 25 list_t *types = create_list();
26 swaynag_types_add_default(types); 26 swaynag_types_add_default(types);
27 27
28 memset(&swaynag, 0, sizeof(swaynag));
29 swaynag.buttons = create_list(); 28 swaynag.buttons = create_list();
30 wl_list_init(&swaynag.outputs); 29 wl_list_init(&swaynag.outputs);
31 wl_list_init(&swaynag.seats); 30 wl_list_init(&swaynag.seats);
32 31
33 struct swaynag_button *button_close = 32 struct swaynag_button button_close = { 0 };
34 calloc(sizeof(struct swaynag_button), 1); 33 button_close.text = strdup("X");
35 button_close->text = strdup("X"); 34 button_close.type = SWAYNAG_ACTION_DISMISS;
36 button_close->type = SWAYNAG_ACTION_DISMISS; 35 list_add(swaynag.buttons, &button_close);
37 list_add(swaynag.buttons, button_close);
38
39 swaynag.details.button_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 36
44 char *config_path = NULL; 37 char *config_path = NULL;
45 bool debug = false; 38 bool debug = false;
46 int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, 39 status = swaynag_parse_options(argc, argv, NULL, NULL, NULL,
47 &config_path, &debug); 40 &config_path, &debug);
48 if (launch_status != 0) { 41 if (status != 0) {
49 exit_code = launch_status;
50 goto cleanup; 42 goto cleanup;
51 } 43 }
52 sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); 44 sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL);
@@ -56,29 +48,29 @@ int main(int argc, char **argv) {
56 } 48 }
57 if (config_path) { 49 if (config_path) {
58 sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); 50 sway_log(SWAY_DEBUG, "Loading config file: %s", config_path);
59 int config_status = swaynag_load_config(config_path, &swaynag, types); 51 status = swaynag_load_config(config_path, &swaynag, types);
60 free(config_path); 52 if (status != 0) {
61 if (config_status != 0) {
62 exit_code = config_status;
63 goto cleanup; 53 goto cleanup;
64 } 54 }
65 } 55 }
66 56
57 swaynag.details.button_details.text = strdup("Toggle details");
58 swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND;
59
67 if (argc > 1) { 60 if (argc > 1) {
68 struct swaynag_type *type_args = swaynag_type_new("<args>"); 61 struct swaynag_type *type_args = swaynag_type_new("<args>");
69 list_add(types, type_args); 62 list_add(types, type_args);
70 63
71 int result = swaynag_parse_options(argc, argv, &swaynag, types, 64 status = swaynag_parse_options(argc, argv, &swaynag, types,
72 type_args, NULL, NULL); 65 type_args, NULL, NULL);
73 if (result != 0) { 66 if (status != 0) {
74 exit_code = result;
75 goto cleanup; 67 goto cleanup;
76 } 68 }
77 } 69 }
78 70
79 if (!swaynag.message) { 71 if (!swaynag.message) {
80 sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); 72 sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m");
81 exit_code = EXIT_FAILURE; 73 status = EXIT_FAILURE;
82 goto cleanup; 74 goto cleanup;
83 } 75 }
84 76
@@ -99,10 +91,7 @@ int main(int argc, char **argv) {
99 swaynag_types_free(types); 91 swaynag_types_free(types);
100 92
101 if (swaynag.details.message) { 93 if (swaynag.details.message) {
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);
@@ -120,12 +109,11 @@ int main(int argc, char **argv) {
120 109
121 swaynag_setup(&swaynag); 110 swaynag_setup(&swaynag);
122 swaynag_run(&swaynag); 111 swaynag_run(&swaynag);
123 return exit_code; 112 return status;
124 113
125cleanup: 114cleanup:
126 swaynag_types_free(types); 115 swaynag_types_free(types);
127 free(swaynag.details.button_details->text); 116 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/render.c b/swaynag/render.c
index cf2cc9e0..21b03289 100644
--- a/swaynag/render.c
+++ b/swaynag/render.c
@@ -1,5 +1,5 @@
1#include <stdint.h> 1#include <stdint.h>
2#include "cairo.h" 2#include "cairo_util.h"
3#include "log.h" 3#include "log.h"
4#include "pango.h" 4#include "pango.h"
5#include "pool-buffer.h" 5#include "pool-buffer.h"
@@ -9,20 +9,20 @@
9 9
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.1.scd b/swaynag/swaynag.1.scd
index 4a03469e..1cc85db7 100644
--- a/swaynag/swaynag.1.scd
+++ b/swaynag/swaynag.1.scd
@@ -48,6 +48,9 @@ _swaynag_ [options...]
48*-e, --edge* top|bottom 48*-e, --edge* top|bottom
49 Set the edge to use. 49 Set the edge to use.
50 50
51*-y, --layer* overlay|top|bottom|background
52 Set the layer to use.
53
51*-f, --font* <font> 54*-f, --font* <font>
52 Set the font to use. 55 Set the font to use.
53 56
diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd
index a078a4d7..3c367d0f 100644
--- a/swaynag/swaynag.5.scd
+++ b/swaynag/swaynag.5.scd
@@ -53,7 +53,7 @@ The following sizing options can also be set:
53*message-padding=<padding>* 53*message-padding=<padding>*
54 Set the padding for the message. 54 Set the padding for the message.
55 55
56*details-gackground=<color>* 56*details-background=<color>*
57 The background color for the details. 57 The background color for the details.
58 58
59*details-border-size=<size>* 59*details-border-size=<size>*
@@ -79,6 +79,9 @@ Additionally, the following options can be assigned a default per-type:
79*edge=top|bottom* 79*edge=top|bottom*
80 Set the edge to use. 80 Set the edge to use.
81 81
82*layer=overlay|top|bottom|background*
83 Set the layer to use.
84
82*font=<font>* 85*font=<font>*
83 Set the font to use. 86 Set the font to use.
84 87
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index d9bec368..5620155d 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -28,10 +28,15 @@ static bool terminal_execute(char *terminal, char *command) {
28 fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); 28 fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command);
29 fclose(tmp); 29 fclose(tmp);
30 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); 30 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR);
31 char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1)); 31 size_t cmd_size = strlen(terminal) + strlen(" -e ") + strlen(fname) + 1;
32 sprintf(cmd, "%s -e %s", terminal, fname); 32 char *cmd = malloc(cmd_size);
33 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 33 if (!cmd) {
34 sway_log_errno(SWAY_ERROR, "Failed to run command, execl() returned."); 34 perror("malloc");
35 return false;
36 }
37 snprintf(cmd, cmd_size, "%s -e %s", terminal, fname);
38 execlp("sh", "sh", "-c", cmd, NULL);
39 sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned.");
35 free(cmd); 40 free(cmd);
36 return false; 41 return false;
37} 42}
@@ -58,7 +63,7 @@ static void swaynag_button_execute(struct swaynag *swaynag,
58 } else if (pid == 0) { 63 } else if (pid == 0) {
59 // Child of the child. Will be reparented to the init process 64 // Child of the child. Will be reparented to the init process
60 char *terminal = getenv("TERMINAL"); 65 char *terminal = getenv("TERMINAL");
61 if (button->terminal && terminal && strlen(terminal)) { 66 if (button->terminal && terminal && *terminal) {
62 sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); 67 sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal);
63 if (!terminal_execute(terminal, button->action)) { 68 if (!terminal_execute(terminal, button->action)) {
64 swaynag_destroy(swaynag); 69 swaynag_destroy(swaynag);
@@ -69,8 +74,8 @@ static void swaynag_button_execute(struct swaynag *swaynag,
69 sway_log(SWAY_DEBUG, 74 sway_log(SWAY_DEBUG,
70 "$TERMINAL not found. Running directly"); 75 "$TERMINAL not found. Running directly");
71 } 76 }
72 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); 77 execlp("sh", "sh", "-c", button->action, NULL);
73 sway_log_errno(SWAY_DEBUG, "execl failed"); 78 sway_log_errno(SWAY_DEBUG, "execlp failed");
74 _exit(EXIT_FAILURE); 79 _exit(EXIT_FAILURE);
75 } 80 }
76 } 81 }
@@ -103,7 +108,7 @@ static void layer_surface_closed(void *data,
103 swaynag_destroy(swaynag); 108 swaynag_destroy(swaynag);
104} 109}
105 110
106static struct zwlr_layer_surface_v1_listener layer_surface_listener = { 111static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
107 .configure = layer_surface_configure, 112 .configure = layer_surface_configure,
108 .closed = layer_surface_closed, 113 .closed = layer_surface_closed,
109}; 114};
@@ -124,7 +129,7 @@ static void surface_enter(void *data, struct wl_surface *surface,
124 }; 129 };
125} 130}
126 131
127static struct wl_surface_listener surface_listener = { 132static const struct wl_surface_listener surface_listener = {
128 .enter = surface_enter, 133 .enter = surface_enter,
129 .leave = nop, 134 .leave = nop,
130}; 135};
@@ -138,7 +143,7 @@ static void update_cursor(struct swaynag_seat *seat) {
138 const char *cursor_theme = getenv("XCURSOR_THEME"); 143 const char *cursor_theme = getenv("XCURSOR_THEME");
139 unsigned cursor_size = 24; 144 unsigned cursor_size = 24;
140 const char *env_cursor_size = getenv("XCURSOR_SIZE"); 145 const char *env_cursor_size = getenv("XCURSOR_SIZE");
141 if (env_cursor_size && strlen(env_cursor_size) > 0) { 146 if (env_cursor_size && *env_cursor_size) {
142 errno = 0; 147 errno = 0;
143 char *end; 148 char *end;
144 unsigned size = strtoul(env_cursor_size, &end, 10); 149 unsigned size = strtoul(env_cursor_size, &end, 10);
@@ -178,6 +183,8 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
178 wl_fixed_t surface_x, wl_fixed_t surface_y) { 183 wl_fixed_t surface_x, wl_fixed_t surface_y) {
179 struct swaynag_seat *seat = data; 184 struct swaynag_seat *seat = data;
180 struct swaynag_pointer *pointer = &seat->pointer; 185 struct swaynag_pointer *pointer = &seat->pointer;
186 pointer->x = wl_fixed_to_int(surface_x);
187 pointer->y = wl_fixed_to_int(surface_y);
181 pointer->serial = serial; 188 pointer->serial = serial;
182 update_cursor(seat); 189 update_cursor(seat);
183} 190}
@@ -198,8 +205,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
198 return; 205 return;
199 } 206 }
200 207
201 double x = seat->pointer.x * swaynag->scale; 208 double x = seat->pointer.x;
202 double y = seat->pointer.y * swaynag->scale; 209 double y = seat->pointer.y;
203 for (int i = 0; i < swaynag->buttons->length; i++) { 210 for (int i = 0; i < swaynag->buttons->length; i++) {
204 struct swaynag_button *nagbutton = swaynag->buttons->items[i]; 211 struct swaynag_button *nagbutton = swaynag->buttons->items[i];
205 if (x >= nagbutton->x 212 if (x >= nagbutton->x
@@ -263,7 +270,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
263 render_frame(swaynag); 270 render_frame(swaynag);
264} 271}
265 272
266static struct wl_pointer_listener pointer_listener = { 273static const struct wl_pointer_listener pointer_listener = {
267 .enter = wl_pointer_enter, 274 .enter = wl_pointer_enter,
268 .leave = nop, 275 .leave = nop,
269 .motion = wl_pointer_motion, 276 .motion = wl_pointer_motion,
@@ -289,7 +296,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
289 } 296 }
290} 297}
291 298
292const struct wl_seat_listener seat_listener = { 299static const struct wl_seat_listener seat_listener = {
293 .capabilities = seat_handle_capabilities, 300 .capabilities = seat_handle_capabilities,
294 .name = nop, 301 .name = nop,
295}; 302};
@@ -305,33 +312,25 @@ static void output_scale(void *data, struct wl_output *output,
305 } 312 }
306} 313}
307 314
308static struct wl_output_listener output_listener = { 315static void output_name(void *data, struct wl_output *output,
309 .geometry = nop, 316 const char *name) {
310 .mode = nop,
311 .done = nop,
312 .scale = output_scale,
313};
314
315static void xdg_output_handle_name(void *data,
316 struct zxdg_output_v1 *xdg_output, const char *name) {
317 struct swaynag_output *swaynag_output = data; 317 struct swaynag_output *swaynag_output = data;
318 char *outname = swaynag_output->swaynag->type->output; 318 swaynag_output->name = strdup(name);
319 sway_log(SWAY_DEBUG, "Checking against output %s for %s", name, outname); 319
320 if (!swaynag_output->swaynag->output && outname && name 320 const char *outname = swaynag_output->swaynag->type->output;
321 && strcmp(outname, name) == 0) { 321 if (!swaynag_output->swaynag->output && outname &&
322 strcmp(outname, name) == 0) {
322 sway_log(SWAY_DEBUG, "Using output %s", name); 323 sway_log(SWAY_DEBUG, "Using output %s", name);
323 swaynag_output->swaynag->output = swaynag_output; 324 swaynag_output->swaynag->output = swaynag_output;
324 } 325 }
325 swaynag_output->name = strdup(name);
326 zxdg_output_v1_destroy(xdg_output);
327 swaynag_output->swaynag->querying_outputs--;
328} 326}
329 327
330static struct zxdg_output_v1_listener xdg_output_listener = { 328static const struct wl_output_listener output_listener = {
331 .logical_position = nop, 329 .geometry = nop,
332 .logical_size = nop, 330 .mode = nop,
333 .done = nop, 331 .done = nop,
334 .name = xdg_output_handle_name, 332 .scale = output_scale,
333 .name = output_name,
335 .description = nop, 334 .description = nop,
336}; 335};
337 336
@@ -345,6 +344,7 @@ static void handle_global(void *data, struct wl_registry *registry,
345 struct swaynag_seat *seat = 344 struct swaynag_seat *seat =
346 calloc(1, sizeof(struct swaynag_seat)); 345 calloc(1, sizeof(struct swaynag_seat));
347 if (!seat) { 346 if (!seat) {
347 perror("calloc");
348 return; 348 return;
349 } 349 }
350 350
@@ -359,33 +359,25 @@ static void handle_global(void *data, struct wl_registry *registry,
359 } else if (strcmp(interface, wl_shm_interface.name) == 0) { 359 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
360 swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); 360 swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
361 } else if (strcmp(interface, wl_output_interface.name) == 0) { 361 } else if (strcmp(interface, wl_output_interface.name) == 0) {
362 if (!swaynag->output && swaynag->xdg_output_manager) { 362 if (!swaynag->output) {
363 swaynag->querying_outputs++;
364 struct swaynag_output *output = 363 struct swaynag_output *output =
365 calloc(1, sizeof(struct swaynag_output)); 364 calloc(1, sizeof(struct swaynag_output));
365 if (!output) {
366 perror("calloc");
367 return;
368 }
366 output->wl_output = wl_registry_bind(registry, name, 369 output->wl_output = wl_registry_bind(registry, name,
367 &wl_output_interface, 3); 370 &wl_output_interface, 4);
368 output->wl_name = name; 371 output->wl_name = name;
369 output->scale = 1; 372 output->scale = 1;
370 output->swaynag = swaynag; 373 output->swaynag = swaynag;
371 wl_list_insert(&swaynag->outputs, &output->link); 374 wl_list_insert(&swaynag->outputs, &output->link);
372 wl_output_add_listener(output->wl_output, 375 wl_output_add_listener(output->wl_output,
373 &output_listener, output); 376 &output_listener, output);
374
375 struct zxdg_output_v1 *xdg_output;
376 xdg_output = zxdg_output_manager_v1_get_xdg_output(
377 swaynag->xdg_output_manager, output->wl_output);
378 zxdg_output_v1_add_listener(xdg_output,
379 &xdg_output_listener, output);
380 } 377 }
381 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 378 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
382 swaynag->layer_shell = wl_registry_bind( 379 swaynag->layer_shell = wl_registry_bind(
383 registry, name, &zwlr_layer_shell_v1_interface, 1); 380 registry, name, &zwlr_layer_shell_v1_interface, 1);
384 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0
385 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
386 swaynag->xdg_output_manager = wl_registry_bind(registry, name,
387 &zxdg_output_manager_v1_interface,
388 ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
389 } 381 }
390} 382}
391 383
@@ -451,12 +443,11 @@ void swaynag_setup(struct swaynag *swaynag) {
451 443
452 assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); 444 assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm);
453 445
454 while (swaynag->querying_outputs > 0) { 446 // Second roundtrip to get wl_output properties
455 if (wl_display_roundtrip(swaynag->display) < 0) { 447 if (wl_display_roundtrip(swaynag->display) < 0) {
456 sway_log(SWAY_ERROR, "Error during outputs init."); 448 sway_log(SWAY_ERROR, "Error during outputs init.");
457 swaynag_destroy(swaynag); 449 swaynag_destroy(swaynag);
458 exit(EXIT_FAILURE); 450 exit(EXIT_FAILURE);
459 }
460 } 451 }
461 452
462 if (!swaynag->output && swaynag->type->output) { 453 if (!swaynag->output && swaynag->type->output) {
@@ -474,7 +465,8 @@ void swaynag_setup(struct swaynag *swaynag) {
474 swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( 465 swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
475 swaynag->layer_shell, swaynag->surface, 466 swaynag->layer_shell, swaynag->surface,
476 swaynag->output ? swaynag->output->wl_output : NULL, 467 swaynag->output ? swaynag->output->wl_output : NULL,
477 ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); 468 swaynag->type->layer,
469 "swaynag");
478 assert(swaynag->layer_surface); 470 assert(swaynag->layer_surface);
479 zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, 471 zwlr_layer_surface_v1_add_listener(swaynag->layer_surface,
480 &layer_surface_listener, swaynag); 472 &layer_surface_listener, swaynag);
@@ -529,13 +521,8 @@ void swaynag_destroy(struct swaynag *swaynag) {
529 swaynag_seat_destroy(seat); 521 swaynag_seat_destroy(seat);
530 } 522 }
531 523
532 if (&swaynag->buffers[0]) { 524 destroy_buffer(&swaynag->buffers[0]);
533 destroy_buffer(&swaynag->buffers[0]); 525 destroy_buffer(&swaynag->buffers[1]);
534 }
535
536 if (&swaynag->buffers[1]) {
537 destroy_buffer(&swaynag->buffers[1]);
538 }
539 526
540 if (swaynag->outputs.prev || swaynag->outputs.next) { 527 if (swaynag->outputs.prev || swaynag->outputs.next) {
541 struct swaynag_output *output, *temp; 528 struct swaynag_output *output, *temp;
diff --git a/swaynag/types.c b/swaynag/types.c
index fa045532..7bef0f87 100644
--- a/swaynag/types.c
+++ b/swaynag/types.c
@@ -26,6 +26,7 @@ struct swaynag_type *swaynag_type_new(const char *name) {
26 type->button_gap_close = -1; 26 type->button_gap_close = -1;
27 type->button_margin_right = -1; 27 type->button_margin_right = -1;
28 type->button_padding = -1; 28 type->button_padding = -1;
29 type->layer = -1;
29 return type; 30 return type;
30} 31}
31 32
@@ -35,6 +36,7 @@ void swaynag_types_add_default(list_t *types) {
35 type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP 36 type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
36 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT 37 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
37 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; 38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
39 type_defaults->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
38 type_defaults->button_background = 0x333333FF; 40 type_defaults->button_background = 0x333333FF;
39 type_defaults->details_background = 0x333333FF; 41 type_defaults->details_background = 0x333333FF;
40 type_defaults->background = 0x323232FF; 42 type_defaults->background = 0x323232FF;
@@ -100,6 +102,10 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) {
100 dest->anchors = src->anchors; 102 dest->anchors = src->anchors;
101 } 103 }
102 104
105 if (src->layer >= 0) {
106 dest->layer = src->layer;
107 }
108
103 // Colors 109 // Colors
104 if (src->button_background > 0) { 110 if (src->button_background > 0) {
105 dest->button_background = src->button_background; 111 dest->button_background = src->button_background;