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.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.md57
-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/pango.c22
-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.in6
-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/ipc-client.h3
-rw-r--r--include/pango.h5
-rw-r--r--include/pool-buffer.h2
-rw-r--r--include/sway/commands.h9
-rw-r--r--include/sway/config.h67
-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/libinput.h2
-rw-r--r--include/sway/input/seat.h13
-rw-r--r--include/sway/input/text_input.h5
-rw-r--r--include/sway/layers.h8
-rw-r--r--include/sway/output.h7
-rw-r--r--include/sway/server.h53
-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.h23
-rw-r--r--include/sway/tree/workspace.h12
-rw-r--r--include/swaybar/bar.h1
-rw-r--r--include/swaybar/i3bar.h1
-rw-r--r--include/swaynag/swaynag.h5
-rw-r--r--include/swaynag/types.h1
-rw-r--r--include/util.h6
-rw-r--r--meson.build158
-rw-r--r--meson_options.txt2
-rw-r--r--protocols/meson.build5
-rw-r--r--sway/commands.c88
-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.c15
-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.c2
-rw-r--r--sway/commands/fullscreen.c34
-rw-r--r--sway/commands/gaps.c14
-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.c37
-rw-r--r--sway/commands/layout.c12
-rw-r--r--sway/commands/mode.c2
-rw-r--r--sway/commands/move.c160
-rw-r--r--sway/commands/output.c10
-rw-r--r--sway/commands/output/background.c12
-rw-r--r--sway/commands/output/dpms.c25
-rw-r--r--sway/commands/output/mode.c58
-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.c107
-rw-r--r--sway/config/bar.c5
-rw-r--r--sway/config/output.c122
-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.c182
-rw-r--r--sway/desktop/output.c269
-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.c173
-rw-r--r--sway/desktop/xwayland.c96
-rw-r--r--sway/input/cursor.c249
-rw-r--r--sway/input/input-manager.c8
-rw-r--r--sway/input/keyboard.c88
-rw-r--r--sway/input/libinput.c30
-rw-r--r--sway/input/seat.c165
-rw-r--r--sway/input/seatop_default.c54
-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.c27
-rw-r--r--sway/input/tablet.c6
-rw-r--r--sway/input/text_input.c51
-rw-r--r--sway/ipc-json.c206
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/lock.c184
-rw-r--r--sway/main.c142
-rw-r--r--sway/meson.build9
-rw-r--r--sway/realtime.c40
-rw-r--r--sway/server.c103
-rw-r--r--sway/sway-bar.5.scd2
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway-ipc.7.scd7
-rw-r--r--sway/sway-output.5.scd29
-rw-r--r--sway/sway.5.scd51
-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/i3bar.c31
-rw-r--r--swaybar/input.c12
-rw-r--r--swaybar/ipc.c20
-rw-r--r--swaybar/main.c2
-rw-r--r--swaybar/render.c367
-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.c115
-rw-r--r--swaymsg/swaymsg.1.scd20
-rw-r--r--swaynag/config.c99
-rw-r--r--swaynag/main.c52
-rw-r--r--swaynag/render.c60
-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
180 files changed, 5263 insertions, 3020 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.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..cd87985a 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] - [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,32 @@ 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[hu]: README.hu.md
75[nl]: https://github.com/swaywm/sway/blob/master/README.nl.md 72[ir]: README.ir.md
76[ru]: https://github.com/swaywm/sway/blob/master/README.ru.md 73[it]: README.it.md
77[zh-TW]: https://github.com/swaywm/sway/blob/master/README.zh-TW.md 74[ja]: README.ja.md
78[pt]: https://github.com/swaywm/sway/blob/master/README.pt.md 75[ko]: README.ko.md
79[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md 76[nl]: README.nl.md
80[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md 77[pl]: README.pl.md
81[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md 78[pt]: README.pt.md
79[ro]: README.ro.md
80[ru]: README.ru.md
81[tr]: README.tr.md
82[uk]: README.uk.md
83[zh-CN]: README.zh-CN.md
84[zh-TW]: README.zh-TW.md
82[i3]: https://i3wm.org/ 85[i3]: https://i3wm.org/
83[Wayland]: http://wayland.freedesktop.org/ 86[Wayland]: http://wayland.freedesktop.org/
84[FAQ]: https://github.com/swaywm/sway/wiki 87[FAQ]: https://github.com/swaywm/sway/wiki
85[IRC channel]: http://webchat.freenode.net/?channels=sway&uio=d4 88[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
86[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 89[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
87[GitHub releases]: https://github.com/swaywm/sway/releases 90[GitHub releases]: https://github.com/swaywm/sway/releases
88[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 91[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
89[wlroots]: https://github.com/swaywm/wlroots 92[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
90[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 93[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/pango.c b/common/pango.c
index fc3d0688..abc18281 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
@@ -109,7 +109,23 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
109 free(buf); 109 free(buf);
110} 110}
111 111
112void pango_printf(cairo_t *cairo, const char *font, 112void get_text_metrics(const char *font, int *height, int *baseline) {
113 cairo_t *cairo = cairo_create(NULL);
114 PangoContext *pango = pango_cairo_create_context(cairo);
115 PangoFontDescription *description = pango_font_description_from_string(font);
116 // When passing NULL as a language, pango uses the current locale.
117 PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
118
119 *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
120 *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
121
122 pango_font_metrics_unref(metrics);
123 pango_font_description_free(description);
124 g_object_unref(pango);
125 cairo_destroy(cairo);
126}
127
128void render_text(cairo_t *cairo, const char *font,
113 double scale, bool markup, const char *fmt, ...) { 129 double scale, bool markup, const char *fmt, ...) {
114 va_list args; 130 va_list args;
115 va_start(args, fmt); 131 va_start(args, fmt);
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..94050ba6 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.
@@ -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..d42fe9d8 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:-~/.config}/user-dirs.dirs" && \
17 . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs 17 . "${XDG_CONFIG_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/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..93affc23 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/**
@@ -17,7 +17,8 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
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 char *font, 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 char *font, int *height, int *baseline);
21void render_text(cairo_t *cairo, const char *font,
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/sway/commands.h b/include/sway/commands.h
index 964b3661..2746ef28 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 */
@@ -112,6 +112,7 @@ sway_cmd cmd_border;
112sway_cmd cmd_client_noop; 112sway_cmd cmd_client_noop;
113sway_cmd cmd_client_focused; 113sway_cmd cmd_client_focused;
114sway_cmd cmd_client_focused_inactive; 114sway_cmd cmd_client_focused_inactive;
115sway_cmd cmd_client_focused_tab_title;
115sway_cmd cmd_client_unfocused; 116sway_cmd cmd_client_unfocused;
116sway_cmd cmd_client_urgent; 117sway_cmd cmd_client_urgent;
117sway_cmd cmd_client_placeholder; 118sway_cmd cmd_client_placeholder;
@@ -282,7 +283,9 @@ sway_cmd output_cmd_dpms;
282sway_cmd output_cmd_enable; 283sway_cmd output_cmd_enable;
283sway_cmd output_cmd_max_render_time; 284sway_cmd output_cmd_max_render_time;
284sway_cmd output_cmd_mode; 285sway_cmd output_cmd_mode;
286sway_cmd output_cmd_modeline;
285sway_cmd output_cmd_position; 287sway_cmd output_cmd_position;
288sway_cmd output_cmd_render_bit_depth;
286sway_cmd output_cmd_scale; 289sway_cmd output_cmd_scale;
287sway_cmd output_cmd_scale_filter; 290sway_cmd output_cmd_scale_filter;
288sway_cmd output_cmd_subpixel; 291sway_cmd output_cmd_subpixel;
diff --git a/include/sway/config.h b/include/sway/config.h
index 59f22ae2..2e24c3ae 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -5,9 +5,10 @@
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"
12#include "list.h" 13#include "list.h"
13#include "swaynag.h" 14#include "swaynag.h"
@@ -47,7 +48,7 @@ enum binding_flags {
47}; 48};
48 49
49/** 50/**
50 * A key binding and an associated command. 51 * A key (or mouse) binding and an associated command.
51 */ 52 */
52struct sway_binding { 53struct sway_binding {
53 enum binding_input_type type; 54 enum binding_input_type type;
@@ -61,12 +62,10 @@ struct sway_binding {
61 char *command; 62 char *command;
62}; 63};
63 64
64/** 65enum sway_switch_trigger {
65 * A mouse binding and an associated command. 66 SWAY_SWITCH_TRIGGER_OFF,
66 */ 67 SWAY_SWITCH_TRIGGER_ON,
67struct sway_mouse_binding { 68 SWAY_SWITCH_TRIGGER_TOGGLE,
68 uint32_t button;
69 char *command;
70}; 69};
71 70
72/** 71/**
@@ -74,7 +73,7 @@ struct sway_mouse_binding {
74 */ 73 */
75struct sway_switch_binding { 74struct sway_switch_binding {
76 enum wlr_switch_type type; 75 enum wlr_switch_type type;
77 enum wlr_switch_state state; 76 enum sway_switch_trigger trigger;
78 uint32_t flags; 77 uint32_t flags;
79 char *command; 78 char *command;
80}; 79};
@@ -246,6 +245,12 @@ enum scale_filter_mode {
246 SCALE_FILTER_SMART, 245 SCALE_FILTER_SMART,
247}; 246};
248 247
248enum render_bit_depth {
249 RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8
250 RENDER_BIT_DEPTH_8,
251 RENDER_BIT_DEPTH_10,
252};
253
249/** 254/**
250 * Size and position configuration for a particular output. 255 * Size and position configuration for a particular output.
251 * 256 *
@@ -257,6 +262,7 @@ struct output_config {
257 int width, height; 262 int width, height;
258 float refresh_rate; 263 float refresh_rate;
259 int custom_mode; 264 int custom_mode;
265 drmModeModeInfo drm_mode;
260 int x, y; 266 int x, y;
261 float scale; 267 float scale;
262 enum scale_filter_mode scale_filter; 268 enum scale_filter_mode scale_filter;
@@ -264,6 +270,7 @@ struct output_config {
264 enum wl_output_subpixel subpixel; 270 enum wl_output_subpixel subpixel;
265 int max_render_time; // In milliseconds 271 int max_render_time; // In milliseconds
266 int adaptive_sync; 272 int adaptive_sync;
273 enum render_bit_depth render_bit_depth;
267 274
268 char *background; 275 char *background;
269 char *background_option; 276 char *background_option;
@@ -281,6 +288,12 @@ struct side_gaps {
281 int left; 288 int left;
282}; 289};
283 290
291enum smart_gaps_mode {
292 SMART_GAPS_OFF,
293 SMART_GAPS_ON,
294 SMART_GAPS_INVERSE_OUTER,
295};
296
284/** 297/**
285 * Stores configuration for a workspace, regardless of whether the workspace 298 * Stores configuration for a workspace, regardless of whether the workspace
286 * exists. 299 * exists.
@@ -292,6 +305,12 @@ struct workspace_config {
292 struct side_gaps gaps_outer; 305 struct side_gaps gaps_outer;
293}; 306};
294 307
308enum pango_markup_config {
309 PANGO_MARKUP_DISABLED = false,
310 PANGO_MARKUP_ENABLED = true,
311 PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix)
312};
313
295struct bar_config { 314struct bar_config {
296 char *swaybar_command; 315 char *swaybar_command;
297 struct wl_client *client; 316 struct wl_client *client;
@@ -323,7 +342,7 @@ struct bar_config {
323 char *position; 342 char *position;
324 list_t *bindings; 343 list_t *bindings;
325 char *status_command; 344 char *status_command;
326 bool pango_markup; 345 enum pango_markup_config pango_markup;
327 char *font; 346 char *font;
328 int height; // -1 not defined 347 int height; // -1 not defined
329 bool workspace_buttons; 348 bool workspace_buttons;
@@ -410,14 +429,6 @@ enum sway_popup_during_fullscreen {
410 POPUP_LEAVE, 429 POPUP_LEAVE,
411}; 430};
412 431
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 { 432enum focus_follows_mouse_mode {
422 FOLLOWS_NO, 433 FOLLOWS_NO,
423 FOLLOWS_YES, 434 FOLLOWS_YES,
@@ -480,8 +491,8 @@ struct sway_config {
480 enum sway_container_layout default_orientation; 491 enum sway_container_layout default_orientation;
481 enum sway_container_layout default_layout; 492 enum sway_container_layout default_layout;
482 char *font; 493 char *font;
483 size_t font_height; 494 int font_height;
484 size_t font_baseline; 495 int font_baseline;
485 bool pango_markup; 496 bool pango_markup;
486 int titlebar_border_thickness; 497 int titlebar_border_thickness;
487 int titlebar_h_padding; 498 int titlebar_h_padding;
@@ -512,7 +523,7 @@ struct sway_config {
512 bool tiling_drag; 523 bool tiling_drag;
513 int tiling_drag_threshold; 524 int tiling_drag_threshold;
514 525
515 bool smart_gaps; 526 enum smart_gaps_mode smart_gaps;
516 int gaps_inner; 527 int gaps_inner;
517 struct side_gaps gaps_outer; 528 struct side_gaps gaps_outer;
518 529
@@ -535,12 +546,15 @@ struct sway_config {
535 struct { 546 struct {
536 struct border_colors focused; 547 struct border_colors focused;
537 struct border_colors focused_inactive; 548 struct border_colors focused_inactive;
549 struct border_colors focused_tab_title;
538 struct border_colors unfocused; 550 struct border_colors unfocused;
539 struct border_colors urgent; 551 struct border_colors urgent;
540 struct border_colors placeholder; 552 struct border_colors placeholder;
541 float background[4]; 553 float background[4];
542 } border_colors; 554 } border_colors;
543 555
556 bool has_focused_tab_title;
557
544 // floating view 558 // floating view
545 int32_t floating_maximum_width; 559 int32_t floating_maximum_width;
546 int32_t floating_maximum_height; 560 int32_t floating_maximum_height;
@@ -559,7 +573,7 @@ struct sway_config {
559 struct sway_node *node; 573 struct sway_node *node;
560 struct sway_container *container; 574 struct sway_container *container;
561 struct sway_workspace *workspace; 575 struct sway_workspace *workspace;
562 bool using_criteria; 576 bool node_overridden; // True if the node is selected by means other than focus
563 struct { 577 struct {
564 int argc; 578 int argc;
565 char **argv; 579 char **argv;
@@ -690,14 +704,13 @@ void free_bar_binding(struct bar_binding *binding);
690void free_workspace_config(struct workspace_config *wsc); 704void free_workspace_config(struct workspace_config *wsc);
691 705
692/** 706/**
693 * Updates the value of config->font_height based on the max title height 707 * 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 708 * font as reported by pango.
695 * recalculate their heights before reporting. 709 *
696 *
697 * If the height has changed, all containers will be rearranged to take on the 710 * If the height has changed, all containers will be rearranged to take on the
698 * new size. 711 * new size.
699 */ 712 */
700void config_update_font_height(bool recalculate); 713void config_update_font_height(void);
701 714
702/** 715/**
703 * Convert bindsym into bindcode using the first configured layout. 716 * 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..3a71a35f 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
@@ -42,6 +42,8 @@ struct sway_cursor {
42 struct wl_listener swipe_begin; 42 struct wl_listener swipe_begin;
43 struct wl_listener swipe_update; 43 struct wl_listener swipe_update;
44 struct wl_listener swipe_end; 44 struct wl_listener swipe_end;
45 struct wl_listener hold_begin;
46 struct wl_listener hold_end;
45 47
46 struct wl_listener motion; 48 struct wl_listener motion;
47 struct wl_listener motion_absolute; 49 struct wl_listener motion_absolute;
@@ -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/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..47726159 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -18,7 +18,7 @@ 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 (*rebase)(struct sway_seat *seat, uint32_t time_msec); 22 void (*rebase)(struct sway_seat *seat, uint32_t time_msec);
23 void (*tablet_tool_motion)(struct sway_seat *seat, 23 void (*tablet_tool_motion)(struct sway_seat *seat,
24 struct sway_tablet_tool *tool, uint32_t time_msec); 24 struct sway_tablet_tool *tool, uint32_t time_msec);
@@ -185,6 +185,10 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat);
185 185
186struct sway_container *seat_get_focused_container(struct sway_seat *seat); 186struct sway_container *seat_get_focused_container(struct sway_seat *seat);
187 187
188// Force focus to a particular surface that is not part of the workspace
189// hierarchy (used for lockscreen)
190void sway_force_focus(struct wlr_surface *surface);
191
188/** 192/**
189 * Return the last container to be focused for the seat (or the most recently 193 * 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 194 * opened if no container has received focused) that is a child of the given
@@ -239,7 +243,10 @@ enum wlr_edges find_resize_edge(struct sway_container *cont,
239void seatop_begin_default(struct sway_seat *seat); 243void seatop_begin_default(struct sway_seat *seat);
240 244
241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 245void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
242 uint32_t time_msec, int sx, int sy); 246 uint32_t time_msec, double sx, double sy);
247
248void seatop_begin_down_on_surface(struct sway_seat *seat,
249 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy);
243 250
244void seatop_begin_move_floating(struct sway_seat *seat, 251void seatop_begin_move_floating(struct sway_seat *seat,
245 struct sway_container *con); 252 struct sway_container *con);
@@ -271,7 +278,7 @@ void seatop_button(struct sway_seat *seat, uint32_t time_msec,
271void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); 278void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
272 279
273void seatop_pointer_axis(struct sway_seat *seat, 280void seatop_pointer_axis(struct sway_seat *seat,
274 struct wlr_event_pointer_axis *event); 281 struct wlr_pointer_axis_event *event);
275 282
276void seatop_tablet_tool_tip(struct sway_seat *seat, 283void seatop_tablet_tool_tip(struct sway_seat *seat,
277 struct sway_tablet_tool *tool, uint32_t time_msec, 284 struct sway_tablet_tool *tool, uint32_t time_msec,
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..26b9709f 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"
@@ -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..3d59ca56 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
@@ -113,11 +155,14 @@ void server_fini(struct sway_server *server);
113bool server_start(struct sway_server *server); 155bool server_start(struct sway_server *server);
114void server_run(struct sway_server *server); 156void server_run(struct sway_server *server);
115 157
158void restore_nofile_limit(void);
159
116void handle_compositor_new_surface(struct wl_listener *listener, void *data); 160void handle_compositor_new_surface(struct wl_listener *listener, void *data);
117void handle_new_output(struct wl_listener *listener, void *data); 161void handle_new_output(struct wl_listener *listener, void *data);
118 162
119void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); 163void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
120void handle_layer_shell_surface(struct wl_listener *listener, void *data); 164void handle_layer_shell_surface(struct wl_listener *listener, void *data);
165void sway_session_lock_init(void);
121void handle_xdg_shell_surface(struct wl_listener *listener, void *data); 166void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
122#if HAVE_XWAYLAND 167#if HAVE_XWAYLAND
123void handle_xwayland_surface(struct wl_listener *listener, void *data); 168void handle_xwayland_surface(struct wl_listener *listener, void *data);
@@ -125,5 +170,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data);
125void handle_server_decoration(struct wl_listener *listener, void *data); 170void handle_server_decoration(struct wl_listener *listener, void *data);
126void handle_xdg_decoration(struct wl_listener *listener, void *data); 171void handle_xdg_decoration(struct wl_listener *listener, void *data);
127void handle_pointer_constraint(struct wl_listener *listener, void *data); 172void handle_pointer_constraint(struct wl_listener *listener, void *data);
173void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
174 void *data);
175
176void set_rr_scheduling(void);
128 177
129#endif 178#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..95708a04 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 {
@@ -132,7 +131,6 @@ struct sway_xdg_shell_view {
132 struct wl_listener commit; 131 struct wl_listener commit;
133 struct wl_listener request_move; 132 struct wl_listener request_move;
134 struct wl_listener request_resize; 133 struct wl_listener request_resize;
135 struct wl_listener request_maximize;
136 struct wl_listener request_fullscreen; 134 struct wl_listener request_fullscreen;
137 struct wl_listener set_title; 135 struct wl_listener set_title;
138 struct wl_listener set_app_id; 136 struct wl_listener set_app_id;
@@ -171,6 +169,7 @@ struct sway_xwayland_unmanaged {
171 169
172 int lx, ly; 170 int lx, ly;
173 171
172 struct wl_listener request_activate;
174 struct wl_listener request_configure; 173 struct wl_listener request_configure;
175 struct wl_listener request_fullscreen; 174 struct wl_listener request_fullscreen;
176 struct wl_listener commit; 175 struct wl_listener commit;
@@ -184,7 +183,7 @@ struct sway_xwayland_unmanaged {
184struct sway_view_child; 183struct sway_view_child;
185 184
186struct sway_view_child_impl { 185struct sway_view_child_impl {
187 void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy); 186 void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy);
188 void (*destroy)(struct sway_view_child *child); 187 void (*destroy)(struct sway_view_child *child);
189}; 188};
190 189
@@ -218,7 +217,7 @@ struct sway_subsurface {
218struct sway_xdg_popup { 217struct sway_xdg_popup {
219 struct sway_view_child child; 218 struct sway_view_child child;
220 219
221 struct wlr_xdg_surface *wlr_xdg_surface; 220 struct wlr_xdg_popup *wlr_xdg_popup;
222 221
223 struct wl_listener new_popup; 222 struct wl_listener new_popup;
224 struct wl_listener destroy; 223 struct wl_listener destroy;
@@ -311,12 +310,22 @@ void view_destroy(struct sway_view *view);
311 310
312void view_begin_destroy(struct sway_view *view); 311void view_begin_destroy(struct sway_view *view);
313 312
313/**
314 * Map a view, ie. make it visible in the tree.
315 *
316 * `fullscreen` should be set to true (and optionally `fullscreen_output`
317 * should be populated) if the view should be made fullscreen immediately.
318 *
319 * `decoration` should be set to true if the client prefers CSD. The client's
320 * preference may be ignored.
321 */
314void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, 322void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
315 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); 323 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
316 324
317void view_unmap(struct sway_view *view); 325void view_unmap(struct sway_view *view);
318 326
319void view_update_size(struct sway_view *view, int width, int height); 327void view_update_size(struct sway_view *view);
328void view_center_surface(struct sway_view *view);
320 329
321void view_child_init(struct sway_view_child *child, 330void view_child_init(struct sway_view_child *child,
322 const struct sway_view_child_impl *impl, struct sway_view *view, 331 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/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..3c3b2754 100644
--- a/include/swaynag/types.h
+++ b/include/swaynag/types.h
@@ -7,6 +7,7 @@ struct swaynag_type {
7 char *font; 7 char *font;
8 char *output; 8 char *output;
9 uint32_t anchors; 9 uint32_t anchors;
10 int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset
10 11
11 // Colors 12 // Colors
12 uint32_t button_text; 13 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..46352da5 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,50 @@ 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'))
73
68wlroots_features = { 74wlroots_features = {
69 'xwayland': false, 75 'xwayland': false,
70 'systemd': false,
71 'elogind': false,
72 'libseat': false,
73} 76}
74if wlroots_proj.found() 77foreach name, _ : wlroots_features
75 wlroots = wlroots_proj.get_variable('wlroots') 78 var_name = 'have_' + name.underscorify()
76 wlroots_conf = wlroots_proj.get_variable('conf_data') 79 have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
77 foreach name, _ : wlroots_features 80 wlroots_features += { name: have }
78 has = wlroots_conf.get('WLR_HAS_' + name.to_upper()) == 1 81endforeach
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 82
89if get_option('xwayland').enabled() and not wlroots_features['xwayland'] 83if get_option('xwayland').enabled() and not wlroots_features['xwayland']
90 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') 84 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
@@ -95,19 +89,11 @@ if get_option('sd-bus-provider') == 'auto'
95 if not get_option('tray').disabled() 89 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') 90 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto')
97 endif 91 endif
98 sdbus = dependency('libsystemd', 92 sdbus = dependency(['libsystemd', 'libelogind'],
99 required: false, 93 required: false,
100 version: '>=239', 94 version: '>=239',
101 not_found_message: 'libsystemd not found, trying libelogind',
102 ) 95 )
103 if not sdbus.found() 96 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) 97 sdbus = dependency('basu', required: false)
112 endif 98 endif
113else 99else
@@ -131,8 +117,7 @@ conf_data.set10('HAVE_TRAY', have_tray)
131 117
132scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) 118scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
133if scdoc.found() 119if scdoc.found()
134 scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) 120 scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true)
135 sh = find_program('sh', native: true)
136 mandir = get_option('mandir') 121 mandir = get_option('mandir')
137 man_files = [ 122 man_files = [
138 'sway/sway.1.scd', 123 'sway/sway.1.scd',
@@ -143,9 +128,15 @@ if scdoc.found()
143 'sway/sway-output.5.scd', 128 'sway/sway-output.5.scd',
144 'swaybar/swaybar-protocol.7.scd', 129 'swaybar/swaybar-protocol.7.scd',
145 'swaymsg/swaymsg.1.scd', 130 'swaymsg/swaymsg.1.scd',
146 'swaynag/swaynag.1.scd',
147 'swaynag/swaynag.5.scd',
148 ] 131 ]
132
133 if get_option('swaynag')
134 man_files += [
135 'swaynag/swaynag.1.scd',
136 'swaynag/swaynag.5.scd',
137 ]
138 endif
139
149 foreach filename : man_files 140 foreach filename : man_files
150 topic = filename.split('.')[-3].split('/')[-1] 141 topic = filename.split('.')[-3].split('/')[-1]
151 section = filename.split('.')[-2] 142 section = filename.split('.')[-2]
@@ -155,10 +146,10 @@ if scdoc.found()
155 output, 146 output,
156 input: filename, 147 input: filename,
157 output: output, 148 output: output,
158 command: [ 149 command: scdoc_prog,
159 sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
160 ],
161 install: true, 150 install: true,
151 feed: true,
152 capture: true,
162 install_dir: '@0@/man@1@'.format(mandir, section) 153 install_dir: '@0@/man@1@'.format(mandir, section)
163 ) 154 )
164 endforeach 155 endforeach
@@ -169,8 +160,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir
169version = '"@0@"'.format(meson.project_version()) 160version = '"@0@"'.format(meson.project_version())
170git = find_program('git', native: true, required: false) 161git = find_program('git', native: true, required: false)
171if git.found() 162if git.found()
172 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) 163 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
173 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) 164 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
174 if git_commit.returncode() == 0 and git_branch.returncode() == 0 165 if git_commit.returncode() == 0 and git_branch.returncode() == 0
175 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( 166 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format(
176 meson.project_version(), 167 meson.project_version(),
@@ -183,7 +174,7 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
183 174
184# Compute the relative path used by compiler invocations. 175# Compute the relative path used by compiler invocations.
185source_root = meson.current_source_dir().split('/') 176source_root = meson.current_source_dir().split('/')
186build_root = meson.build_root().split('/') 177build_root = meson.global_build_root().split('/')
187relative_dir_parts = [] 178relative_dir_parts = []
188i = 0 179i = 0
189in_prefix = true 180in_prefix = true
@@ -227,9 +218,15 @@ subdir('common')
227subdir('sway') 218subdir('sway')
228subdir('swaymsg') 219subdir('swaymsg')
229 220
230subdir('client') 221if get_option('swaybar') or get_option('swaynag')
231subdir('swaybar') 222 subdir('client')
232subdir('swaynag') 223endif
224if get_option('swaybar')
225 subdir('swaybar')
226endif
227if get_option('swaynag')
228 subdir('swaynag')
229endif
233 230
234config = configuration_data() 231config = configuration_data()
235config.set('datadir', join_paths(prefix, datadir)) 232config.set('datadir', join_paths(prefix, datadir))
@@ -277,13 +274,17 @@ endif
277if get_option('bash-completions') 274if get_option('bash-completions')
278 bash_files = files( 275 bash_files = files(
279 'completions/bash/sway', 276 'completions/bash/sway',
280 'completions/bash/swaybar',
281 'completions/bash/swaymsg', 277 'completions/bash/swaymsg',
282 ) 278 )
279
280 if get_option('swaybar')
281 bash_files += files('completions/bash/swaybar')
282 endif
283
283 if bash_comp.found() 284 if bash_comp.found()
284 bash_install_dir = bash_comp.get_pkgconfig_variable( 285 bash_install_dir = bash_comp.get_variable(
285 'completionsdir', 286 pkgconfig: 'completionsdir',
286 define_variable: ['datadir', datadir] 287 pkgconfig_define: ['datadir', datadir]
287 ) 288 )
288 else 289 else
289 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') 290 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
@@ -296,12 +297,16 @@ if get_option('fish-completions')
296 fish_files = files( 297 fish_files = files(
297 'completions/fish/sway.fish', 298 'completions/fish/sway.fish',
298 'completions/fish/swaymsg.fish', 299 'completions/fish/swaymsg.fish',
299 'completions/fish/swaynag.fish',
300 ) 300 )
301
302 if get_option('swaynag')
303 fish_files += files('completions/fish/swaynag.fish')
304 endif
305
301 if fish_comp.found() 306 if fish_comp.found()
302 fish_install_dir = fish_comp.get_pkgconfig_variable( 307 fish_install_dir = fish_comp.get_variable(
303 'completionsdir', 308 pkgconfig: 'completionsdir',
304 define_variable: ['datadir', datadir] 309 pkgconfig_define: ['datadir', datadir]
305 ) 310 )
306 else 311 else
307 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') 312 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
@@ -313,12 +318,7 @@ endif
313summary({ 318summary({
314 'xwayland': have_xwayland, 319 'xwayland': have_xwayland,
315 'gdk-pixbuf': gdk_pixbuf.found(), 320 'gdk-pixbuf': gdk_pixbuf.found(),
316 'sd-bus': sdbus.found(),
317 'tray': have_tray, 321 'tray': have_tray,
318 'man-pages': scdoc.found(), 322 'man-pages': scdoc.found(),
319}, bool_yn: true) 323}, bool_yn: true)
320 324
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..5a1fd32e 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -42,7 +42,7 @@ 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 },
@@ -51,6 +51,7 @@ static struct cmd_handler handlers[] = {
51 { "client.background", cmd_client_noop }, 51 { "client.background", cmd_client_noop },
52 { "client.focused", cmd_client_focused }, 52 { "client.focused", cmd_client_focused },
53 { "client.focused_inactive", cmd_client_focused_inactive }, 53 { "client.focused_inactive", cmd_client_focused_inactive },
54 { "client.focused_tab_title", cmd_client_focused_tab_title },
54 { "client.placeholder", cmd_client_noop }, 55 { "client.placeholder", cmd_client_noop },
55 { "client.unfocused", cmd_client_unfocused }, 56 { "client.unfocused", cmd_client_unfocused },
56 { "client.urgent", cmd_client_urgent }, 57 { "client.urgent", cmd_client_urgent },
@@ -98,7 +99,7 @@ static struct cmd_handler handlers[] = {
98}; 99};
99 100
100/* Config-time only commands. Keep alphabetized */ 101/* Config-time only commands. Keep alphabetized */
101static struct cmd_handler config_handlers[] = { 102static const struct cmd_handler config_handlers[] = {
102 { "default_orientation", cmd_default_orientation }, 103 { "default_orientation", cmd_default_orientation },
103 { "include", cmd_include }, 104 { "include", cmd_include },
104 { "swaybg_command", cmd_swaybg_command }, 105 { "swaybg_command", cmd_swaybg_command },
@@ -108,7 +109,7 @@ static struct cmd_handler config_handlers[] = {
108}; 109};
109 110
110/* Runtime-only commands. Keep alphabetized */ 111/* Runtime-only commands. Keep alphabetized */
111static struct cmd_handler command_handlers[] = { 112static const struct cmd_handler command_handlers[] = {
112 { "border", cmd_border }, 113 { "border", cmd_border },
113 { "create_output", cmd_create_output }, 114 { "create_output", cmd_create_output },
114 { "exit", cmd_exit }, 115 { "exit", cmd_exit },
@@ -144,22 +145,22 @@ static int handler_compare(const void *_a, const void *_b) {
144 return strcasecmp(a->command, b->command); 145 return strcasecmp(a->command, b->command);
145} 146}
146 147
147struct cmd_handler *find_handler(char *line, struct cmd_handler *handlers, 148const struct cmd_handler *find_handler(char *line,
148 size_t handlers_size) { 149 const struct cmd_handler *handlers, size_t handlers_size) {
149 if (!handlers || !handlers_size) { 150 if (!handlers || !handlers_size) {
150 return NULL; 151 return NULL;
151 } 152 }
152 struct cmd_handler query = { .command = line }; 153 const struct cmd_handler query = { .command = line };
153 return bsearch(&query, handlers, 154 return bsearch(&query, handlers,
154 handlers_size / sizeof(struct cmd_handler), 155 handlers_size / sizeof(struct cmd_handler),
155 sizeof(struct cmd_handler), handler_compare); 156 sizeof(struct cmd_handler), handler_compare);
156} 157}
157 158
158static struct cmd_handler *find_handler_ex(char *line, 159static const struct cmd_handler *find_handler_ex(char *line,
159 struct cmd_handler *config_handlers, size_t config_handlers_size, 160 const struct cmd_handler *config_handlers, size_t config_handlers_size,
160 struct cmd_handler *command_handlers, size_t command_handlers_size, 161 const struct cmd_handler *command_handlers, size_t command_handlers_size,
161 struct cmd_handler *handlers, size_t handlers_size) { 162 const struct cmd_handler *handlers, size_t handlers_size) {
162 struct cmd_handler *handler = NULL; 163 const struct cmd_handler *handler = NULL;
163 if (config->reading) { 164 if (config->reading) {
164 handler = find_handler(line, config_handlers, config_handlers_size); 165 handler = find_handler(line, config_handlers, config_handlers_size);
165 } else if (config->active) { 166 } else if (config->active) {
@@ -168,16 +169,17 @@ static struct cmd_handler *find_handler_ex(char *line,
168 return handler ? handler : find_handler(line, handlers, handlers_size); 169 return handler ? handler : find_handler(line, handlers, handlers_size);
169} 170}
170 171
171static struct cmd_handler *find_core_handler(char *line) { 172static const struct cmd_handler *find_core_handler(char *line) {
172 return find_handler_ex(line, config_handlers, sizeof(config_handlers), 173 return find_handler_ex(line, config_handlers, sizeof(config_handlers),
173 command_handlers, sizeof(command_handlers), 174 command_handlers, sizeof(command_handlers),
174 handlers, sizeof(handlers)); 175 handlers, sizeof(handlers));
175} 176}
176 177
177static void set_config_node(struct sway_node *node) { 178static void set_config_node(struct sway_node *node, bool node_overridden) {
178 config->handler_context.node = node; 179 config->handler_context.node = node;
179 config->handler_context.container = NULL; 180 config->handler_context.container = NULL;
180 config->handler_context.workspace = NULL; 181 config->handler_context.workspace = NULL;
182 config->handler_context.node_overridden = node_overridden;
181 183
182 if (node == NULL) { 184 if (node == NULL) {
183 return; 185 return;
@@ -186,7 +188,7 @@ static void set_config_node(struct sway_node *node) {
186 switch (node->type) { 188 switch (node->type) {
187 case N_CONTAINER: 189 case N_CONTAINER:
188 config->handler_context.container = node->sway_container; 190 config->handler_context.container = node->sway_container;
189 config->handler_context.workspace = node->sway_container->workspace; 191 config->handler_context.workspace = node->sway_container->pending.workspace;
190 break; 192 break;
191 case N_WORKSPACE: 193 case N_WORKSPACE:
192 config->handler_context.workspace = node->sway_workspace; 194 config->handler_context.workspace = node->sway_workspace;
@@ -202,6 +204,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
202 char *cmd; 204 char *cmd;
203 char matched_delim = ';'; 205 char matched_delim = ';';
204 list_t *containers = NULL; 206 list_t *containers = NULL;
207 bool using_criteria = false;
205 208
206 if (seat == NULL) { 209 if (seat == NULL) {
207 // passing a NULL seat means we just pick the default seat 210 // passing a NULL seat means we just pick the default seat
@@ -225,7 +228,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
225 for (; isspace(*head); ++head) {} 228 for (; isspace(*head); ++head) {}
226 // Extract criteria (valid for this command list only). 229 // Extract criteria (valid for this command list only).
227 if (matched_delim == ';') { 230 if (matched_delim == ';') {
228 config->handler_context.using_criteria = false; 231 using_criteria = false;
229 if (*head == '[') { 232 if (*head == '[') {
230 char *error = NULL; 233 char *error = NULL;
231 struct criteria *criteria = criteria_parse(head, &error); 234 struct criteria *criteria = criteria_parse(head, &error);
@@ -239,7 +242,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
239 containers = criteria_get_containers(criteria); 242 containers = criteria_get_containers(criteria);
240 head += strlen(criteria->raw); 243 head += strlen(criteria->raw);
241 criteria_destroy(criteria); 244 criteria_destroy(criteria);
242 config->handler_context.using_criteria = true; 245 using_criteria = true;
243 // Skip leading whitespace 246 // Skip leading whitespace
244 for (; isspace(*head); ++head) {} 247 for (; isspace(*head); ++head) {}
245 } 248 }
@@ -265,7 +268,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
265 } 268 }
266 } 269 }
267 } 270 }
268 struct cmd_handler *handler = find_core_handler(argv[0]); 271 const struct cmd_handler *handler = find_core_handler(argv[0]);
269 if (!handler) { 272 if (!handler) {
270 list_add(res_list, cmd_results_new(CMD_INVALID, 273 list_add(res_list, cmd_results_new(CMD_INVALID,
271 "Unknown/invalid command '%s'", argv[0])); 274 "Unknown/invalid command '%s'", argv[0]));
@@ -278,11 +281,14 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
278 argv[i] = do_var_replacement(argv[i]); 281 argv[i] = do_var_replacement(argv[i]);
279 } 282 }
280 283
281 if (!config->handler_context.using_criteria) { 284
282 // The container or workspace which this command will run on. 285 if (!using_criteria) {
283 struct sway_node *node = con ? &con->node : 286 if (con) {
284 seat_get_focus_inactive(seat, &root->node); 287 set_config_node(&con->node, true);
285 set_config_node(node); 288 } else {
289 set_config_node(seat_get_focus_inactive(seat, &root->node),
290 false);
291 }
286 struct cmd_results *res = handler->handle(argc-1, argv+1); 292 struct cmd_results *res = handler->handle(argc-1, argv+1);
287 list_add(res_list, res); 293 list_add(res_list, res);
288 if (res->status == CMD_INVALID) { 294 if (res->status == CMD_INVALID) {
@@ -296,7 +302,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
296 struct cmd_results *fail_res = NULL; 302 struct cmd_results *fail_res = NULL;
297 for (int i = 0; i < containers->length; ++i) { 303 for (int i = 0; i < containers->length; ++i) {
298 struct sway_container *container = containers->items[i]; 304 struct sway_container *container = containers->items[i];
299 set_config_node(&container->node); 305 set_config_node(&container->node, true);
300 struct cmd_results *res = handler->handle(argc-1, argv+1); 306 struct cmd_results *res = handler->handle(argc-1, argv+1);
301 if (res->status == CMD_SUCCESS) { 307 if (res->status == CMD_SUCCESS) {
302 free_cmd_results(res); 308 free_cmd_results(res);
@@ -370,7 +376,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
370 376
371 // Determine the command handler 377 // Determine the command handler
372 sway_log(SWAY_INFO, "Config command: %s", exec); 378 sway_log(SWAY_INFO, "Config command: %s", exec);
373 struct cmd_handler *handler = find_core_handler(argv[0]); 379 const struct cmd_handler *handler = find_core_handler(argv[0]);
374 if (!handler || !handler->handle) { 380 if (!handler || !handler->handle) {
375 const char *error = handler 381 const char *error = handler
376 ? "Command '%s' is shimmed, but unimplemented" 382 ? "Command '%s' is shimmed, but unimplemented"
@@ -418,12 +424,12 @@ cleanup:
418} 424}
419 425
420struct cmd_results *config_subcommand(char **argv, int argc, 426struct cmd_results *config_subcommand(char **argv, int argc,
421 struct cmd_handler *handlers, size_t handlers_size) { 427 const struct cmd_handler *handlers, size_t handlers_size) {
422 char *command = join_args(argv, argc); 428 char *command = join_args(argv, argc);
423 sway_log(SWAY_DEBUG, "Subcommand: %s", command); 429 sway_log(SWAY_DEBUG, "Subcommand: %s", command);
424 free(command); 430 free(command);
425 431
426 struct cmd_handler *handler = find_handler(argv[0], handlers, 432 const struct cmd_handler *handler = find_handler(argv[0], handlers,
427 handlers_size); 433 handlers_size);
428 if (!handler) { 434 if (!handler) {
429 return cmd_results_new(CMD_INVALID, 435 return cmd_results_new(CMD_INVALID,
@@ -453,41 +459,13 @@ struct cmd_results *config_commands_command(char *exec) {
453 goto cleanup; 459 goto cleanup;
454 } 460 }
455 461
456 struct cmd_handler *handler = find_handler(cmd, NULL, 0); 462 const struct cmd_handler *handler = find_handler(cmd, NULL, 0);
457 if (!handler && strcmp(cmd, "*") != 0) { 463 if (!handler && strcmp(cmd, "*") != 0) {
458 results = cmd_results_new(CMD_INVALID, 464 results = cmd_results_new(CMD_INVALID,
459 "Unknown/invalid command '%s'", cmd); 465 "Unknown/invalid command '%s'", cmd);
460 goto cleanup; 466 goto cleanup;
461 } 467 }
462 468
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); 469 results = cmd_results_new(CMD_SUCCESS, NULL);
492 470
493cleanup: 471cleanup:
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..26c99e63 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) !=
@@ -550,17 +551,17 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
550 "unknown switch %s)", bindtype, split->items[0]); 551 "unknown switch %s)", bindtype, split->items[0]);
551 } 552 }
552 if (strcmp(split->items[1], "on") == 0) { 553 if (strcmp(split->items[1], "on") == 0) {
553 binding->state = WLR_SWITCH_STATE_ON; 554 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
554 } else if (strcmp(split->items[1], "off") == 0) { 555 } else if (strcmp(split->items[1], "off") == 0) {
555 binding->state = WLR_SWITCH_STATE_OFF; 556 binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
556 } else if (strcmp(split->items[1], "toggle") == 0) { 557 } else if (strcmp(split->items[1], "toggle") == 0) {
557 binding->state = WLR_SWITCH_STATE_TOGGLE; 558 binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
558 } else { 559 } else {
559 free_switch_binding(binding); 560 free_switch_binding(binding);
560 return cmd_results_new(CMD_FAILURE, 561 return cmd_results_new(CMD_FAILURE,
561 "Invalid %s command " 562 "Invalid %s command "
562 "(expected switch state: unknown state %d)", 563 "(expected switch state: unknown state %s)",
563 bindtype, split->items[0]); 564 bindtype, split->items[1]);
564 } 565 }
565 list_free_items_and_destroy(split); 566 list_free_items_and_destroy(split);
566 567
@@ -642,6 +643,8 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
642 if (success) { 643 if (success) {
643 ipc_event_binding(binding); 644 ipc_event_binding(binding);
644 } 645 }
646
647 transaction_commit_dirty();
645} 648}
646 649
647/** 650/**
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..cea720f5 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -22,6 +22,6 @@ struct cmd_results *cmd_font(int argc, char **argv) {
22 } 22 }
23 23
24 free(font); 24 free(font);
25 config_update_font_height(true); 25 config_update_font_height();
26 return cmd_results_new(CMD_SUCCESS, NULL); 26 return cmd_results_new(CMD_SUCCESS, NULL);
27} 27}
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/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..dabc6697 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,23 @@ 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 = dev->wlr_device->keyboard;
79 if (relative) { 102 if (relative) {
80 switch_layout_relative(dev->wlr_device->keyboard, relative); 103 action->layout = get_layout_relative(
104 dev->wlr_device->keyboard, relative);
81 } else { 105 } else {
82 switch_layout(dev->wlr_device->keyboard, layout); 106 action->layout = layout;
83 } 107 }
84 } 108 }
85 109
110 for (size_t i = 0; i < actions_len; i++) {
111 switch_layout(actions[i].keyboard, actions[i].layout);
112 }
113 free(actions);
114
86 return cmd_results_new(CMD_SUCCESS, NULL); 115 return cmd_results_new(CMD_SUCCESS, NULL);
87} 116}
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..e23e4ee4 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -9,7 +9,7 @@
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 { "bindswitch", cmd_bindswitch }, 14 { "bindswitch", cmd_bindswitch },
15 { "bindsym", cmd_bindsym }, 15 { "bindsym", cmd_bindsym },
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..125df5a7 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,10 @@ 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 { "render_bit_depth", output_cmd_render_bit_depth },
20 { "res", output_cmd_mode }, 22 { "res", output_cmd_mode },
21 { "resolution", output_cmd_mode }, 23 { "resolution", output_cmd_mode },
22 { "scale", output_cmd_scale }, 24 { "scale", output_cmd_scale },
@@ -32,9 +34,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
32 return error; 34 return error;
33 } 35 }
34 36
35 // The NOOP-1 output is a dummy output used when there's no outputs 37 // The HEADLESS-1 output is a dummy output used when there's no outputs
36 // connected. It should never be configured. 38 // connected. It should never be configured.
37 if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { 39 if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
38 return cmd_results_new(CMD_FAILURE, 40 return cmd_results_new(CMD_FAILURE,
39 "Refusing to configure the no op output"); 41 "Refusing to configure the no op output");
40 } 42 }
@@ -51,7 +53,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
51 if (!sway_output) { 53 if (!sway_output) {
52 return cmd_results_new(CMD_FAILURE, "Unknown output"); 54 return cmd_results_new(CMD_FAILURE, "Unknown output");
53 } 55 }
54 if (sway_output == root->noop_output) { 56 if (sway_output == root->fallback_output) {
55 return cmd_results_new(CMD_FAILURE, 57 return cmd_results_new(CMD_FAILURE,
56 "Refusing to configure the no op output"); 58 "Refusing to configure the no op output");
57 } 59 }
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..638c0ade 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,6 +1,8 @@
1#include "sway/commands.h" 1#include "sway/commands.h"
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/output.h"
3#include "util.h" 4#include "util.h"
5#include <strings.h>
4 6
5struct cmd_results *output_cmd_dpms(int argc, char **argv) { 7struct cmd_results *output_cmd_dpms(int argc, char **argv) {
6 if (!config->handler_context.output_config) { 8 if (!config->handler_context.output_config) {
@@ -10,7 +12,28 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) {
10 return cmd_results_new(CMD_INVALID, "Missing dpms argument."); 12 return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
11 } 13 }
12 14
13 if (parse_boolean(argv[0], true)) { 15 enum config_dpms current_dpms = DPMS_ON;
16
17 if (strcasecmp(argv[0], "toggle") == 0) {
18
19 const char *oc_name = config->handler_context.output_config->name;
20 if (strcmp(oc_name, "*") == 0) {
21 return cmd_results_new(CMD_INVALID,
22 "Cannot apply toggle to all outputs.");
23 }
24
25 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
26 if (!sway_output || !sway_output->wlr_output) {
27 return cmd_results_new(CMD_FAILURE,
28 "Cannot apply toggle to unknown output %s", oc_name);
29 }
30
31 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
32 current_dpms = DPMS_OFF;
33 }
34 }
35
36 if (parse_boolean(argv[0], current_dpms == DPMS_ON)) {
14 config->handler_context.output_config->dpms_state = DPMS_ON; 37 config->handler_context.output_config->dpms_state = DPMS_ON;
15 } else { 38 } else {
16 config->handler_context.output_config->dpms_state = DPMS_OFF; 39 config->handler_context.output_config->dpms_state = DPMS_OFF;
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/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..e4745a5c 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"
@@ -236,7 +236,6 @@ static void config_defaults(struct sway_config *config) {
236 config->default_layout = L_NONE; 236 config->default_layout = L_NONE;
237 config->default_orientation = L_NONE; 237 config->default_orientation = L_NONE;
238 if (!(config->font = strdup("monospace 10"))) goto cleanup; 238 if (!(config->font = strdup("monospace 10"))) goto cleanup;
239 config->font_height = 17; // height of monospace 10
240 config->urgent_timeout = 500; 239 config->urgent_timeout = 500;
241 config->focus_on_window_activation = FOWA_URGENT; 240 config->focus_on_window_activation = FOWA_URGENT;
242 config->popup_during_fullscreen = POPUP_SMART; 241 config->popup_during_fullscreen = POPUP_SMART;
@@ -267,7 +266,7 @@ static void config_defaults(struct sway_config *config) {
267 config->tiling_drag = true; 266 config->tiling_drag = true;
268 config->tiling_drag_threshold = 9; 267 config->tiling_drag_threshold = 9;
269 268
270 config->smart_gaps = false; 269 config->smart_gaps = SMART_GAPS_OFF;
271 config->gaps_inner = 0; 270 config->gaps_inner = 0;
272 config->gaps_outer.top = 0; 271 config->gaps_outer.top = 0;
273 config->gaps_outer.right = 0; 272 config->gaps_outer.right = 0;
@@ -291,6 +290,8 @@ static void config_defaults(struct sway_config *config) {
291 config->hide_edge_borders_smart = ESMART_OFF; 290 config->hide_edge_borders_smart = ESMART_OFF;
292 config->hide_lone_tab = false; 291 config->hide_lone_tab = false;
293 292
293 config->has_focused_tab_title = false;
294
294 // border colors 295 // border colors
295 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); 296 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF);
296 color_to_rgba(config->border_colors.focused.background, 0x285577FF); 297 color_to_rgba(config->border_colors.focused.background, 0x285577FF);
@@ -338,35 +339,62 @@ static bool file_exists(const char *path) {
338 return path && access(path, R_OK) != -1; 339 return path && access(path, R_OK) != -1;
339} 340}
340 341
342static char *config_path(const char *prefix, const char *config_folder) {
343 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
344 return NULL;
345 }
346
347 const char *filename = "config";
348
349 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
350 char *path = calloc(size, sizeof(char));
351 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
352 return path;
353}
354
341static char *get_config_path(void) { 355static char *get_config_path(void) {
342 static const char *config_paths[] = { 356 char *path = NULL;
343 "$HOME/.sway/config", 357 const char *home = getenv("HOME");
344 "$XDG_CONFIG_HOME/sway/config", 358 char *config_home_fallback = NULL;
345 "$HOME/.i3/config", 359
346 "$XDG_CONFIG_HOME/i3/config", 360 const char *config_home = getenv("XDG_CONFIG_HOME");
347 SYSCONFDIR "/sway/config", 361 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
348 SYSCONFDIR "/i3/config", 362 size_t size_fallback = 1 + strlen(home) + strlen("/.config");
363 config_home_fallback = calloc(size_fallback, sizeof(char));
364 if (config_home_fallback != NULL)
365 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
366 config_home = config_home_fallback;
367 }
368
369 struct config_path {
370 const char *prefix;
371 const char *config_folder;
349 }; 372 };
350 373
351 char *config_home = getenv("XDG_CONFIG_HOME"); 374 struct config_path config_paths[] = {
352 if (!config_home || !*config_home) { 375 { .prefix = home, .config_folder = ".sway"},
353 config_paths[1] = "$HOME/.config/sway/config"; 376 { .prefix = config_home, .config_folder = "sway"},
354 config_paths[3] = "$HOME/.config/i3/config"; 377 { .prefix = home, .config_folder = ".i3"},
355 } 378 { .prefix = config_home, .config_folder = "i3"},
379 { .prefix = SYSCONFDIR, .config_folder = "sway"},
380 { .prefix = SYSCONFDIR, .config_folder = "i3"}
381 };
356 382
357 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { 383 size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);
358 wordexp_t p; 384 for (size_t i = 0; i < num_config_paths; i++) {
359 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { 385 path = config_path(config_paths[i].prefix, config_paths[i].config_folder);
360 char *path = strdup(p.we_wordv[0]); 386 if (!path) {
361 wordfree(&p); 387 continue;
362 if (file_exists(path)) { 388 }
363 return path; 389 if (file_exists(path)) {
364 } 390 break;
365 free(path);
366 } 391 }
392 free(path);
393 path = NULL;
367 } 394 }
368 395
369 return NULL; 396 free(config_home_fallback);
397 return path;
370} 398}
371 399
372static bool load_config(const char *path, struct sway_config *config, 400static bool load_config(const char *path, struct sway_config *config,
@@ -514,6 +542,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
514 return success; 542 return success;
515 } 543 }
516 544
545 // Only really necessary if not explicitly `font` is set in the config.
546 config_update_font_height();
547
517 if (is_active && !validating) { 548 if (is_active && !validating) {
518 input_manager_verify_fallback_seat(); 549 input_manager_verify_fallback_seat();
519 550
@@ -964,31 +995,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
964 return lenient_strcmp(wsa->workspace, wsb->workspace); 995 return lenient_strcmp(wsa->workspace, wsb->workspace);
965} 996}
966 997
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 998
985void config_update_font_height(bool recalculate) { 999void config_update_font_height(void) {
986 size_t prev_max_height = config->font_height; 1000 int prev_max_height = config->font_height;
987 config->font_height = 0;
988 config->font_baseline = 0;
989 1001
990 root_for_each_container(find_baseline_iterator, &recalculate); 1002 get_text_metrics(config->font, &config->font_height, &config->font_baseline);
991 root_for_each_container(find_font_height_iterator, NULL);
992 1003
993 if (config->font_height != prev_max_height) { 1004 if (config->font_height != prev_max_height) {
994 arrange_root(); 1005 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..aa4cf946 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,7 @@ 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;
68 return oc; 74 return oc;
69} 75}
70 76
@@ -99,6 +105,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 105 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 106 dst->custom_mode = src->custom_mode;
101 } 107 }
108 if (src->drm_mode.type != (uint32_t) -1) {
109 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
110 }
102 if (src->transform != -1) { 111 if (src->transform != -1) {
103 dst->transform = src->transform; 112 dst->transform = src->transform;
104 } 113 }
@@ -108,6 +117,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
108 if (src->adaptive_sync != -1) { 117 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync; 118 dst->adaptive_sync = src->adaptive_sync;
110 } 119 }
120 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
121 dst->render_bit_depth = src->render_bit_depth;
122 }
111 if (src->background) { 123 if (src->background) {
112 free(dst->background); 124 free(dst->background);
113 dst->background = strdup(src->background); 125 dst->background = strdup(src->background);
@@ -271,6 +283,18 @@ static void set_mode(struct wlr_output *output, int width, int height,
271 wlr_output_set_mode(output, best); 283 wlr_output_set_mode(output, best);
272} 284}
273 285
286static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
287 if (!wlr_output_is_drm(output)) {
288 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
289 return;
290 }
291 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
292 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
293 if (mode) {
294 wlr_output_set_mode(output, mode);
295 }
296}
297
274/* Some manufacturers hardcode the aspect-ratio of the output in the physical 298/* Some manufacturers hardcode the aspect-ratio of the output in the physical
275 * size field. */ 299 * size field. */
276static bool phys_size_is_aspect_ratio(struct wlr_output *output) { 300static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
@@ -334,9 +358,26 @@ static int compute_default_scale(struct wlr_output *output) {
334 return 2; 358 return 2;
335} 359}
336 360
361/* Lists of formats to try, in order, when a specific render bit depth has
362 * been asked for. The second to last format in each list should always
363 * be XRGB8888, as a reliable backup in case the others are not available;
364 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */
365static const uint32_t *bit_depth_preferences[] = {
366 [RENDER_BIT_DEPTH_8] = (const uint32_t []){
367 DRM_FORMAT_XRGB8888,
368 DRM_FORMAT_INVALID,
369 },
370 [RENDER_BIT_DEPTH_10] = (const uint32_t []){
371 DRM_FORMAT_XRGB2101010,
372 DRM_FORMAT_XBGR2101010,
373 DRM_FORMAT_XRGB8888,
374 DRM_FORMAT_INVALID,
375 },
376};
377
337static void queue_output_config(struct output_config *oc, 378static void queue_output_config(struct output_config *oc,
338 struct sway_output *output) { 379 struct sway_output *output) {
339 if (output == root->noop_output) { 380 if (output == root->fallback_output) {
340 return; 381 return;
341 } 382 }
342 383
@@ -351,14 +392,36 @@ static void queue_output_config(struct output_config *oc,
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 392 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 393 wlr_output_enable(wlr_output, true);
353 394
354 if (oc && oc->width > 0 && oc->height > 0) { 395 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
396 sway_log(SWAY_DEBUG, "Set %s modeline",
397 wlr_output->name);
398 set_modeline(wlr_output, &oc->drm_mode);
399 } else if (oc && oc->width > 0 && oc->height > 0) {
355 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 400 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 401 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 402 set_mode(wlr_output, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 403 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 404 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 405 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 406 struct wlr_output_mode *preferred_mode =
407 wlr_output_preferred_mode(wlr_output);
408 wlr_output_set_mode(wlr_output, preferred_mode);
409
410 if (!wlr_output_test(wlr_output)) {
411 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
412 "falling back to another mode");
413 struct wlr_output_mode *mode;
414 wl_list_for_each(mode, &wlr_output->modes, link) {
415 if (mode == preferred_mode) {
416 continue;
417 }
418
419 wlr_output_set_mode(wlr_output, mode);
420 if (wlr_output_test(wlr_output)) {
421 break;
422 }
423 }
424 }
362 } 425 }
363 426
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 427 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -367,9 +430,16 @@ static void queue_output_config(struct output_config *oc,
367 wlr_output_set_subpixel(wlr_output, oc->subpixel); 430 wlr_output_set_subpixel(wlr_output, oc->subpixel);
368 } 431 }
369 432
433 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
370 if (oc && oc->transform >= 0) { 434 if (oc && oc->transform >= 0) {
371 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 435 tr = oc->transform;
372 wlr_output_set_transform(wlr_output, oc->transform); 436 } else if (wlr_output_is_drm(wlr_output)) {
437 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
438 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
439 }
440 if (wlr_output->transform != tr) {
441 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
442 wlr_output_set_transform(wlr_output, tr);
373 } 443 }
374 444
375 // Apply the scale last before the commit, because the scale auto-detection 445 // Apply the scale last before the commit, because the scale auto-detection
@@ -391,10 +461,26 @@ static void queue_output_config(struct output_config *oc,
391 oc->adaptive_sync); 461 oc->adaptive_sync);
392 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 462 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1);
393 } 463 }
464
465 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
466 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth];
467 assert(fmts);
468
469 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
470 wlr_output_set_render_format(wlr_output, fmts[i]);
471 if (wlr_output_test(wlr_output)) {
472 break;
473 }
474
475 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
476 "failed to work, falling back to next in "
477 "list, 0x%08x", fmts[i], fmts[i + 1]);
478 }
479 }
394} 480}
395 481
396bool apply_output_config(struct output_config *oc, struct sway_output *output) { 482bool apply_output_config(struct output_config *oc, struct sway_output *output) {
397 if (output == root->noop_output) { 483 if (output == root->fallback_output) {
398 return false; 484 return false;
399 } 485 }
400 486
@@ -462,12 +548,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
462 } 548 }
463 549
464 // Update output->{lx, ly, width, height} 550 // Update output->{lx, ly, width, height}
465 struct wlr_box *output_box = 551 struct wlr_box output_box;
466 wlr_output_layout_get_box(root->output_layout, wlr_output); 552 wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
467 output->lx = output_box->x; 553 output->lx = output_box.x;
468 output->ly = output_box->y; 554 output->ly = output_box.y;
469 output->width = output_box->width; 555 output->width = output_box.width;
470 output->height = output_box->height; 556 output->height = output_box.height;
471 557
472 if (!output->enabled) { 558 if (!output->enabled) {
473 output_enable(output); 559 output_enable(output);
@@ -483,11 +569,13 @@ 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 569 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present. 570 // dependent on an output being present.
485 input_manager_configure_all_inputs(); 571 input_manager_configure_all_inputs();
572 // Reconfigure the cursor images, since the scale may have changed.
573 input_manager_configure_xcursor();
486 return true; 574 return true;
487} 575}
488 576
489bool test_output_config(struct output_config *oc, struct sway_output *output) { 577bool test_output_config(struct output_config *oc, struct sway_output *output) {
490 if (output == root->noop_output) { 578 if (output == root->fallback_output) {
491 return false; 579 return false;
492 } 580 }
493 581
@@ -702,6 +790,8 @@ static bool _spawn_swaybg(char **command) {
702 sway_log_errno(SWAY_ERROR, "fork failed"); 790 sway_log_errno(SWAY_ERROR, "fork failed");
703 return false; 791 return false;
704 } else if (pid == 0) { 792 } else if (pid == 0) {
793 restore_nofile_limit();
794
705 pid = fork(); 795 pid = fork();
706 if (pid < 0) { 796 if (pid < 0) {
707 sway_log_errno(SWAY_ERROR, "fork failed"); 797 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..159f3336 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,8 +484,8 @@ 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->geometry.x - popup->base->current.geometry.x;
472 int popup_sy = popup->geometry.y - popup->base->geometry.y; 488 int popup_sy = popup->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) {
@@ -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..5b7ad4ee 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,
765 output->wlr_output, &output_box);
818 // We mark the output enabled even if it is switched off by DPMS 766 // We mark the output enabled even if it is switched off by DPMS
819 config_head->state.enabled = output->enabled; 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) {
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..51168f4c 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->geometry.x - wlr_popup->base->current.geometry.x,
31 31 wlr_popup->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);
@@ -336,25 +328,20 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
336static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 328static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
337 struct sway_xdg_shell_view *xdg_shell_view = 329 struct sway_xdg_shell_view *xdg_shell_view =
338 wl_container_of(listener, xdg_shell_view, request_fullscreen); 330 wl_container_of(listener, xdg_shell_view, request_fullscreen);
339 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 331 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; 332 struct sway_view *view = &xdg_shell_view->view;
343 333
344 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 334 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; 335 return;
351 } 336 }
352 337
353 struct sway_container *container = view->container; 338 struct sway_container *container = view->container;
354 if (e->fullscreen && e->output && e->output->data) { 339 struct wlr_xdg_toplevel_requested *req = &toplevel->requested;
355 struct sway_output *output = e->output->data; 340 if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {
341 struct sway_output *output = req->fullscreen_output->data;
356 struct sway_workspace *ws = output_get_active_workspace(output); 342 struct sway_workspace *ws = output_get_active_workspace(output);
357 if (ws && !container_is_scratchpad_hidden(container)) { 343 if (ws && !container_is_scratchpad_hidden(container) &&
344 container->pending.workspace != ws) {
358 if (container_is_floating(container)) { 345 if (container_is_floating(container)) {
359 workspace_add_floating(ws, container); 346 workspace_add_floating(ws, container);
360 } else { 347 } else {
@@ -363,22 +350,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
363 } 350 }
364 } 351 }
365 352
366 container_set_fullscreen(container, e->fullscreen); 353 container_set_fullscreen(container, req->fullscreen);
367 354
368 arrange_root(); 355 arrange_root();
369 transaction_commit_dirty(); 356 transaction_commit_dirty();
370} 357}
371 358
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) { 359static void handle_request_move(struct wl_listener *listener, void *data) {
378 struct sway_xdg_shell_view *xdg_shell_view = 360 struct sway_xdg_shell_view *xdg_shell_view =
379 wl_container_of(listener, xdg_shell_view, request_move); 361 wl_container_of(listener, xdg_shell_view, request_move);
380 struct sway_view *view = &xdg_shell_view->view; 362 struct sway_view *view = &xdg_shell_view->view;
381 if (!container_is_floating(view->container)) { 363 if (!container_is_floating(view->container) ||
364 view->container->pending.fullscreen_mode) {
382 return; 365 return;
383 } 366 }
384 struct wlr_xdg_toplevel_move_event *e = data; 367 struct wlr_xdg_toplevel_move_event *e = data;
@@ -416,7 +399,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
416 wl_list_remove(&xdg_shell_view->commit.link); 399 wl_list_remove(&xdg_shell_view->commit.link);
417 wl_list_remove(&xdg_shell_view->new_popup.link); 400 wl_list_remove(&xdg_shell_view->new_popup.link);
418 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 401 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
419 wl_list_remove(&xdg_shell_view->request_maximize.link);
420 wl_list_remove(&xdg_shell_view->request_move.link); 402 wl_list_remove(&xdg_shell_view->request_move.link);
421 wl_list_remove(&xdg_shell_view->request_resize.link); 403 wl_list_remove(&xdg_shell_view->request_resize.link);
422 wl_list_remove(&xdg_shell_view->set_title.link); 404 wl_list_remove(&xdg_shell_view->set_title.link);
@@ -427,62 +409,61 @@ static void handle_map(struct wl_listener *listener, void *data) {
427 struct sway_xdg_shell_view *xdg_shell_view = 409 struct sway_xdg_shell_view *xdg_shell_view =
428 wl_container_of(listener, xdg_shell_view, map); 410 wl_container_of(listener, xdg_shell_view, map);
429 struct sway_view *view = &xdg_shell_view->view; 411 struct sway_view *view = &xdg_shell_view->view;
430 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 412 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
431 413
432 view->natural_width = view->wlr_xdg_surface->geometry.width; 414 view->natural_width = toplevel->base->current.geometry.width;
433 view->natural_height = view->wlr_xdg_surface->geometry.height; 415 view->natural_height = toplevel->base->current.geometry.height;
434 if (!view->natural_width && !view->natural_height) { 416 if (!view->natural_width && !view->natural_height) {
435 view->natural_width = view->wlr_xdg_surface->surface->current.width; 417 view->natural_width = toplevel->base->surface->current.width;
436 view->natural_height = view->wlr_xdg_surface->surface->current.height; 418 view->natural_height = toplevel->base->surface->current.height;
437 } 419 }
438 420
439 bool csd = false; 421 bool csd = false;
440 422
441 if (!view->xdg_decoration) { 423 if (view->xdg_decoration) {
424 enum wlr_xdg_toplevel_decoration_v1_mode mode =
425 view->xdg_decoration->wlr_xdg_decoration->requested_mode;
426 csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
427 } else {
442 struct sway_server_decoration *deco = 428 struct sway_server_decoration *deco =
443 decoration_from_surface(xdg_surface->surface); 429 decoration_from_surface(toplevel->base->surface);
444 csd = !deco || deco->wlr_server_decoration->mode == 430 csd = !deco || deco->wlr_server_decoration->mode ==
445 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 431 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
446
447 } 432 }
448 433
449 view_map(view, view->wlr_xdg_surface->surface, 434 view_map(view, toplevel->base->surface,
450 xdg_surface->toplevel->client_pending.fullscreen, 435 toplevel->requested.fullscreen,
451 xdg_surface->toplevel->client_pending.fullscreen_output, 436 toplevel->requested.fullscreen_output,
452 csd); 437 csd);
453 438
454 transaction_commit_dirty(); 439 transaction_commit_dirty();
455 440
456 xdg_shell_view->commit.notify = handle_commit; 441 xdg_shell_view->commit.notify = handle_commit;
457 wl_signal_add(&xdg_surface->surface->events.commit, 442 wl_signal_add(&toplevel->base->surface->events.commit,
458 &xdg_shell_view->commit); 443 &xdg_shell_view->commit);
459 444
460 xdg_shell_view->new_popup.notify = handle_new_popup; 445 xdg_shell_view->new_popup.notify = handle_new_popup;
461 wl_signal_add(&xdg_surface->events.new_popup, 446 wl_signal_add(&toplevel->base->events.new_popup,
462 &xdg_shell_view->new_popup); 447 &xdg_shell_view->new_popup);
463 448
464 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; 449 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
465 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, 450 wl_signal_add(&toplevel->events.request_fullscreen,
466 &xdg_shell_view->request_fullscreen); 451 &xdg_shell_view->request_fullscreen);
467 452
468 xdg_shell_view->request_maximize.notify = handle_request_maximize;
469 wl_signal_add(&xdg_surface->toplevel->events.request_maximize,
470 &xdg_shell_view->request_maximize);
471
472 xdg_shell_view->request_move.notify = handle_request_move; 453 xdg_shell_view->request_move.notify = handle_request_move;
473 wl_signal_add(&xdg_surface->toplevel->events.request_move, 454 wl_signal_add(&toplevel->events.request_move,
474 &xdg_shell_view->request_move); 455 &xdg_shell_view->request_move);
475 456
476 xdg_shell_view->request_resize.notify = handle_request_resize; 457 xdg_shell_view->request_resize.notify = handle_request_resize;
477 wl_signal_add(&xdg_surface->toplevel->events.request_resize, 458 wl_signal_add(&toplevel->events.request_resize,
478 &xdg_shell_view->request_resize); 459 &xdg_shell_view->request_resize);
479 460
480 xdg_shell_view->set_title.notify = handle_set_title; 461 xdg_shell_view->set_title.notify = handle_set_title;
481 wl_signal_add(&xdg_surface->toplevel->events.set_title, 462 wl_signal_add(&toplevel->events.set_title,
482 &xdg_shell_view->set_title); 463 &xdg_shell_view->set_title);
483 464
484 xdg_shell_view->set_app_id.notify = handle_set_app_id; 465 xdg_shell_view->set_app_id.notify = handle_set_app_id;
485 wl_signal_add(&xdg_surface->toplevel->events.set_app_id, 466 wl_signal_add(&toplevel->events.set_app_id,
486 &xdg_shell_view->set_app_id); 467 &xdg_shell_view->set_app_id);
487} 468}
488 469
@@ -496,7 +477,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
496 wl_list_remove(&xdg_shell_view->destroy.link); 477 wl_list_remove(&xdg_shell_view->destroy.link);
497 wl_list_remove(&xdg_shell_view->map.link); 478 wl_list_remove(&xdg_shell_view->map.link);
498 wl_list_remove(&xdg_shell_view->unmap.link); 479 wl_list_remove(&xdg_shell_view->unmap.link);
499 view->wlr_xdg_surface = NULL; 480 view->wlr_xdg_toplevel = NULL;
500 if (view->xdg_decoration) { 481 if (view->xdg_decoration) {
501 view->xdg_decoration->view = NULL; 482 view->xdg_decoration->view = NULL;
502 } 483 }
@@ -527,7 +508,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
527 } 508 }
528 509
529 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 510 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
530 xdg_shell_view->view.wlr_xdg_surface = xdg_surface; 511 xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel;
531 512
532 xdg_shell_view->map.notify = handle_map; 513 xdg_shell_view->map.notify = handle_map;
533 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 514 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..0b2f03a2 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 = device->tablet;
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 }
@@ -914,7 +931,8 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
914static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { 931static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
915 struct sway_cursor *cursor = wl_container_of( 932 struct sway_cursor *cursor = wl_container_of(
916 listener, cursor, pinch_begin); 933 listener, cursor, pinch_begin);
917 struct wlr_event_pointer_pinch_begin *event = data; 934 struct wlr_pointer_pinch_begin_event *event = data;
935 cursor_handle_activity_from_device(cursor, &event->pointer->base);
918 wlr_pointer_gestures_v1_send_pinch_begin( 936 wlr_pointer_gestures_v1_send_pinch_begin(
919 cursor->pointer_gestures, cursor->seat->wlr_seat, 937 cursor->pointer_gestures, cursor->seat->wlr_seat,
920 event->time_msec, event->fingers); 938 event->time_msec, event->fingers);
@@ -923,7 +941,8 @@ static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data)
923static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { 941static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
924 struct sway_cursor *cursor = wl_container_of( 942 struct sway_cursor *cursor = wl_container_of(
925 listener, cursor, pinch_update); 943 listener, cursor, pinch_update);
926 struct wlr_event_pointer_pinch_update *event = data; 944 struct wlr_pointer_pinch_update_event *event = data;
945 cursor_handle_activity_from_device(cursor, &event->pointer->base);
927 wlr_pointer_gestures_v1_send_pinch_update( 946 wlr_pointer_gestures_v1_send_pinch_update(
928 cursor->pointer_gestures, cursor->seat->wlr_seat, 947 cursor->pointer_gestures, cursor->seat->wlr_seat,
929 event->time_msec, event->dx, event->dy, 948 event->time_msec, event->dx, event->dy,
@@ -933,7 +952,8 @@ static void handle_pointer_pinch_update(struct wl_listener *listener, void *data
933static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { 952static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
934 struct sway_cursor *cursor = wl_container_of( 953 struct sway_cursor *cursor = wl_container_of(
935 listener, cursor, pinch_end); 954 listener, cursor, pinch_end);
936 struct wlr_event_pointer_pinch_end *event = data; 955 struct wlr_pointer_pinch_end_event *event = data;
956 cursor_handle_activity_from_device(cursor, &event->pointer->base);
937 wlr_pointer_gestures_v1_send_pinch_end( 957 wlr_pointer_gestures_v1_send_pinch_end(
938 cursor->pointer_gestures, cursor->seat->wlr_seat, 958 cursor->pointer_gestures, cursor->seat->wlr_seat,
939 event->time_msec, event->cancelled); 959 event->time_msec, event->cancelled);
@@ -942,7 +962,8 @@ static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
942static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { 962static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
943 struct sway_cursor *cursor = wl_container_of( 963 struct sway_cursor *cursor = wl_container_of(
944 listener, cursor, swipe_begin); 964 listener, cursor, swipe_begin);
945 struct wlr_event_pointer_swipe_begin *event = data; 965 struct wlr_pointer_swipe_begin_event *event = data;
966 cursor_handle_activity_from_device(cursor, &event->pointer->base);
946 wlr_pointer_gestures_v1_send_swipe_begin( 967 wlr_pointer_gestures_v1_send_swipe_begin(
947 cursor->pointer_gestures, cursor->seat->wlr_seat, 968 cursor->pointer_gestures, cursor->seat->wlr_seat,
948 event->time_msec, event->fingers); 969 event->time_msec, event->fingers);
@@ -951,7 +972,8 @@ static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data)
951static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { 972static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
952 struct sway_cursor *cursor = wl_container_of( 973 struct sway_cursor *cursor = wl_container_of(
953 listener, cursor, swipe_update); 974 listener, cursor, swipe_update);
954 struct wlr_event_pointer_swipe_update *event = data; 975 struct wlr_pointer_swipe_update_event *event = data;
976 cursor_handle_activity_from_device(cursor, &event->pointer->base);
955 wlr_pointer_gestures_v1_send_swipe_update( 977 wlr_pointer_gestures_v1_send_swipe_update(
956 cursor->pointer_gestures, cursor->seat->wlr_seat, 978 cursor->pointer_gestures, cursor->seat->wlr_seat,
957 event->time_msec, event->dx, event->dy); 979 event->time_msec, event->dx, event->dy);
@@ -960,12 +982,33 @@ static void handle_pointer_swipe_update(struct wl_listener *listener, void *data
960static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { 982static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
961 struct sway_cursor *cursor = wl_container_of( 983 struct sway_cursor *cursor = wl_container_of(
962 listener, cursor, swipe_end); 984 listener, cursor, swipe_end);
963 struct wlr_event_pointer_swipe_end *event = data; 985 struct wlr_pointer_swipe_end_event *event = data;
986 cursor_handle_activity_from_device(cursor, &event->pointer->base);
964 wlr_pointer_gestures_v1_send_swipe_end( 987 wlr_pointer_gestures_v1_send_swipe_end(
965 cursor->pointer_gestures, cursor->seat->wlr_seat, 988 cursor->pointer_gestures, cursor->seat->wlr_seat,
966 event->time_msec, event->cancelled); 989 event->time_msec, event->cancelled);
967} 990}
968 991
992static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
993 struct sway_cursor *cursor = wl_container_of(
994 listener, cursor, hold_begin);
995 struct wlr_pointer_hold_begin_event *event = data;
996 cursor_handle_activity_from_device(cursor, &event->pointer->base);
997 wlr_pointer_gestures_v1_send_hold_begin(
998 cursor->pointer_gestures, cursor->seat->wlr_seat,
999 event->time_msec, event->fingers);
1000}
1001
1002static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
1003 struct sway_cursor *cursor = wl_container_of(
1004 listener, cursor, hold_end);
1005 struct wlr_pointer_hold_end_event *event = data;
1006 cursor_handle_activity_from_device(cursor, &event->pointer->base);
1007 wlr_pointer_gestures_v1_send_hold_end(
1008 cursor->pointer_gestures, cursor->seat->wlr_seat,
1009 event->time_msec, event->cancelled);
1010}
1011
969static void handle_image_surface_destroy(struct wl_listener *listener, 1012static void handle_image_surface_destroy(struct wl_listener *listener,
970 void *data) { 1013 void *data) {
971 struct sway_cursor *cursor = 1014 struct sway_cursor *cursor =
@@ -1043,6 +1086,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1043 wl_list_remove(&cursor->swipe_begin.link); 1086 wl_list_remove(&cursor->swipe_begin.link);
1044 wl_list_remove(&cursor->swipe_update.link); 1087 wl_list_remove(&cursor->swipe_update.link);
1045 wl_list_remove(&cursor->swipe_end.link); 1088 wl_list_remove(&cursor->swipe_end.link);
1089 wl_list_remove(&cursor->hold_begin.link);
1090 wl_list_remove(&cursor->hold_end.link);
1046 wl_list_remove(&cursor->motion.link); 1091 wl_list_remove(&cursor->motion.link);
1047 wl_list_remove(&cursor->motion_absolute.link); 1092 wl_list_remove(&cursor->motion_absolute.link);
1048 wl_list_remove(&cursor->button.link); 1093 wl_list_remove(&cursor->button.link);
@@ -1051,6 +1096,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1051 wl_list_remove(&cursor->touch_down.link); 1096 wl_list_remove(&cursor->touch_down.link);
1052 wl_list_remove(&cursor->touch_up.link); 1097 wl_list_remove(&cursor->touch_up.link);
1053 wl_list_remove(&cursor->touch_motion.link); 1098 wl_list_remove(&cursor->touch_motion.link);
1099 wl_list_remove(&cursor->touch_frame.link);
1054 wl_list_remove(&cursor->tool_axis.link); 1100 wl_list_remove(&cursor->tool_axis.link);
1055 wl_list_remove(&cursor->tool_tip.link); 1101 wl_list_remove(&cursor->tool_tip.link);
1056 wl_list_remove(&cursor->tool_button.link); 1102 wl_list_remove(&cursor->tool_button.link);
@@ -1098,6 +1144,10 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1098 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); 1144 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
1099 cursor->swipe_end.notify = handle_pointer_swipe_end; 1145 cursor->swipe_end.notify = handle_pointer_swipe_end;
1100 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); 1146 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
1147 cursor->hold_begin.notify = handle_pointer_hold_begin;
1148 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1149 cursor->hold_end.notify = handle_pointer_hold_end;
1150 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1101 1151
1102 // input events 1152 // input events
1103 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1153 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@@ -1126,6 +1176,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1126 &cursor->touch_motion); 1176 &cursor->touch_motion);
1127 cursor->touch_motion.notify = handle_touch_motion; 1177 cursor->touch_motion.notify = handle_touch_motion;
1128 1178
1179 wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame);
1180 cursor->touch_frame.notify = handle_touch_frame;
1181
1129 wl_signal_add(&wlr_cursor->events.tablet_tool_axis, 1182 wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
1130 &cursor->tool_axis); 1183 &cursor->tool_axis);
1131 cursor->tool_axis.notify = handle_tool_axis; 1184 cursor->tool_axis.notify = handle_tool_axis;
@@ -1170,8 +1223,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
1170 return; 1223 return;
1171 } 1224 }
1172 1225
1173 double x = container->x + container->width / 2.0; 1226 double x = container->pending.x + container->pending.width / 2.0;
1174 double y = container->y + container->height / 2.0; 1227 double y = container->pending.y + container->pending.height / 2.0;
1175 1228
1176 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1229 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1177 cursor_unhide(cursor); 1230 cursor_unhide(cursor);
@@ -1284,8 +1337,8 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1284 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1337 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1285 struct sway_container *con = view->container; 1338 struct sway_container *con = view->container;
1286 1339
1287 double lx = sx + con->content_x - view->geometry.x; 1340 double lx = sx + con->pending.content_x - view->geometry.x;
1288 double ly = sy + con->content_y - view->geometry.y; 1341 double ly = sy + con->pending.content_y - view->geometry.y;
1289 1342
1290 wlr_cursor_warp(cursor->cursor, NULL, lx, ly); 1343 wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
1291 1344
@@ -1333,7 +1386,7 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1333 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1386 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1334 1387
1335 struct sway_node *focus = seat_get_focus(seat); 1388 struct sway_node *focus = seat_get_focus(seat);
1336 if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { 1389 if (focus && node_is_view(focus)) {
1337 struct wlr_surface *surface = focus->sway_container->view->surface; 1390 struct wlr_surface *surface = focus->sway_container->view->surface;
1338 if (surface == constraint->surface) { 1391 if (surface == constraint->surface) {
1339 sway_cursor_constrain(seat->cursor, constraint); 1392 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..8f18b8ba 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"
@@ -379,8 +378,30 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
379 } 378 }
380} 379}
381 380
381/**
382 * Get keyboard grab of the seat from sway_keyboard if we should forward events
383 * to it.
384 *
385 * Returns NULL if the keyboard is not grabbed by an input method,
386 * or if event is from virtual keyboard of the same client as grab.
387 * TODO: see swaywm/wlroots#2322
388 */
389static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(
390 struct sway_keyboard *keyboard) {
391 struct wlr_input_method_v2 *input_method = keyboard->seat_device->
392 sway_seat->im_relay.input_method;
393 struct wlr_virtual_keyboard_v1 *virtual_keyboard =
394 wlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device);
395 if (!input_method || !input_method->keyboard_grab || (virtual_keyboard &&
396 wl_resource_get_client(virtual_keyboard->resource) ==
397 wl_resource_get_client(input_method->keyboard_grab->resource))) {
398 return NULL;
399 }
400 return input_method->keyboard_grab;
401}
402
382static void handle_key_event(struct sway_keyboard *keyboard, 403static void handle_key_event(struct sway_keyboard *keyboard,
383 struct wlr_event_keyboard_key *event) { 404 struct wlr_keyboard_key_event *event) {
384 struct sway_seat *seat = keyboard->seat_device->sway_seat; 405 struct sway_seat *seat = keyboard->seat_device->sway_seat;
385 struct wlr_seat *wlr_seat = seat->wlr_seat; 406 struct wlr_seat *wlr_seat = seat->wlr_seat;
386 struct wlr_input_device *wlr_device = 407 struct wlr_input_device *wlr_device =
@@ -388,7 +409,8 @@ static void handle_key_event(struct sway_keyboard *keyboard,
388 char *device_identifier = input_device_get_identifier(wlr_device); 409 char *device_identifier = input_device_get_identifier(wlr_device);
389 bool exact_identifier = wlr_device->keyboard->group != NULL; 410 bool exact_identifier = wlr_device->keyboard->group != NULL;
390 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); 411 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
391 bool input_inhibited = seat->exclusive_client != NULL; 412 bool input_inhibited = seat->exclusive_client != NULL ||
413 server.session_lock.locked;
392 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = 414 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
393 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 415 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
394 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; 416 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@@ -489,18 +511,42 @@ static void handle_key_event(struct sway_keyboard *keyboard,
489 keyinfo.raw_keysyms_len); 511 keyinfo.raw_keysyms_len);
490 } 512 }
491 513
492 if (!handled || event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { 514 if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
515 // If the pressed event was sent to a client, also send the released
516 // event. In particular, don't send the released event to the IM grab.
493 bool pressed_sent = update_shortcut_state( 517 bool pressed_sent = update_shortcut_state(
494 &keyboard->state_pressed_sent, event->keycode, event->state, 518 &keyboard->state_pressed_sent, event->keycode,
495 keyinfo.keycode, 0); 519 event->state, keyinfo.keycode, 0);
496 if (pressed_sent || event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 520 if (pressed_sent) {
497 wlr_seat_set_keyboard(wlr_seat, wlr_device); 521 wlr_seat_set_keyboard(wlr_seat, wlr_device->keyboard);
498 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 522 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
499 event->keycode, event->state); 523 event->keycode, event->state);
524 handled = true;
500 } 525 }
501 } 526 }
502 527
503 transaction_commit_dirty(); 528 if (!handled) {
529 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
530
531 if (kb_grab) {
532 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,
533 wlr_device->keyboard);
534 wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
535 event->time_msec, event->keycode, event->state);
536 handled = true;
537 }
538 }
539
540 if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) {
541 // If a released event failed pressed sent test, and not in sent to
542 // keyboard grab, it is still not handled. Don't handle released here.
543 update_shortcut_state(
544 &keyboard->state_pressed_sent, event->keycode, event->state,
545 keyinfo.keycode, 0);
546 wlr_seat_set_keyboard(wlr_seat, wlr_device->keyboard);
547 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
548 event->keycode, event->state);
549 }
504 550
505 free(device_identifier); 551 free(device_identifier);
506} 552}
@@ -587,7 +633,6 @@ static int handle_keyboard_repeat(void *data) {
587 633
588 seat_execute_command(keyboard->seat_device->sway_seat, 634 seat_execute_command(keyboard->seat_device->sway_seat,
589 keyboard->repeat_binding); 635 keyboard->repeat_binding);
590 transaction_commit_dirty();
591 } 636 }
592 return 0; 637 return 0;
593} 638}
@@ -617,10 +662,19 @@ static void handle_modifier_event(struct sway_keyboard *keyboard) {
617 struct wlr_input_device *wlr_device = 662 struct wlr_input_device *wlr_device =
618 keyboard->seat_device->input_device->wlr_device; 663 keyboard->seat_device->input_device->wlr_device;
619 if (!wlr_device->keyboard->group) { 664 if (!wlr_device->keyboard->group) {
620 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 665 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
621 wlr_seat_set_keyboard(wlr_seat, wlr_device); 666
622 wlr_seat_keyboard_notify_modifiers(wlr_seat, 667 if (kb_grab) {
623 &wlr_device->keyboard->modifiers); 668 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,
669 wlr_device->keyboard);
670 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
671 &wlr_device->keyboard->modifiers);
672 } else {
673 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
674 wlr_seat_set_keyboard(wlr_seat, wlr_device->keyboard);
675 wlr_seat_keyboard_notify_modifiers(wlr_seat,
676 &wlr_device->keyboard->modifiers);
677 }
624 678
625 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); 679 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
626 determine_bar_visibility(modifiers); 680 determine_bar_visibility(modifiers);
@@ -897,7 +951,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
897 goto cleanup; 951 goto cleanup;
898 } 952 }
899 sway_group->seat_device->input_device->wlr_device = 953 sway_group->seat_device->input_device->wlr_device =
900 sway_group->wlr_group->input_device; 954 &sway_group->wlr_group->keyboard.base;
901 955
902 if (!sway_keyboard_create(seat, sway_group->seat_device)) { 956 if (!sway_keyboard_create(seat, sway_group->seat_device)) {
903 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); 957 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
@@ -1033,7 +1087,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1033 } 1087 }
1034 1088
1035 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; 1089 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
1036 wlr_seat_set_keyboard(seat, wlr_device); 1090 wlr_seat_set_keyboard(seat, wlr_device->keyboard);
1037 1091
1038 wl_list_remove(&keyboard->keyboard_key.link); 1092 wl_list_remove(&keyboard->keyboard_key.link);
1039 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); 1093 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
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..11c78154 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
@@ -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 = dev->pointer->output_name;
749 break;
750 case WLR_INPUT_DEVICE_TOUCH:
751 mapped_to_output = dev->touch->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 seat_device->input_device->wlr_device->keyboard);
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 }
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index a583ed62..15d1ca8b 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -4,6 +4,7 @@
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 "sway/desktop/transaction.h"
7#include "sway/input/cursor.h" 8#include "sway/input/cursor.h"
8#include "sway/input/seat.h" 9#include "sway/input/seat.h"
9#include "sway/input/tablet.h" 10#include "sway/input/tablet.h"
@@ -59,7 +60,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
59 return false; 60 return false;
60 } 61 }
61 } 62 }
62 cont = cont->parent; 63 cont = cont->pending.parent;
63 } 64 }
64 return true; 65 return true;
65} 66}
@@ -69,25 +70,25 @@ static enum wlr_edges find_edge(struct sway_container *cont,
69 if (!cont->view || (surface && cont->view->surface != surface)) { 70 if (!cont->view || (surface && cont->view->surface != surface)) {
70 return WLR_EDGE_NONE; 71 return WLR_EDGE_NONE;
71 } 72 }
72 if (cont->border == B_NONE || !cont->border_thickness || 73 if (cont->pending.border == B_NONE || !cont->pending.border_thickness ||
73 cont->border == B_CSD) { 74 cont->pending.border == B_CSD) {
74 return WLR_EDGE_NONE; 75 return WLR_EDGE_NONE;
75 } 76 }
76 if (cont->fullscreen_mode) { 77 if (cont->pending.fullscreen_mode) {
77 return WLR_EDGE_NONE; 78 return WLR_EDGE_NONE;
78 } 79 }
79 80
80 enum wlr_edges edge = 0; 81 enum wlr_edges edge = 0;
81 if (cursor->cursor->x < cont->x + cont->border_thickness) { 82 if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) {
82 edge |= WLR_EDGE_LEFT; 83 edge |= WLR_EDGE_LEFT;
83 } 84 }
84 if (cursor->cursor->y < cont->y + cont->border_thickness) { 85 if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) {
85 edge |= WLR_EDGE_TOP; 86 edge |= WLR_EDGE_TOP;
86 } 87 }
87 if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { 88 if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) {
88 edge |= WLR_EDGE_RIGHT; 89 edge |= WLR_EDGE_RIGHT;
89 } 90 }
90 if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { 91 if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) {
91 edge |= WLR_EDGE_BOTTOM; 92 edge |= WLR_EDGE_BOTTOM;
92 } 93 }
93 94
@@ -231,6 +232,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
231 wlr_layer_surface_v1_from_wlr_surface(surface); 232 wlr_layer_surface_v1_from_wlr_surface(surface);
232 if (layer->current.keyboard_interactive) { 233 if (layer->current.keyboard_interactive) {
233 seat_set_focus_layer(seat, layer); 234 seat_set_focus_layer(seat, layer);
235 transaction_commit_dirty();
234 } 236 }
235 } else if (cont) { 237 } else if (cont) {
236 bool is_floating_or_child = container_is_floating_or_child(cont); 238 bool is_floating_or_child = container_is_floating_or_child(cont);
@@ -249,7 +251,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
249 251
250 // Handle moving a tiling container 252 // Handle moving a tiling container
251 if (config->tiling_drag && mod_pressed && !is_floating_or_child && 253 if (config->tiling_drag && mod_pressed && !is_floating_or_child &&
252 cont->fullscreen_mode == FULLSCREEN_NONE) { 254 cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
253 seatop_begin_move_tiling(seat, cont); 255 seatop_begin_move_tiling(seat, cont);
254 return; 256 return;
255 } 257 }
@@ -268,6 +270,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
268 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 270 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
269 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 271 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
270 seat_set_focus_surface(seat, xsurface->surface, false); 272 seat_set_focus_surface(seat, xsurface->surface, false);
273 transaction_commit_dirty();
271 } 274 }
272 } 275 }
273#endif 276#endif
@@ -356,6 +359,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
356 if (node && node->type == N_WORKSPACE) { 359 if (node && node->type == N_WORKSPACE) {
357 if (state == WLR_BUTTON_PRESSED) { 360 if (state == WLR_BUTTON_PRESSED) {
358 seat_set_focus(seat, node); 361 seat_set_focus(seat, node);
362 transaction_commit_dirty();
359 } 363 }
360 seat_pointer_notify_button(seat, time_msec, button, state); 364 seat_pointer_notify_button(seat, time_msec, button, state);
361 return; 365 return;
@@ -367,6 +371,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
367 wlr_layer_surface_v1_from_wlr_surface(surface); 371 wlr_layer_surface_v1_from_wlr_surface(surface);
368 if (layer->current.keyboard_interactive) { 372 if (layer->current.keyboard_interactive) {
369 seat_set_focus_layer(seat, layer); 373 seat_set_focus_layer(seat, layer);
374 transaction_commit_dirty();
375 }
376 if (state == WLR_BUTTON_PRESSED) {
377 seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy);
370 } 378 }
371 seat_pointer_notify_button(seat, time_msec, button, state); 379 seat_pointer_notify_button(seat, time_msec, button, state);
372 return; 380 return;
@@ -381,7 +389,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
381 struct sway_container *cont_to_focus = cont; 389 struct sway_container *cont_to_focus = cont;
382 enum sway_container_layout layout = container_parent_layout(cont); 390 enum sway_container_layout layout = container_parent_layout(cont);
383 if (layout == L_TABBED || layout == L_STACKED) { 391 if (layout == L_TABBED || layout == L_STACKED) {
384 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); 392 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node);
385 } 393 }
386 394
387 seat_set_focus_container(seat, cont_to_focus); 395 seat_set_focus_container(seat, cont_to_focus);
@@ -397,9 +405,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
397 BTN_LEFT : BTN_RIGHT; 405 BTN_LEFT : BTN_RIGHT;
398 if (button == btn_resize) { 406 if (button == btn_resize) {
399 edge = 0; 407 edge = 0;
400 edge |= cursor->cursor->x > cont->x + cont->width / 2 ? 408 edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ?
401 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 409 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
402 edge |= cursor->cursor->y > cont->y + cont->height / 2 ? 410 edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ?
403 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 411 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
404 412
405 const char *image = NULL; 413 const char *image = NULL;
@@ -446,9 +454,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
446 if (mod_pressed && button == btn_resize) { 454 if (mod_pressed && button == btn_resize) {
447 struct sway_container *floater = container_toplevel_ancestor(cont); 455 struct sway_container *floater = container_toplevel_ancestor(cont);
448 edge = 0; 456 edge = 0;
449 edge |= cursor->cursor->x > floater->x + floater->width / 2 ? 457 edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ?
450 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 458 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
451 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 459 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
452 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 460 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
453 seatop_begin_resize_floating(seat, floater, edge); 461 seatop_begin_resize_floating(seat, floater, edge);
454 return; 462 return;
@@ -458,7 +466,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
458 // Handle moving a tiling container 466 // Handle moving a tiling container
459 if (config->tiling_drag && (mod_pressed || on_titlebar) && 467 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
460 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 468 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
461 cont && cont->fullscreen_mode == FULLSCREEN_NONE) { 469 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
462 struct sway_container *focus = seat_get_focused_container(seat); 470 struct sway_container *focus = seat_get_focused_container(seat);
463 bool focused = focus == cont || container_has_ancestor(focus, cont); 471 bool focused = focus == cont || container_has_ancestor(focus, cont);
464 if (on_titlebar && !focused) { 472 if (on_titlebar && !focused) {
@@ -466,7 +474,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
466 seat_set_focus(seat, node); 474 seat_set_focus(seat, node);
467 } 475 }
468 476
469 // If moving a container by it's title bar, use a threshold for the drag 477 // If moving a container by its title bar, use a threshold for the drag
470 if (!mod_pressed && config->tiling_drag_threshold > 0) { 478 if (!mod_pressed && config->tiling_drag_threshold > 0) {
471 seatop_begin_move_tiling_threshold(seat, cont); 479 seatop_begin_move_tiling_threshold(seat, cont);
472 } else { 480 } else {
@@ -487,6 +495,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
487 if (cont && state == WLR_BUTTON_PRESSED) { 495 if (cont && state == WLR_BUTTON_PRESSED) {
488 node = seat_get_focus_inactive(seat, &cont->node); 496 node = seat_get_focus_inactive(seat, &cont->node);
489 seat_set_focus(seat, node); 497 seat_set_focus(seat, node);
498 transaction_commit_dirty();
490 seat_pointer_notify_button(seat, time_msec, button, state); 499 seat_pointer_notify_button(seat, time_msec, button, state);
491 return; 500 return;
492 } 501 }
@@ -501,6 +510,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
501 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 510 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
502 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 511 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
503 seat_set_focus_surface(seat, xsurface->surface, false); 512 seat_set_focus_surface(seat, xsurface->surface, false);
513 transaction_commit_dirty();
504 seat_pointer_notify_button(seat, time_msec, button, state); 514 seat_pointer_notify_button(seat, time_msec, button, state);
505 return; 515 return;
506 } 516 }
@@ -530,6 +540,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
530 if (focus && hovered_output != node_get_output(focus)) { 540 if (focus && hovered_output != node_get_output(focus)) {
531 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 541 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
532 seat_set_focus(seat, &ws->node); 542 seat_set_focus(seat, &ws->node);
543 transaction_commit_dirty();
533 } 544 }
534 return; 545 return;
535 } 546 }
@@ -541,6 +552,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
541 struct sway_output *hovered_output = node_get_output(hovered_node); 552 struct sway_output *hovered_output = node_get_output(hovered_node);
542 if (hovered_output != focused_output) { 553 if (hovered_output != focused_output) {
543 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); 554 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node));
555 transaction_commit_dirty();
544 } 556 }
545 return; 557 return;
546 } 558 }
@@ -556,6 +568,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
556 if (hovered_node != e->previous_node || 568 if (hovered_node != e->previous_node ||
557 config->focus_follows_mouse == FOLLOWS_ALWAYS) { 569 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
558 seat_set_focus(seat, hovered_node); 570 seat_set_focus(seat, hovered_node);
571 transaction_commit_dirty();
559 } 572 }
560 } 573 }
561} 574}
@@ -632,7 +645,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
632 * Functions used by handle_pointer_axis / 645 * Functions used by handle_pointer_axis /
633 *--------------------------------------*/ 646 *--------------------------------------*/
634 647
635static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { 648static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
636 switch (event->orientation) { 649 switch (event->orientation) {
637 case WLR_AXIS_ORIENTATION_VERTICAL: 650 case WLR_AXIS_ORIENTATION_VERTICAL:
638 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 651 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
@@ -645,9 +658,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
645} 658}
646 659
647static void handle_pointer_axis(struct sway_seat *seat, 660static void handle_pointer_axis(struct sway_seat *seat,
648 struct wlr_event_pointer_axis *event) { 661 struct wlr_pointer_axis_event *event) {
649 struct sway_input_device *input_device = 662 struct sway_input_device *input_device =
650 event->device ? event->device->data : NULL; 663 event->pointer ? event->pointer->base.data : NULL;
651 struct input_config *ic = 664 struct input_config *ic =
652 input_device ? input_device_get_config(input_device) : NULL; 665 input_device ? input_device_get_config(input_device) : NULL;
653 struct sway_cursor *cursor = seat->cursor; 666 struct sway_cursor *cursor = seat->cursor;
@@ -664,7 +677,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
664 bool on_border = edge != WLR_EDGE_NONE; 677 bool on_border = edge != WLR_EDGE_NONE;
665 bool on_titlebar = cont && !on_border && !surface; 678 bool on_titlebar = cont && !on_border && !surface;
666 bool on_titlebar_border = cont && on_border && 679 bool on_titlebar_border = cont && on_border &&
667 cursor->cursor->y < cont->content_y; 680 cursor->cursor->y < cont->pending.content_y;
668 bool on_contents = cont && !on_border && surface; 681 bool on_contents = cont && !on_border && surface;
669 bool on_workspace = node && node->type == N_WORKSPACE; 682 bool on_workspace = node && node->type == N_WORKSPACE;
670 float scroll_factor = 683 float scroll_factor =
@@ -714,6 +727,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
714 // Use the focused child of the tabbed/stacked container, not the 727 // Use the focused child of the tabbed/stacked container, not the
715 // container the user scrolled on. 728 // container the user scrolled on.
716 seat_set_focus(seat, new_focus); 729 seat_set_focus(seat, new_focus);
730 transaction_commit_dirty();
717 handled = true; 731 handled = true;
718 } 732 }
719 } 733 }
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..ac4baece 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"
@@ -20,9 +19,23 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
20 return switch_device; 19 return switch_device;
21} 20}
22 21
22static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
23 enum wlr_switch_state state) {
24 switch (trigger) {
25 case SWAY_SWITCH_TRIGGER_ON:
26 return state == WLR_SWITCH_STATE_ON;
27 case SWAY_SWITCH_TRIGGER_OFF:
28 return state == WLR_SWITCH_STATE_OFF;
29 case SWAY_SWITCH_TRIGGER_TOGGLE:
30 return true;
31 }
32 abort(); // unreachable
33}
34
23static void execute_binding(struct sway_switch *sway_switch) { 35static void execute_binding(struct sway_switch *sway_switch) {
24 struct sway_seat* seat = sway_switch->seat_device->sway_seat; 36 struct sway_seat* seat = sway_switch->seat_device->sway_seat;
25 bool input_inhibited = seat->exclusive_client != NULL; 37 bool input_inhibited = seat->exclusive_client != NULL ||
38 server.session_lock.locked;
26 39
27 list_t *bindings = config->current_mode->switch_bindings; 40 list_t *bindings = config->current_mode->switch_bindings;
28 struct sway_switch_binding *matched_binding = NULL; 41 struct sway_switch_binding *matched_binding = NULL;
@@ -31,11 +44,10 @@ static void execute_binding(struct sway_switch *sway_switch) {
31 if (binding->type != sway_switch->type) { 44 if (binding->type != sway_switch->type) {
32 continue; 45 continue;
33 } 46 }
34 if (binding->state != WLR_SWITCH_STATE_TOGGLE && 47 if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {
35 binding->state != sway_switch->state) {
36 continue; 48 continue;
37 } 49 }
38 if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE 50 if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE
39 || (binding->flags & BINDING_RELOAD) == 0)) { 51 || (binding->flags & BINDING_RELOAD) == 0)) {
40 continue; 52 continue;
41 } 53 }
@@ -61,15 +73,12 @@ static void execute_binding(struct sway_switch *sway_switch) {
61 seat_execute_command(seat, dummy_binding); 73 seat_execute_command(seat, dummy_binding);
62 free(dummy_binding); 74 free(dummy_binding);
63 } 75 }
64
65 transaction_commit_dirty();
66
67} 76}
68 77
69static void handle_switch_toggle(struct wl_listener *listener, void *data) { 78static void handle_switch_toggle(struct wl_listener *listener, void *data) {
70 struct sway_switch *sway_switch = 79 struct sway_switch *sway_switch =
71 wl_container_of(listener, sway_switch, switch_toggle); 80 wl_container_of(listener, sway_switch, switch_toggle);
72 struct wlr_event_switch_toggle *event = data; 81 struct wlr_switch_toggle_event *event = data;
73 struct sway_seat *seat = sway_switch->seat_device->sway_seat; 82 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
74 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); 83 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);
75 84
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 26e86e36..db2f93ec 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;
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..e682bc36 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,61 @@ 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));
242 json_object_object_add(object, "layout", json_object_new_string("output")); 280 json_object_object_add(object, "layout", json_object_new_string("output"));
243 json_object_object_add(object, "orientation", 281 json_object_object_add(object, "orientation",
244 json_object_new_string( 282 json_object_new_string(
245 ipc_json_orientation_description(L_NONE))); 283 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", 284 json_object_object_add(object, "scale",
253 json_object_new_double(wlr_output->scale)); 285 json_object_new_double(wlr_output->scale));
254 json_object_object_add(object, "scale_filter", 286 json_object_object_add(object, "scale_filter",
@@ -315,33 +347,13 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
315 347
316 json_object *object = json_object_new_object(); 348 json_object *object = json_object_new_object();
317 349
350 ipc_json_describe_output(output, object);
351
318 json_object_object_add(object, "type", json_object_new_string("output")); 352 json_object_object_add(object, "type", json_object_new_string("output"));
319 json_object_object_add(object, "name", 353 json_object_object_add(object, "name",
320 json_object_new_string(wlr_output->name)); 354 json_object_new_string(wlr_output->name));
321 json_object_object_add(object, "active", json_object_new_boolean(false)); 355 json_object_object_add(object, "active", json_object_new_boolean(false));
322 json_object_object_add(object, "dpms", json_object_new_boolean(false)); 356 json_object_object_add(object, "dpms", json_object_new_boolean(false));
323 json_object_object_add(object, "primary", 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 357
346 json_object_object_add(object, "current_workspace", NULL); 358 json_object_object_add(object, "current_workspace", NULL);
347 359
@@ -369,11 +381,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
369 json_object_new_int(container->node.id)); 381 json_object_new_int(container->node.id));
370 } 382 }
371 383
372 json_object *workspace = ipc_json_create_node(i3_scratch_id, 384 json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace",
373 "__i3_scratch", false, workspace_focus, &box); 385 "__i3_scratch", false, workspace_focus, &box);
374 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); 386 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 387
378 // List all hidden scratchpad containers as floating nodes 388 // List all hidden scratchpad containers as floating nodes
379 json_object *floating_array = json_object_new_array(); 389 json_object *floating_array = json_object_new_array();
@@ -390,10 +400,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
390 json_object *output_focus = json_object_new_array(); 400 json_object *output_focus = json_object_new_array();
391 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); 401 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
392 402
393 json_object *output = ipc_json_create_node(i3_output_id, 403 json_object *output = ipc_json_create_node(i3_output_id, "output",
394 "__i3", false, output_focus, &box); 404 "__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", 405 json_object_object_add(output, "layout",
398 json_object_new_string("output")); 406 json_object_new_string("output"));
399 407
@@ -423,7 +431,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
423 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); 431 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1));
424 json_object_object_add(object, "output", workspace->output ? 432 json_object_object_add(object, "output", workspace->output ?
425 json_object_new_string(workspace->output->wlr_output->name) : NULL); 433 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", 434 json_object_object_add(object, "urgent",
428 json_object_new_boolean(workspace->urgent)); 435 json_object_new_boolean(workspace->urgent));
429 json_object_object_add(object, "representation", workspace->representation ? 436 json_object_object_add(object, "representation", workspace->representation ?
@@ -451,27 +458,27 @@ static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
451 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; 458 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED;
452 if (((!tab_or_stack || container_is_floating(c)) && 459 if (((!tab_or_stack || container_is_floating(c)) &&
453 c->current.border != B_NORMAL) || 460 c->current.border != B_NORMAL) ||
454 c->fullscreen_mode != FULLSCREEN_NONE || 461 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
455 c->workspace == NULL) { 462 c->pending.workspace == NULL) {
456 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; 463 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0;
457 return; 464 return;
458 } 465 }
459 466
460 if (c->parent) { 467 if (c->pending.parent) {
461 deco_rect->x = c->x - c->parent->x; 468 deco_rect->x = c->pending.x - c->pending.parent->pending.x;
462 deco_rect->y = c->y - c->parent->y; 469 deco_rect->y = c->pending.y - c->pending.parent->pending.y;
463 } else { 470 } else {
464 deco_rect->x = c->x - c->workspace->x; 471 deco_rect->x = c->pending.x - c->pending.workspace->x;
465 deco_rect->y = c->y - c->workspace->y; 472 deco_rect->y = c->pending.y - c->pending.workspace->y;
466 } 473 }
467 deco_rect->width = c->width; 474 deco_rect->width = c->pending.width;
468 deco_rect->height = container_titlebar_height(); 475 deco_rect->height = container_titlebar_height();
469 476
470 if (!container_is_floating(c)) { 477 if (!container_is_floating(c)) {
471 if (parent_layout == L_TABBED) { 478 if (parent_layout == L_TABBED) {
472 deco_rect->width = c->parent 479 deco_rect->width = c->pending.parent
473 ? c->parent->width / c->parent->children->length 480 ? c->pending.parent->pending.width / c->pending.parent->pending.children->length
474 : c->workspace->width / c->workspace->tiling->length; 481 : c->pending.workspace->width / c->pending.workspace->tiling->length;
475 deco_rect->x += deco_rect->width * container_sibling_index(c); 482 deco_rect->x += deco_rect->width * container_sibling_index(c);
476 } else if (parent_layout == L_STACKED) { 483 } else if (parent_layout == L_STACKED) {
477 if (!c->view) { 484 if (!c->view) {
@@ -494,10 +501,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)); 501 json_object_object_add(object, "visible", json_object_new_boolean(visible));
495 502
496 struct wlr_box window_box = { 503 struct wlr_box window_box = {
497 c->content_x - c->x, 504 c->pending.content_x - c->pending.x,
498 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 505 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0,
499 c->content_width, 506 c->pending.content_width,
500 c->content_height 507 c->pending.content_height
501 }; 508 };
502 509
503 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); 510 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box));
@@ -583,16 +590,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) { 590static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
584 json_object_object_add(object, "name", 591 json_object_object_add(object, "name",
585 c->title ? json_object_new_string(c->title) : NULL); 592 c->title ? json_object_new_string(c->title) : NULL);
586 json_object_object_add(object, "type", 593 if (container_is_floating(c)) {
587 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 594 json_object_object_add(object, "type",
595 json_object_new_string("floating_con"));
596 }
588 597
589 json_object_object_add(object, "layout", 598 json_object_object_add(object, "layout",
590 json_object_new_string( 599 json_object_new_string(
591 ipc_json_layout_description(c->layout))); 600 ipc_json_layout_description(c->pending.layout)));
592 601
593 json_object_object_add(object, "orientation", 602 json_object_object_add(object, "orientation",
594 json_object_new_string( 603 json_object_new_string(
595 ipc_json_orientation_description(c->layout))); 604 ipc_json_orientation_description(c->pending.layout)));
596 605
597 bool urgent = c->view ? 606 bool urgent = c->view ?
598 view_is_urgent(c->view) : container_has_urgent_child(c); 607 view_is_urgent(c->view) : container_has_urgent_child(c);
@@ -600,7 +609,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)); 609 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
601 610
602 json_object_object_add(object, "fullscreen_mode", 611 json_object_object_add(object, "fullscreen_mode",
603 json_object_new_int(c->fullscreen_mode)); 612 json_object_new_int(c->pending.fullscreen_mode));
604 613
605 struct sway_node *parent = node_get_parent(&c->node); 614 struct sway_node *parent = node_get_parent(&c->node);
606 struct wlr_box parent_box = {0, 0, 0, 0}; 615 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -610,8 +619,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
610 } 619 }
611 620
612 if (parent_box.width != 0 && parent_box.height != 0) { 621 if (parent_box.width != 0 && parent_box.height != 0) {
613 double percent = ((double)c->width / parent_box.width) 622 double percent = ((double)c->pending.width / parent_box.width)
614 * ((double)c->height / parent_box.height); 623 * ((double)c->pending.height / parent_box.height);
615 json_object_object_add(object, "percent", json_object_new_double(percent)); 624 json_object_object_add(object, "percent", json_object_new_double(percent));
616 } 625 }
617 626
@@ -692,15 +701,14 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
692 }; 701 };
693 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 702 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
694 703
695 json_object *object = ipc_json_create_node( 704 json_object *object = ipc_json_create_node((int)node->id,
696 (int)node->id, name, focused, focus, &box); 705 ipc_json_node_type_description(node->type), name, focused, focus, &box);
697 706
698 switch (node->type) { 707 switch (node->type) {
699 case N_ROOT: 708 case N_ROOT:
700 ipc_json_describe_root(root, object);
701 break; 709 break;
702 case N_OUTPUT: 710 case N_OUTPUT:
703 ipc_json_describe_output(node->sway_output, object); 711 ipc_json_describe_enabled_output(node->sway_output, object);
704 break; 712 break;
705 case N_CONTAINER: 713 case N_CONTAINER:
706 ipc_json_describe_container(node->sway_container, object); 714 ipc_json_describe_container(node->sway_container, object);
@@ -743,10 +751,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
743 } 751 }
744 break; 752 break;
745 case N_CONTAINER: 753 case N_CONTAINER:
746 if (node->sway_container->children) { 754 if (node->sway_container->pending.children) {
747 for (i = 0; i < node->sway_container->children->length; ++i) { 755 for (i = 0; i < node->sway_container->pending.children->length; ++i) {
748 struct sway_container *child = 756 struct sway_container *child =
749 node->sway_container->children->items[i]; 757 node->sway_container->pending.children->items[i];
750 json_object_array_add(children, 758 json_object_array_add(children,
751 ipc_json_describe_node_recursive(&child->node)); 759 ipc_json_describe_node_recursive(&child->node));
752 } 760 }
@@ -974,6 +982,11 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
974 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 982 struct wlr_keyboard *keyboard = device->wlr_device->keyboard;
975 struct xkb_keymap *keymap = keyboard->keymap; 983 struct xkb_keymap *keymap = keyboard->keymap;
976 struct xkb_state *state = keyboard->xkb_state; 984 struct xkb_state *state = keyboard->xkb_state;
985
986 json_object_object_add(object, "repeat_delay",
987 json_object_new_int(keyboard->repeat_info.delay));
988 json_object_object_add(object, "repeat_rate",
989 json_object_new_int(keyboard->repeat_info.rate));
977 990
978 json_object *layouts_arr = json_object_new_array(); 991 json_object *layouts_arr = json_object_new_array();
979 json_object_object_add(object, "xkb_layout_names", layouts_arr); 992 json_object_object_add(object, "xkb_layout_names", layouts_arr);
@@ -996,6 +1009,17 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
996 } 1009 }
997 } 1010 }
998 1011
1012 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1013 struct input_config *ic = input_device_get_config(device);
1014 float scroll_factor = 1.0f;
1015 if (ic != NULL && !isnan(ic->scroll_factor) &&
1016 ic->scroll_factor != FLT_MIN) {
1017 scroll_factor = ic->scroll_factor;
1018 }
1019 json_object_object_add(object, "scroll_factor",
1020 json_object_new_double(scroll_factor));
1021 }
1022
999 if (wlr_input_device_is_libinput(device->wlr_device)) { 1023 if (wlr_input_device_is_libinput(device->wlr_device)) {
1000 struct libinput_device *libinput_dev; 1024 struct libinput_device *libinput_dev;
1001 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1025 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
@@ -1109,7 +1133,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
1109 json_object_object_add(json, "verbose", 1133 json_object_object_add(json, "verbose",
1110 json_object_new_boolean(bar->verbose)); 1134 json_object_new_boolean(bar->verbose));
1111 json_object_object_add(json, "pango_markup", 1135 json_object_object_add(json, "pango_markup",
1112 json_object_new_boolean(bar->pango_markup)); 1136 json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT
1137 ? config->pango_markup
1138 : bar->pango_markup));
1113 1139
1114 json_object *colors = json_object_new_object(); 1140 json_object *colors = json_object_new_object();
1115 json_object_object_add(colors, "background", 1141 json_object_object_add(colors, "background",
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index aad9a7b5..1bf5a05f 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -687,7 +687,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
687 } 687 }
688 struct sway_output *output; 688 struct sway_output *output;
689 wl_list_for_each(output, &root->all_outputs, link) { 689 wl_list_for_each(output, &root->all_outputs, link) {
690 if (!output->enabled && output != root->noop_output) { 690 if (!output->enabled && output != root->fallback_output) {
691 json_object_array_add(outputs, 691 json_object_array_add(outputs,
692 ipc_json_describe_disabled_output(output)); 692 ipc_json_describe_disabled_output(output));
693 } 693 }
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..a0033c45 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;
@@ -187,6 +153,9 @@ static void log_kernel(void) {
187 153
188static bool drop_permissions(void) { 154static bool drop_permissions(void) {
189 if (getuid() != geteuid() || getgid() != getegid()) { 155 if (getuid() != geteuid() || getgid() != getegid()) {
156 sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: "
157 "SUID privilege drop will be removed in a future release, please migrate to seatd-launch");
158
190 // Set the gid and uid in the correct order. 159 // Set the gid and uid in the correct order.
191 if (setgid(getgid()) != 0) { 160 if (setgid(getgid()) != 0) {
192 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); 161 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
@@ -205,6 +174,33 @@ static bool drop_permissions(void) {
205 return true; 174 return true;
206} 175}
207 176
177static void increase_nofile_limit(void) {
178 if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
179 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
180 "getrlimit(NOFILE) failed");
181 return;
182 }
183
184 struct rlimit new_rlimit = original_nofile_rlimit;
185 new_rlimit.rlim_cur = new_rlimit.rlim_max;
186 if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {
187 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
188 "setrlimit(NOFILE) failed");
189 sway_log(SWAY_INFO, "Running with %d max open files",
190 (int)original_nofile_rlimit.rlim_cur);
191 }
192}
193
194void restore_nofile_limit(void) {
195 if (original_nofile_rlimit.rlim_cur == 0) {
196 return;
197 }
198 if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
199 sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: "
200 "setrlimit(NOFILE) failed");
201 }
202}
203
208void enable_debug_flag(const char *flag) { 204void enable_debug_flag(const char *flag) {
209 if (strcmp(flag, "damage=highlight") == 0) { 205 if (strcmp(flag, "damage=highlight") == 0) {
210 debug.damage = DAMAGE_HIGHLIGHT; 206 debug.damage = DAMAGE_HIGHLIGHT;
@@ -218,6 +214,8 @@ void enable_debug_flag(const char *flag) {
218 debug.txn_timings = true; 214 debug.txn_timings = true;
219 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 215 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
220 server.txn_timeout_ms = atoi(&flag[12]); 216 server.txn_timeout_ms = atoi(&flag[12]);
217 } else if (strcmp(flag, "noscanout") == 0) {
218 debug.noscanout = true;
221 } else { 219 } else {
222 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 220 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
223 } 221 }
@@ -242,36 +240,35 @@ static void handle_wlr_log(enum wlr_log_importance importance,
242 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); 240 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
243} 241}
244 242
243static const struct option long_options[] = {
244 {"help", no_argument, NULL, 'h'},
245 {"config", required_argument, NULL, 'c'},
246 {"validate", no_argument, NULL, 'C'},
247 {"debug", no_argument, NULL, 'd'},
248 {"version", no_argument, NULL, 'v'},
249 {"verbose", no_argument, NULL, 'V'},
250 {"get-socketpath", no_argument, NULL, 'p'},
251 {"unsupported-gpu", no_argument, NULL, 'u'},
252 {0, 0, 0, 0}
253};
254
255static const char usage[] =
256 "Usage: sway [options] [command]\n"
257 "\n"
258 " -h, --help Show help message and quit.\n"
259 " -c, --config <config> Specify a config file.\n"
260 " -C, --validate Check the validity of the config file, then exit.\n"
261 " -d, --debug Enables full logging, including debug information.\n"
262 " -v, --version Show the version number and quit.\n"
263 " -V, --verbose Enables more verbose logging.\n"
264 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
265 "\n";
266
245int main(int argc, char **argv) { 267int main(int argc, char **argv) {
246 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 268 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 269
261 char *config_path = NULL; 270 char *config_path = NULL;
262 271
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; 272 int c;
276 while (1) { 273 while (1) {
277 int option_index = 0; 274 int option_index = 0;
@@ -289,23 +286,23 @@ int main(int argc, char **argv) {
289 config_path = strdup(optarg); 286 config_path = strdup(optarg);
290 break; 287 break;
291 case 'C': // validate 288 case 'C': // validate
292 validate = 1; 289 validate = true;
293 break; 290 break;
294 case 'd': // debug 291 case 'd': // debug
295 debug = 1; 292 debug = true;
296 break; 293 break;
297 case 'D': // extended debug options 294 case 'D': // extended debug options
298 enable_debug_flag(optarg); 295 enable_debug_flag(optarg);
299 break; 296 break;
300 case 'u': 297 case 'u':
301 allow_unsupported_gpu = 1; 298 allow_unsupported_gpu = true;
302 break; 299 break;
303 case 'v': // version 300 case 'v': // version
304 printf("sway version " SWAY_VERSION "\n"); 301 printf("sway version " SWAY_VERSION "\n");
305 exit(EXIT_SUCCESS); 302 exit(EXIT_SUCCESS);
306 break; 303 break;
307 case 'V': // verbose 304 case 'V': // verbose
308 verbose = 1; 305 verbose = true;
309 break; 306 break;
310 case 'p': ; // --get-socketpath 307 case 'p': ; // --get-socketpath
311 if (getenv("SWAYSOCK")) { 308 if (getenv("SWAYSOCK")) {
@@ -344,11 +341,10 @@ int main(int argc, char **argv) {
344 } 341 }
345 342
346 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); 343 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION);
344 sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR);
347 log_kernel(); 345 log_kernel();
348 log_distro(); 346 log_distro();
349 log_env(); 347 log_env();
350 detect_proprietary(allow_unsupported_gpu);
351 detect_raspi();
352 348
353 if (optind < argc) { // Behave as IPC client 349 if (optind < argc) { // Behave as IPC client
354 if (optind != 1) { 350 if (optind != 1) {
@@ -375,6 +371,8 @@ int main(int argc, char **argv) {
375 return 0; 371 return 0;
376 } 372 }
377 373
374 detect_proprietary(allow_unsupported_gpu);
375
378 if (!server_privileged_prepare(&server)) { 376 if (!server_privileged_prepare(&server)) {
379 return 1; 377 return 1;
380 } 378 }
@@ -384,6 +382,8 @@ int main(int argc, char **argv) {
384 exit(EXIT_FAILURE); 382 exit(EXIT_FAILURE);
385 } 383 }
386 384
385 increase_nofile_limit();
386
387 // handle SIGTERM signals 387 // handle SIGTERM signals
388 signal(SIGTERM, sig_handler); 388 signal(SIGTERM, sig_handler);
389 signal(SIGINT, sig_handler); 389 signal(SIGINT, sig_handler);
@@ -413,6 +413,8 @@ int main(int argc, char **argv) {
413 goto shutdown; 413 goto shutdown;
414 } 414 }
415 415
416 set_rr_scheduling();
417
416 if (!server_start(&server)) { 418 if (!server_start(&server)) {
417 sway_terminate(EXIT_FAILURE); 419 sway_terminate(EXIT_FAILURE);
418 goto shutdown; 420 goto shutdown;
diff --git a/sway/meson.build b/sway/meson.build
index 6e138101..6762ef2d 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',
@@ -187,6 +190,7 @@ sway_sources = files(
187 'commands/output/max_render_time.c', 190 'commands/output/max_render_time.c',
188 'commands/output/mode.c', 191 'commands/output/mode.c',
189 'commands/output/position.c', 192 'commands/output/position.c',
193 'commands/output/render_bit_depth.c',
190 'commands/output/scale.c', 194 'commands/output/scale.c',
191 'commands/output/scale_filter.c', 195 'commands/output/scale_filter.c',
192 'commands/output/subpixel.c', 196 'commands/output/subpixel.c',
@@ -204,18 +208,21 @@ sway_sources = files(
204 208
205sway_deps = [ 209sway_deps = [
206 cairo, 210 cairo,
211 drm,
207 jsonc, 212 jsonc,
208 libevdev, 213 libevdev,
209 libinput, 214 libinput,
215 libudev,
210 math, 216 math,
211 pango, 217 pango,
212 pcre, 218 pcre2,
213 glesv2, 219 glesv2,
214 pixman, 220 pixman,
215 server_protos, 221 server_protos,
216 wayland_server, 222 wayland_server,
217 wlroots, 223 wlroots,
218 xkbcommon, 224 xkbcommon,
225 xcb_icccm,
219] 226]
220 227
221if have_xwayland 228if 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..627d80d6 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -7,24 +7,32 @@
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>
23#include <wlr/types/wlr_server_decoration.h> 26#include <wlr/types/wlr_server_decoration.h>
27#include <wlr/types/wlr_subcompositor.h>
24#include <wlr/types/wlr_tablet_v2.h> 28#include <wlr/types/wlr_tablet_v2.h>
25#include <wlr/types/wlr_viewporter.h> 29#include <wlr/types/wlr_viewporter.h>
26#include <wlr/types/wlr_xcursor_manager.h> 30#include <wlr/types/wlr_xcursor_manager.h>
31#include <wlr/types/wlr_xdg_activation_v1.h>
27#include <wlr/types/wlr_xdg_decoration_v1.h> 32#include <wlr/types/wlr_xdg_decoration_v1.h>
33#include <wlr/types/wlr_xdg_foreign_registry.h>
34#include <wlr/types/wlr_xdg_foreign_v1.h>
35#include <wlr/types/wlr_xdg_foreign_v2.h>
28#include <wlr/types/wlr_xdg_output_v1.h> 36#include <wlr/types/wlr_xdg_output_v1.h>
29#include "config.h" 37#include "config.h"
30#include "list.h" 38#include "list.h"
@@ -52,19 +60,52 @@ bool server_privileged_prepare(struct sway_server *server) {
52 return true; 60 return true;
53} 61}
54 62
63static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
64 /* We only offer non-desktop outputs, but in the future we might want to do
65 * more logic here. */
66
67 struct wlr_drm_lease_request_v1 *req = data;
68 struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
69 if (!lease) {
70 sway_log(SWAY_ERROR, "Failed to grant lease request");
71 wlr_drm_lease_request_v1_reject(req);
72 }
73}
74
75#define SWAY_XDG_SHELL_VERSION 2
76
55bool server_init(struct sway_server *server) { 77bool server_init(struct sway_server *server) {
56 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 78 sway_log(SWAY_DEBUG, "Initializing Wayland server");
57 79
58 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); 80 server->renderer = wlr_renderer_autocreate(server->backend);
59 assert(renderer); 81 if (!server->renderer) {
82 sway_log(SWAY_ERROR, "Failed to create renderer");
83 return false;
84 }
60 85
61 wlr_renderer_init_wl_display(renderer, server->wl_display); 86 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
62 87
63 server->compositor = wlr_compositor_create(server->wl_display, renderer); 88 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) {
89 wlr_drm_create(server->wl_display, server->renderer);
90 server->linux_dmabuf_v1 =
91 wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer);
92 }
93
94 server->allocator = wlr_allocator_autocreate(server->backend,
95 server->renderer);
96 if (!server->allocator) {
97 sway_log(SWAY_ERROR, "Failed to create allocator");
98 return false;
99 }
100
101 server->compositor = wlr_compositor_create(server->wl_display,
102 server->renderer);
64 server->compositor_new_surface.notify = handle_compositor_new_surface; 103 server->compositor_new_surface.notify = handle_compositor_new_surface;
65 wl_signal_add(&server->compositor->events.new_surface, 104 wl_signal_add(&server->compositor->events.new_surface,
66 &server->compositor_new_surface); 105 &server->compositor_new_surface);
67 106
107 wlr_subcompositor_create(server->wl_display);
108
68 server->data_device_manager = 109 server->data_device_manager =
69 wlr_data_device_manager_create(server->wl_display); 110 wlr_data_device_manager_create(server->wl_display);
70 111
@@ -87,7 +128,8 @@ bool server_init(struct sway_server *server) {
87 &server->layer_shell_surface); 128 &server->layer_shell_surface);
88 server->layer_shell_surface.notify = handle_layer_shell_surface; 129 server->layer_shell_surface.notify = handle_layer_shell_surface;
89 130
90 server->xdg_shell = wlr_xdg_shell_create(server->wl_display); 131 server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
132 SWAY_XDG_SHELL_VERSION);
91 wl_signal_add(&server->xdg_shell->events.new_surface, 133 wl_signal_add(&server->xdg_shell->events.new_surface,
92 &server->xdg_shell_surface); 134 &server->xdg_shell_surface);
93 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 135 server->xdg_shell_surface.notify = handle_xdg_shell_surface;
@@ -144,16 +186,40 @@ bool server_init(struct sway_server *server) {
144 server->foreign_toplevel_manager = 186 server->foreign_toplevel_manager =
145 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 187 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
146 188
189 sway_session_lock_init();
190
191 server->drm_lease_manager=
192 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
193 if (server->drm_lease_manager) {
194 server->drm_lease_request.notify = handle_drm_lease_request;
195 wl_signal_add(&server->drm_lease_manager->events.request,
196 &server->drm_lease_request);
197 } else {
198 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
199 sway_log(SWAY_INFO, "VR will not be available");
200 }
201
147 wlr_export_dmabuf_manager_v1_create(server->wl_display); 202 wlr_export_dmabuf_manager_v1_create(server->wl_display);
148 wlr_screencopy_manager_v1_create(server->wl_display); 203 wlr_screencopy_manager_v1_create(server->wl_display);
149 wlr_data_control_manager_v1_create(server->wl_display); 204 wlr_data_control_manager_v1_create(server->wl_display);
150 wlr_primary_selection_v1_device_manager_create(server->wl_display); 205 wlr_primary_selection_v1_device_manager_create(server->wl_display);
151 wlr_viewporter_create(server->wl_display); 206 wlr_viewporter_create(server->wl_display);
152 207
208 struct wlr_xdg_foreign_registry *foreign_registry =
209 wlr_xdg_foreign_registry_create(server->wl_display);
210 wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);
211 wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry);
212
213 server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display);
214 server->xdg_activation_v1_request_activate.notify =
215 xdg_activation_v1_handle_request_activate;
216 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
217 &server->xdg_activation_v1_request_activate);
218
153 // Avoid using "wayland-0" as display socket 219 // Avoid using "wayland-0" as display socket
154 char name_candidate[16]; 220 char name_candidate[16];
155 for (int i = 1; i <= 32; ++i) { 221 for (unsigned int i = 1; i <= 32; ++i) {
156 sprintf(name_candidate, "wayland-%d", i); 222 snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
157 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { 223 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
158 server->socket = strdup(name_candidate); 224 server->socket = strdup(name_candidate);
159 break; 225 break;
@@ -166,27 +232,26 @@ bool server_init(struct sway_server *server) {
166 return false; 232 return false;
167 } 233 }
168 234
169 server->noop_backend = wlr_noop_backend_create(server->wl_display); 235 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) { 236 if (!server->headless_backend) {
177 sway_log(SWAY_INFO, "Failed to create secondary headless backend, " 237 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
178 "starting without it"); 238 wlr_backend_destroy(server->backend);
239 return false;
179 } else { 240 } else {
180 wlr_multi_backend_add(server->backend, server->headless_backend); 241 wlr_multi_backend_add(server->backend, server->headless_backend);
181 } 242 }
182 243
244 struct wlr_output *wlr_output =
245 wlr_headless_add_output(server->headless_backend, 800, 600);
246 wlr_output_set_name(wlr_output, "FALLBACK");
247 root->fallback_output = output_create(wlr_output);
248
183 // This may have been set already via -Dtxn-timeout 249 // This may have been set already via -Dtxn-timeout
184 if (!server->txn_timeout_ms) { 250 if (!server->txn_timeout_ms) {
185 server->txn_timeout_ms = 200; 251 server->txn_timeout_ms = 200;
186 } 252 }
187 253
188 server->dirty_nodes = create_list(); 254 server->dirty_nodes = create_list();
189 server->transactions = create_list();
190 255
191 server->input = input_manager_create(server); 256 server->input = input_manager_create(server);
192 input_manager_get_default_seat(); // create seat0 257 input_manager_get_default_seat(); // create seat0
@@ -202,7 +267,6 @@ void server_fini(struct sway_server *server) {
202 wl_display_destroy_clients(server->wl_display); 267 wl_display_destroy_clients(server->wl_display);
203 wl_display_destroy(server->wl_display); 268 wl_display_destroy(server->wl_display);
204 list_free(server->dirty_nodes); 269 list_free(server->dirty_nodes);
205 list_free(server->transactions);
206} 270}
207 271
208bool server_start(struct sway_server *server) { 272bool server_start(struct sway_server *server) {
@@ -238,6 +302,7 @@ bool server_start(struct sway_server *server) {
238 wlr_backend_destroy(server->backend); 302 wlr_backend_destroy(server->backend);
239 return false; 303 return false;
240 } 304 }
305
241 return true; 306 return true;
242} 307}
243 308
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..5cdcb99b 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -294,7 +294,7 @@ following properties:
294Retrieve a JSON representation of the tree 294Retrieve a JSON representation of the tree
295 295
296*REPLY*++ 296*REPLY*++
297An array of object the represent the current tree. Each object represents one 297An array of objects that represent the current tree. Each object represents one
298node and will have the following properties: 298node and will have the following properties:
299 299
300[- *PROPERTY* 300[- *PROPERTY*
@@ -370,7 +370,7 @@ node and will have the following properties:
370 that can be used as an aid in submitting reproduction steps for bug reports 370 that can be used as an aid in submitting reproduction steps for bug reports
371|- fullscreen_mode 371|- fullscreen_mode
372: integer 372: integer
373: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means 373: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
374 full workspace, and 2 means global fullscreen 374 full workspace, and 2 means global fullscreen
375|- app_id 375|- app_id
376: string 376: string
@@ -1131,6 +1131,9 @@ following properties:
1131|- xkb_active_layout_index 1131|- xkb_active_layout_index
1132: integer 1132: integer
1133: (Only keyboards) The index of the active keyboard layout in use 1133: (Only keyboards) The index of the active keyboard layout in use
1134|- scroll_factor
1135: floating
1136: (Only pointers) Multiplier applied on scroll event values.
1134|- libinput 1137|- libinput
1135: object 1138: object
1136: (Only libinput devices) An object describing the current device settings. 1139: (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..4159a851 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -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
@@ -112,7 +122,7 @@ must be separated by one space. For example:
112*output* <name> toggle 122*output* <name> toggle
113 Toggle the specified output. 123 Toggle the specified output.
114 124
115*output* <name> dpms on|off 125*output* <name> dpms on|off|toggle
116 Enables or disables the specified output via DPMS. To turn an output off 126 Enables or disables the specified output via DPMS. To turn an output off
117 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. 127 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
118 128
@@ -142,11 +152,26 @@ must be separated by one space. For example:
142 Enables or disables adaptive synchronization (often referred to as Variable 152 Enables or disables adaptive synchronization (often referred to as Variable
143 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). 153 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync).
144 154
145 Adaptive sync allows clients to submit frames a little to late without 155 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 156 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 157 adaptive sync can improve latency, but can cause flickering on some
148 hardware. 158 hardware.
149 159
160*output* <name> render_bit_depth 8|10
161 Controls the color channel bit depth at which frames are rendered; the
162 default is currently 8 bits per channel.
163
164 Setting higher values will not have an effect if hardware and software lack
165 support for such bit depths. Successfully increasing the render bit depth
166 will not necessarily increase the bit depth of the frames sent to a display.
167 An increased render bit depth may provide smoother rendering of gradients,
168 and screenshots which can more precisely store the colors of programs
169 which display high bit depth colors.
170
171 Warnings: this can break screenshot/screencast programs which have not been
172 updated to work with different bit depths. This command is experimental,
173 and may be removed or changed in the future.
174
150# SEE ALSO 175# SEE ALSO
151 176
152*sway*(5) *sway-input*(5) 177*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 02592b5f..2780370f 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
@@ -215,7 +221,7 @@ set|plus|minus <amount>
215 If unspecified, the default is 10 pixels. Pixels are ignored when moving 221 If unspecified, the default is 10 pixels. Pixels are ignored when moving
216 tiled containers. 222 tiled containers.
217 223
218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] 224*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
219 Moves the focused container to the specified position in the workspace. 225 Moves the focused container to the specified position in the workspace.
220 The position can be specified in pixels or percentage points, omitting 226 The position can be specified in pixels or percentage points, omitting
221 the unit defaults to pixels. If _absolute_ is used, the position is 227 the unit defaults to pixels. If _absolute_ is used, the position is
@@ -319,8 +325,10 @@ set|plus|minus <amount>
319 established by the *seat* subcommand of the same name. See 325 established by the *seat* subcommand of the same name. See
320 *sway-input*(5) for more ways to affect inhibitors. 326 *sway-input*(5) for more ways to affect inhibitors.
321 327
322*split* vertical|v|horizontal|h|toggle|t 328*split* vertical|v|horizontal|h|none|n|toggle|t
323 Splits the current container, vertically or horizontally. When _toggle_ is 329 Splits the current container, vertically or horizontally. When _none_ is
330 specified, the effect of a previous split is undone if the current
331 container is the only child of a split parent. When _toggle_ is
324 specified, the current container is split opposite to the parent 332 specified, the current container is split opposite to the parent
325 container's layout. 333 container's layout.
326 334
@@ -404,7 +412,7 @@ runtime.
404 a keyboard shortcuts inhibitor is active for the currently focused 412 a keyboard shortcuts inhibitor is active for the currently focused
405 window. Such inhibitors are usually requested by remote desktop and 413 window. Such inhibitors are usually requested by remote desktop and
406 virtualization software to enable the user to send keyboard shortcuts 414 virtualization software to enable the user to send keyboard shortcuts
407 to the remote or virtual session. The _--inhibited_ flag allows to 415 to the remote or virtual session. The _--inhibited_ flag allows one to
408 define bindings which will be exempt from pass-through to such 416 define bindings which will be exempt from pass-through to such
409 software. The same preference logic as for _--locked_ applies. 417 software. The same preference logic as for _--locked_ applies.
410 418
@@ -497,6 +505,12 @@ runtime.
497 *client.focused_inactive* 505 *client.focused_inactive*
498 The most recently focused view within a container which is not focused. 506 The most recently focused view within a container which is not focused.
499 507
508 *client.focused_tab_title*
509 A view that has focused descendant container.
510 Tab or stack container title that is the parent of the focused container
511 but is not directly focused. Defaults to focused_inactive if not
512 specified and does not use the indicator and child_border colors.
513
500 *client.placeholder* 514 *client.placeholder*
501 Ignored (present for i3 compatibility). 515 Ignored (present for i3 compatibility).
502 516
@@ -552,6 +566,12 @@ The default colors are:
552: #ffffff 566: #ffffff
553: #484e50 567: #484e50
554: #5f676a 568: #5f676a
569| *focused_tab_title*
570: #333333
571: #5f676a
572: #ffffff
573: n/a
574: n/a
555| *unfocused* 575| *unfocused*
556: #333333 576: #333333
557: #222222 577: #222222
@@ -606,11 +626,11 @@ The default colors are:
606 after switching between workspaces. 626 after switching between workspaces.
607 627
608*focus_on_window_activation* smart|urgent|focus|none 628*focus_on_window_activation* smart|urgent|focus|none
609 This option determines what to do when an xwayland client requests 629 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 630 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. 631 _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 632 become focused only if it is already visible, otherwise the urgent state
613 visible, otherwise the urgent state will be set. Default is _urgent_. 633 will be set. Default is _urgent_.
614 634
615*focus_wrapping* yes|no|force|workspace 635*focus_wrapping* yes|no|force|workspace
616 This option determines what to do when attempting to focus over the edge 636 This option determines what to do when attempting to focus over the edge
@@ -630,14 +650,14 @@ The default colors are:
630 should be used instead. Regardless of whether pango markup is enabled, 650 should be used instead. Regardless of whether pango markup is enabled,
631 _font_ should be specified as a pango font description. For more 651 _font_ should be specified as a pango font description. For more
632 information on pango font descriptions, see 652 information on pango font descriptions, see
633 https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 653 https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
634 654
635*force_display_urgency_hint* <timeout> [ms] 655*force_display_urgency_hint* <timeout> [ms]
636 If an application on another workspace sets an urgency hint, switching to this 656 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 657 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 658 window decoration color would be immediately reset to *client.focused*. This
639 may make it unnecessarily hard to tell which window originally raised the 659 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. 660 event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset.
641 661
642*titlebar_border_thickness* <thickness> 662*titlebar_border_thickness* <thickness>
643 Thickness of the titlebar border in pixels 663 Thickness of the titlebar border in pixels
@@ -690,9 +710,10 @@ The default colors are:
690 borders will only be enabled if the workspace has more than one visible 710 borders will only be enabled if the workspace has more than one visible
691 child and gaps equal to zero. 711 child and gaps equal to zero.
692 712
693*smart_gaps* on|off 713*smart_gaps* on|off|toggle|inverse_outer
694 If smart_gaps are _on_ gaps will only be enabled if a workspace has more 714 If smart_gaps are _on_ gaps will only be enabled if a workspace has more
695 than one child. 715 than one child. If smart_gaps are _inverse_outer_ outer gaps will only
716 be enabled if a workspace has exactly one child.
696 717
697*mark* --add|--replace [--toggle] <identifier> 718*mark* --add|--replace [--toggle] <identifier>
698 Marks are arbitrary labels that can be used to identify certain windows and 719 Marks are arbitrary labels that can be used to identify certain windows and
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..09766ce5 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, &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, 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/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..2cb235bf 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -547,9 +547,23 @@ bool handle_ipc_readable(struct swaybar *bar) {
547 return false; 547 return false;
548 } 548 }
549 549
550 json_object *result = json_tokener_parse(resp->payload); 550 // The default depth of 32 is too small to represent some nested layouts, but
551 if (!result) { 551 // 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"); 552 // all the memory for its stack.
553 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
554 if (!tok) {
555 sway_log_errno(SWAY_ERROR, "failed to create tokener");
556 free_ipc_response(resp);
557 return false;
558 }
559
560 json_object *result = json_tokener_parse_ex(tok, resp->payload, -1);
561 enum json_tokener_error err = json_tokener_get_error(tok);
562 json_tokener_free(tok);
563
564 if (err != json_tokener_success) {
565 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
566 json_tokener_error_desc(err));
553 free_ipc_response(resp); 567 free_ipc_response(resp);
554 return false; 568 return false;
555 } 569 }
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..7e2f97b7 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 char *font = output->bar->config->font;
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, &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, 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, &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, &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,20 +209,20 @@ 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;
@@ -201,9 +230,9 @@ static uint32_t render_status_block(cairo_t *cairo,
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, &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, 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, 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, &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, &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,18 +373,18 @@ 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;
@@ -355,9 +392,9 @@ static void predict_status_block_pos(cairo_t *cairo,
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, &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, &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}
@@ -438,38 +475,39 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
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, &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, &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, 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, &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, 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, 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..02bb12c6 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,74 @@ 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 }
334}
307 335
308 if (type == IPC_GET_CONFIG) { 336static void pretty_print(int type, json_object *resp) {
337 switch (type) {
338 case IPC_SEND_TICK:
339 return;
340 case IPC_GET_VERSION:
341 pretty_print_version(resp);
342 return;
343 case IPC_GET_CONFIG:
309 pretty_print_config(resp); 344 pretty_print_config(resp);
310 return; 345 return;
346 case IPC_GET_TREE:
347 pretty_print_tree(resp, 0);
348 return;
349 case IPC_COMMAND:
350 case IPC_GET_WORKSPACES:
351 case IPC_GET_INPUTS:
352 case IPC_GET_OUTPUTS:
353 case IPC_GET_SEATS:
354 break;
355 default:
356 printf("%s\n", json_object_to_json_string_ext(resp,
357 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
358 return;
311 } 359 }
312 360
313 json_object *obj; 361 json_object *obj;
@@ -343,7 +391,7 @@ int main(int argc, char **argv) {
343 391
344 sway_log_init(SWAY_INFO, NULL); 392 sway_log_init(SWAY_INFO, NULL);
345 393
346 static struct option long_options[] = { 394 static const struct option long_options[] = {
347 {"help", no_argument, NULL, 'h'}, 395 {"help", no_argument, NULL, 'h'},
348 {"monitor", no_argument, NULL, 'm'}, 396 {"monitor", no_argument, NULL, 'm'},
349 {"pretty", no_argument, NULL, 'p'}, 397 {"pretty", no_argument, NULL, 'p'},
@@ -480,12 +528,20 @@ int main(int argc, char **argv) {
480 char *resp = ipc_single_command(socketfd, type, command, &len); 528 char *resp = ipc_single_command(socketfd, type, command, &len);
481 529
482 // pretty print the json 530 // pretty print the json
483 json_object *obj = json_tokener_parse(resp); 531 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
484 if (obj == NULL) { 532 if (tok == NULL) {
533 if (quiet) {
534 exit(EXIT_FAILURE);
535 }
536 sway_abort("failed allocating json_tokener");
537 }
538 json_object *obj = json_tokener_parse_ex(tok, resp, -1);
539 enum json_tokener_error err = json_tokener_get_error(tok);
540 json_tokener_free(tok);
541 if (obj == NULL || err != json_tokener_success) {
485 if (!quiet) { 542 if (!quiet) {
486 fprintf(stderr, "ERROR: Could not parse json response from ipc. " 543 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
487 "This is a bug in sway."); 544 json_tokener_error_desc(err));
488 printf("%s\n", resp);
489 } 545 }
490 ret = 1; 546 ret = 1;
491 } else { 547 } else {
@@ -517,13 +573,22 @@ int main(int argc, char **argv) {
517 break; 573 break;
518 } 574 }
519 575
520 json_object *obj = json_tokener_parse(reply->payload); 576 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
521 if (obj == NULL) { 577 if (tok == NULL) {
578 if (quiet) {
579 exit(EXIT_FAILURE);
580 }
581 sway_abort("failed allocating json_tokener");
582 }
583 json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1);
584 enum json_tokener_error err = json_tokener_get_error(tok);
585 json_tokener_free(tok);
586 if (obj == NULL || err != json_tokener_success) {
522 if (!quiet) { 587 if (!quiet) {
523 fprintf(stderr, "ERROR: Could not parse json response from" 588 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
524 " ipc. This is a bug in sway."); 589 json_tokener_error_desc(err));
525 ret = 1;
526 } 590 }
591 ret = 1;
527 break; 592 break;
528 } else if (quiet) { 593 } else if (quiet) {
529 json_object_put(obj); 594 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..9aeec3c2 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,6 +206,24 @@ 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);
@@ -193,15 +233,18 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
193 case 'l': // Detailed Message 233 case 'l': // Detailed Message
194 if (swaynag) { 234 if (swaynag) {
195 free(swaynag->details.message); 235 free(swaynag->details.message);
196 swaynag->details.message = read_from_stdin(); 236 swaynag->details.message = read_and_trim_stdin();
237 if (!swaynag->details.message) {
238 return EXIT_FAILURE;
239 }
197 swaynag->details.button_up.text = strdup("â–²"); 240 swaynag->details.button_up.text = strdup("â–²");
198 swaynag->details.button_down.text = strdup("â–¼"); 241 swaynag->details.button_down.text = strdup("â–¼");
199 } 242 }
200 break; 243 break;
201 case 'L': // Detailed Button Text 244 case 'L': // Detailed Button Text
202 if (swaynag) { 245 if (swaynag) {
203 free(swaynag->details.button_details->text); 246 free(swaynag->details.button_details.text);
204 swaynag->details.button_details->text = strdup(optarg); 247 swaynag->details.button_details.text = strdup(optarg);
205 } 248 }
206 break; 249 break;
207 case 'm': // Message 250 case 'm': // Message
@@ -218,8 +261,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
218 break; 261 break;
219 case 's': // Dismiss Button Text 262 case 's': // Dismiss Button Text
220 if (swaynag) { 263 if (swaynag) {
221 struct swaynag_button *button_close; 264 struct swaynag_button *button_close = swaynag->buttons->items[0];
222 button_close = swaynag->buttons->items[0];
223 free(button_close->text); 265 free(button_close->text);
224 button_close->text = strdup(optarg); 266 button_close->text = strdup(optarg);
225 } 267 }
@@ -378,23 +420,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {
378 420
379 if (line[0] == '[') { 421 if (line[0] == '[') {
380 char *close = strchr(line, ']'); 422 char *close = strchr(line, ']');
381 if (!close) { 423 if (!close || close != &line[nread - 2] || nread <= 3) {
382 fprintf(stderr, "Closing bracket not found on line %d\n", 424 fprintf(stderr, "Line %d is malformed\n", line_number);
383 line_number);
384 result = 1; 425 result = 1;
385 break; 426 break;
386 } 427 }
387 char *name = calloc(1, close - line); 428 *close = '\0';
388 strncat(name, line + 1, close - line - 1); 429 type = swaynag_type_get(types, &line[1]);
389 type = swaynag_type_get(types, name);
390 if (!type) { 430 if (!type) {
391 type = swaynag_type_new(name); 431 type = swaynag_type_new(&line[1]);
392 list_add(types, type); 432 list_add(types, type);
393 } 433 }
394 free(name);
395 } else { 434 } else {
396 char *flag = malloc(sizeof(char) * (nread + 3)); 435 char *flag = malloc(nread + 3);
397 sprintf(flag, "--%s", line); 436 if (!flag) {
437 perror("calloc");
438 return EXIT_FAILURE;
439 }
440 snprintf(flag, nread + 3, "--%s", line);
398 char *argv[] = {"swaynag", flag}; 441 char *argv[] = {"swaynag", flag};
399 result = swaynag_parse_options(2, argv, swaynag, types, type, 442 result = swaynag_parse_options(2, argv, swaynag, types, type,
400 NULL, NULL); 443 NULL, NULL);
diff --git a/swaynag/main.c b/swaynag/main.c
index 88007818..56e4950b 100644
--- a/swaynag/main.c
+++ b/swaynag/main.c
@@ -20,33 +20,20 @@ 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 =
34 calloc(sizeof(struct swaynag_button), 1);
35 button_close->text = strdup("X");
36 button_close->type = SWAYNAG_ACTION_DISMISS;
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
44 char *config_path = NULL; 32 char *config_path = NULL;
45 bool debug = false; 33 bool debug = false;
46 int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, 34 status = swaynag_parse_options(argc, argv, NULL, NULL, NULL,
47 &config_path, &debug); 35 &config_path, &debug);
48 if (launch_status != 0) { 36 if (status != 0) {
49 exit_code = launch_status;
50 goto cleanup; 37 goto cleanup;
51 } 38 }
52 sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); 39 sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL);
@@ -56,29 +43,29 @@ int main(int argc, char **argv) {
56 } 43 }
57 if (config_path) { 44 if (config_path) {
58 sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); 45 sway_log(SWAY_DEBUG, "Loading config file: %s", config_path);
59 int config_status = swaynag_load_config(config_path, &swaynag, types); 46 status = swaynag_load_config(config_path, &swaynag, types);
60 free(config_path); 47 if (status != 0) {
61 if (config_status != 0) {
62 exit_code = config_status;
63 goto cleanup; 48 goto cleanup;
64 } 49 }
65 } 50 }
66 51
52 swaynag.details.button_details.text = strdup("Toggle details");
53 swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND;
54
67 if (argc > 1) { 55 if (argc > 1) {
68 struct swaynag_type *type_args = swaynag_type_new("<args>"); 56 struct swaynag_type *type_args = swaynag_type_new("<args>");
69 list_add(types, type_args); 57 list_add(types, type_args);
70 58
71 int result = swaynag_parse_options(argc, argv, &swaynag, types, 59 status = swaynag_parse_options(argc, argv, &swaynag, types,
72 type_args, NULL, NULL); 60 type_args, NULL, NULL);
73 if (result != 0) { 61 if (status != 0) {
74 exit_code = result;
75 goto cleanup; 62 goto cleanup;
76 } 63 }
77 } 64 }
78 65
79 if (!swaynag.message) { 66 if (!swaynag.message) {
80 sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); 67 sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m");
81 exit_code = EXIT_FAILURE; 68 status = EXIT_FAILURE;
82 goto cleanup; 69 goto cleanup;
83 } 70 }
84 71
@@ -98,11 +85,13 @@ int main(int argc, char **argv) {
98 85
99 swaynag_types_free(types); 86 swaynag_types_free(types);
100 87
88 struct swaynag_button button_close = { 0 };
89 button_close.text = strdup("X");
90 button_close.type = SWAYNAG_ACTION_DISMISS;
91 list_add(swaynag.buttons, &button_close);
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..d72f42c2 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"
@@ -10,19 +10,19 @@
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, &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, 1, false,
26 "%s", swaynag->message); 26 "%s", swaynag->message);
27 27
28 return ideal_surface_height; 28 return ideal_surface_height;
@@ -32,10 +32,10 @@ static void render_details_scroll_button(cairo_t *cairo,
32 struct swaynag *swaynag, struct swaynag_button *button) { 32 struct swaynag *swaynag, struct swaynag_button *button) {
33 int text_width, text_height; 33 int text_width, text_height;
34 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 34 get_text_size(cairo, swaynag->type->font, &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,7 +50,7 @@ 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, 1, true,
54 "%s", button->text); 54 "%s", button->text);
55} 55}
56 56
@@ -58,33 +58,33 @@ static int get_detailed_scroll_button_width(cairo_t *cairo,
58 struct swaynag *swaynag) { 58 struct swaynag *swaynag) {
59 int up_width, down_width, temp_height; 59 int up_width, down_width, temp_height;
60 get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, 60 get_text_size(cairo, swaynag->type->font, &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, &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,
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,
@@ -173,13 +173,13 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
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, &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, 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;