aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/alpine.yml13
-rw-r--r--.builds/archlinux.yml4
-rw-r--r--.builds/freebsd.yml3
-rw-r--r--.clang-format6
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md5
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml4
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--README.de.md4
-rw-r--r--README.dk.md65
-rw-r--r--README.es.md6
-rw-r--r--README.fr.md64
-rw-r--r--README.gr.md73
-rw-r--r--README.hu.md77
-rw-r--r--README.ir.md70
-rw-r--r--README.ja.md4
-rw-r--r--README.ko.md4
-rw-r--r--README.md18
-rw-r--r--README.nl.md6
-rw-r--r--README.pl.md6
-rw-r--r--README.pt.md6
-rw-r--r--README.ro.md4
-rw-r--r--README.ru.md41
-rw-r--r--README.tr.md68
-rw-r--r--README.uk.md6
-rw-r--r--README.zh-CN.md6
-rw-r--r--README.zh-TW.md6
-rw-r--r--client/pool-buffer.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.c6
-rw-r--r--config.in4
-rwxr-xr-xcontrib/_incr_version21
-rwxr-xr-xcontrib/autoname-workspaces.py28
-rwxr-xr-xcontrib/grimshot8
-rw-r--r--contrib/grimshot.13
-rw-r--r--contrib/grimshot.1.scd1
-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/pango.h5
-rw-r--r--include/pool-buffer.h2
-rw-r--r--include/sway/commands.h7
-rw-r--r--include/sway/config.h27
-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.h2
-rw-r--r--include/sway/input/libinput.h2
-rw-r--r--include/sway/input/seat.h5
-rw-r--r--include/sway/input/text_input.h3
-rw-r--r--include/sway/layers.h2
-rw-r--r--include/sway/output.h5
-rw-r--r--include/sway/server.h28
-rw-r--r--include/sway/tree/container.h60
-rw-r--r--include/sway/tree/view.h16
-rw-r--r--include/sway/tree/workspace.h12
-rw-r--r--include/swaybar/i3bar.h1
-rw-r--r--include/swaynag/types.h1
-rw-r--r--include/util.h6
-rw-r--r--meson.build129
-rw-r--r--meson_options.txt2
-rw-r--r--protocols/meson.build4
-rw-r--r--sway/commands.c59
-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/bind.c7
-rw-r--r--sway/commands/border.c8
-rw-r--r--sway/commands/exec_always.c21
-rw-r--r--sway/commands/floating.c8
-rw-r--r--sway/commands/focus.c33
-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/layout.c12
-rw-r--r--sway/commands/mode.c2
-rw-r--r--sway/commands/move.c148
-rw-r--r--sway/commands/output.c3
-rw-r--r--sway/commands/output/dpms.c25
-rw-r--r--sway/commands/output/mode.c58
-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/split.c23
-rw-r--r--sway/commands/sticky.c6
-rw-r--r--sway/commands/swap.c56
-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.c103
-rw-r--r--sway/config/bar.c3
-rw-r--r--sway/config/output.c47
-rw-r--r--sway/criteria.c4
-rw-r--r--sway/desktop/idle_inhibit_v1.c8
-rw-r--r--sway/desktop/layer_shell.c78
-rw-r--r--sway/desktop/output.c175
-rw-r--r--sway/desktop/render.c140
-rw-r--r--sway/desktop/transaction.c253
-rw-r--r--sway/desktop/xdg_shell.c59
-rw-r--r--sway/desktop/xwayland.c64
-rw-r--r--sway/input/cursor.c63
-rw-r--r--sway/input/keyboard.c77
-rw-r--r--sway/input/libinput.c23
-rw-r--r--sway/input/seat.c89
-rw-r--r--sway/input/seatop_default.c46
-rw-r--r--sway/input/seatop_down.c44
-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.c36
-rw-r--r--sway/input/seatop_resize_tiling.c28
-rw-r--r--sway/input/switch.c4
-rw-r--r--sway/input/text_input.c53
-rw-r--r--sway/ipc-json.c141
-rw-r--r--sway/main.c44
-rw-r--r--sway/meson.build3
-rw-r--r--sway/server.c42
-rw-r--r--sway/sway-bar.5.scd2
-rw-r--r--sway/sway-input.5.scd3
-rw-r--r--sway/sway-ipc.7.scd5
-rw-r--r--sway/sway-output.5.scd14
-rw-r--r--sway/sway.5.scd12
-rw-r--r--sway/swaynag.c4
-rw-r--r--sway/tree/arrange.c70
-rw-r--r--sway/tree/container.c622
-rw-r--r--sway/tree/node.c18
-rw-r--r--sway/tree/output.c14
-rw-r--r--sway/tree/root.c33
-rw-r--r--sway/tree/view.c363
-rw-r--r--sway/tree/workspace.c77
-rw-r--r--sway/xdg_activation_v1.c20
-rw-r--r--sway/xdg_decoration.c28
-rw-r--r--swaybar/bar.c21
-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.c353
-rw-r--r--swaybar/tray/item.c11
-rw-r--r--swaymsg/main.c2
-rw-r--r--swaynag/config.c25
-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.c29
-rw-r--r--swaynag/types.c6
149 files changed, 3281 insertions, 2125 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index dc5e7c11..7f0bef02 100644
--- a/.builds/alpine.yml
+++ b/.builds/alpine.yml
@@ -6,6 +6,7 @@ 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
@@ -16,7 +17,8 @@ packages:
16 - wayland-dev 17 - wayland-dev
17 - wayland-protocols 18 - wayland-protocols
18 - xcb-util-image-dev 19 - xcb-util-image-dev
19 - xorg-server-xwayland 20 - xcb-util-wm-dev
21 - xwayland
20sources: 22sources:
21 - https://github.com/swaywm/sway 23 - https://github.com/swaywm/sway
22 - https://github.com/swaywm/wlroots 24 - https://github.com/swaywm/wlroots
@@ -28,7 +30,7 @@ tasks:
28 sudo ninja -C build install 30 sudo ninja -C build install
29 - setup: | 31 - setup: |
30 cd sway 32 cd sway
31 meson build -Dauto_features=enabled -Dtray=disabled 33 meson build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled
32 - build: | 34 - build: |
33 cd sway 35 cd sway
34 ninja -C build 36 ninja -C build
@@ -36,3 +38,10 @@ tasks:
36 cd sway 38 cd sway
37 meson configure build -Dxwayland=disabled 39 meson configure build -Dxwayland=disabled
38 ninja -C build 40 ninja -C build
41 - build-static: |
42 cd sway
43 mkdir subprojects
44 ln -s ../../wlroots subprojects/wlroots
45 rm -rf build
46 meson build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots
47 ninja -C build
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index c0f70186..a8f1dfed 100644
--- a/.builds/archlinux.yml
+++ b/.builds/archlinux.yml
@@ -13,7 +13,9 @@ packages:
13 - wayland 13 - wayland
14 - wayland-protocols 14 - wayland-protocols
15 - xcb-util-image 15 - xcb-util-image
16 - xcb-util-wm
16 - xorg-xwayland 17 - xorg-xwayland
18 - seatd
17sources: 19sources:
18 - https://github.com/swaywm/sway 20 - https://github.com/swaywm/sway
19 - https://github.com/swaywm/wlroots 21 - https://github.com/swaywm/wlroots
@@ -25,7 +27,7 @@ tasks:
25 sudo ninja -C build install 27 sudo ninja -C build install
26 - setup: | 28 - setup: |
27 cd sway 29 cd sway
28 meson build -Dauto_features=enabled -Dsd-bus-provider=libsystemd 30 meson build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd
29 - build: | 31 - build: |
30 cd sway 32 cd sway
31 ninja -C build 33 ninja -C build
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index 273badbc..1a3c8512 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -19,6 +19,7 @@ packages:
19- devel/libudev-devd 19- devel/libudev-devd
20- graphics/libdrm 20- graphics/libdrm
21- graphics/mesa-libs 21- graphics/mesa-libs
22- sysutils/seatd
22- x11/libinput 23- x11/libinput
23- x11/libX11 24- x11/libX11
24- x11/pixman 25- x11/pixman
@@ -33,7 +34,7 @@ tasks:
33 cd subprojects 34 cd subprojects
34 ln -s ../../wlroots wlroots 35 ln -s ../../wlroots wlroots
35 cd .. 36 cd ..
36 meson build -Dtray=enabled -Dsd-bus-provider=basu 37 meson build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu
37- build: | 38- build: |
38 cd sway 39 cd sway
39 ninja -C build 40 ninja -C build
diff --git a/.clang-format b/.clang-format
index 5818da3c..24ea869d 100644
--- a/.clang-format
+++ b/.clang-format
@@ -8,9 +8,11 @@ IndentCaseLabels: false
8SortIncludes: false 8SortIncludes: false
9ColumnLimit: 80 9ColumnLimit: 80
10AlignAfterOpenBracket: DontAlign 10AlignAfterOpenBracket: DontAlign
11BinPackParameters: false 11BinPackParameters: true
12BinPackArguments: false 12BinPackArguments: true
13ContinuationIndentWidth: 8 13ContinuationIndentWidth: 8
14AllowAllParametersOfDeclarationOnNextLine: false 14AllowAllParametersOfDeclarationOnNextLine: false
15AllowShortLoopsOnASingleLine: true 15AllowShortLoopsOnASingleLine: true
16ReflowComments: false 16ReflowComments: false
17AllowAllArgumentsOnNextLine: false
18AlignOperands: DontAlign
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 156accde..8542b7b9 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:
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..6e1e8ca9 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
diff --git a/README.dk.md b/README.dk.md
index 535000c3..94c0b9eb 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* pcre
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://github.com/swaywm/wlroots
74[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.es.md b/README.es.md
index 53f11f68..951a2eba 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
diff --git a/README.fr.md b/README.fr.md
index a72696d6..d1f4f934 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* pcre
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://github.com/swaywm/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..5bb04932
--- /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* pcre
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://github.com/swaywm/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..75999071
--- /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* pcre
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://github.com/swaywm/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..890a0fd2
--- /dev/null
+++ b/README.ir.md
@@ -0,0 +1,70 @@
1<div dir="rtl">
2
3# sway
4
5sway یک کامپوزیتور الهام گرÙته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال
6IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (#sway sur
7irc.libera.chat).
8
9برای حمایت از تیم توسعه sway به [صÙحه
10Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید.
11
12## امضای نسخه‌ها
13
14امضای نسخه‌ها با [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) در [GitHub](https://github.com/swaywm/sway/releases) منتشر می‌شود.
15
16## شیوه نصب
17
18### از بسته‌های رسمی
19
20sway در بسته‌های رسمی توزیع‌های مختل٠وجود دارد. بسته «sway» را نصب کنید. در صورتی Ú©Ù‡ بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صÙحه راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید.
21
22اگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استÙاده کنید یا به sir@cmpwn.com ایمیل بزنید.
23
24### کامپایل کردن کد
25
26چنانچه می‌خواهید آخرین نسخه کد sway Ùˆ wlroots را برای آزمایش یا توسعه بسازید به این [صÙحه راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید.
27
28بسته‌های مورد نیاز:
29
30* meson \*
31* [wlroots](https://github.com/swaywm/wlroots)
32* wayland
33* wayland-protocols \*
34* pcre
35* json-c
36* pango
37* cairo
38* gdk-pixbuf2 (انتخابی: برای system tray)
39* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صÙحه‌های راهنما) \*
40* git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \*
41
42_\*نیازمندی‌های زمان کامپایل برنامه_
43
44این Ùرمان‌ها را اجرا کنید:
45</div>
46
47 meson build
48 ninja -C build
49 sudo ninja -C build install
50
51<div dir="rtl">
52
53روی سیستم‌های بدون logindØŒ باید Ùرمان زیر را برای suid کردن باینری sway اجرا کنید:
54</div>
55
56 sudo chmod a+s /usr/local/bin/sway
57
58<div dir="rtl">
59sway پس از startup مجوزهای دسترسی root را رها می‌کند.
60
61### شخصی سازی و تنظیمات
62
63اگر در حال حاضر از i3 استÙاده می‌کنید، تنظیمات i3 خودتان را در Ùایل ‪`~/.config/sway/config`‬ Ú©Ù¾ÛŒ کنید Ùˆ بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، Ùایل نمونه تنظیمات را استÙاده کنید. این Ùایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید.
64
65## اجرا
66
67در محیط TTY کاÙیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طر٠sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد).
68
69</div>
70
diff --git a/README.ja.md b/README.ja.md
index fa28f3da..7af54fb2 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
diff --git a/README.ko.md b/README.ko.md
index 9c3dd323..76ce2f4d 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## 설치
diff --git a/README.md b/README.md
index 4698afbe..7307435f 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]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [中文-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [中文-ç¹é«”][zh-TW] - [Português][pt] - [Dansk][dk] - [한국어][ko] - [Română][ro] - [Magyar][hu] - [Türkçe][tr] - [Ùارسی][ir] - [Ελληνικά][gr]
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
@@ -42,11 +42,11 @@ _\*Compile-time dep_
42 42
43Run these commands: 43Run these commands:
44 44
45 meson build 45 meson build/
46 ninja -C build 46 ninja -C build/
47 sudo ninja -C build install 47 sudo ninja -C build/ install
48 48
49On systems without logind, you need to suid the sway binary: 49On systems without logind nor seatd, you need to suid the sway binary:
50 50
51 sudo chmod a+s /usr/local/bin/sway 51 sudo chmod a+s /usr/local/bin/sway
52 52
@@ -79,10 +79,14 @@ sway (gdm is known to work fairly well).
79[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md 79[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md
80[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md 80[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md
81[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md 81[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md
82[hu]: https://github.com/swaywm/sway/blob/master/README.hu.md
83[tr]: https://github.com/swaywm/sway/blob/master/README.tr.md
84[ir]: https://github.com/swaywm/sway/blob/master/README.ir.md
85[gr]: https://github.com/swaywm/sway/blob/master/README.gr.md
82[i3]: https://i3wm.org/ 86[i3]: https://i3wm.org/
83[Wayland]: http://wayland.freedesktop.org/ 87[Wayland]: http://wayland.freedesktop.org/
84[FAQ]: https://github.com/swaywm/sway/wiki 88[FAQ]: https://github.com/swaywm/sway/wiki
85[IRC channel]: http://webchat.freenode.net/?channels=sway&uio=d4 89[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
86[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 90[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
87[GitHub releases]: https://github.com/swaywm/sway/releases 91[GitHub releases]: https://github.com/swaywm/sway/releases
88[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 92[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
diff --git a/README.nl.md b/README.nl.md
index 86ebe398..3351db39 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
diff --git a/README.pl.md b/README.pl.md
index b63b8567..da987b7c 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
diff --git a/README.pt.md b/README.pt.md
index ad7cab65..7d449ef3 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
diff --git a/README.ro.md b/README.ro.md
index dd895b56..79524b79 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
diff --git a/README.ru.md b/README.ru.md
index a870ec89..70396905 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* pcre
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://github.com/swaywm/wlroots
74[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.tr.md b/README.tr.md
new file mode 100644
index 00000000..c0f72d72
--- /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* pcre
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://github.com/swaywm/wlroots
68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.uk.md b/README.uk.md
index 95047cb8..3d7402de 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## Ð’ÑтановленнÑ
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 9a3337ce..ecb46789 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.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## 安装
diff --git a/README.zh-TW.md b/README.zh-TW.md
index 13a9d2f4..4fd656da 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## 安è£
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..199f3ee1 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}
diff --git a/config.in b/config.in
index 08703bef..e0fdab3f 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.
@@ -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..4ce31f29 100755
--- a/contrib/grimshot
+++ b/contrib/grimshot
@@ -32,13 +32,13 @@ FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
32 32
33if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then 33if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
34 echo "Usage:" 34 echo "Usage:"
35 echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE]" 35 echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE|-]"
36 echo " grimshot check" 36 echo " grimshot check"
37 echo " grimshot usage" 37 echo " grimshot usage"
38 echo "" 38 echo ""
39 echo "Commands:" 39 echo "Commands:"
40 echo " copy: Copy the screenshot data into the clipboard." 40 echo " copy: Copy the screenshot data into the clipboard."
41 echo " save: Save the screenshot to a regular file." 41 echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
42 echo " check: Verify if required tools are installed and exit." 42 echo " check: Verify if required tools are installed and exit."
43 echo " usage: Show this message and exit." 43 echo " usage: Show this message and exit."
44 echo "" 44 echo ""
@@ -113,7 +113,7 @@ elif [ "$SUBJECT" = "area" ] ; then
113 GEOM=$(slurp -d) 113 GEOM=$(slurp -d)
114 # Check if user exited slurp without selecting the area 114 # Check if user exited slurp without selecting the area
115 if [ -z "$GEOM" ]; then 115 if [ -z "$GEOM" ]; then
116 exit 116 exit 1
117 fi 117 fi
118 WHAT="Area" 118 WHAT="Area"
119elif [ "$SUBJECT" = "active" ] ; then 119elif [ "$SUBJECT" = "active" ] ; then
@@ -132,7 +132,7 @@ elif [ "$SUBJECT" = "window" ] ; then
132 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) 132 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)
133 # Check if user exited slurp without selecting the area 133 # Check if user exited slurp without selecting the area
134 if [ -z "$GEOM" ]; then 134 if [ -z "$GEOM" ]; then
135 exit 135 exit 1
136 fi 136 fi
137 WHAT="Window" 137 WHAT="Window"
138else 138else
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1
index f6c8a377..e4baccfd 100644
--- a/contrib/grimshot.1
+++ b/contrib/grimshot.1
@@ -5,7 +5,7 @@
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" "2021-02-23"
9.P 9.P
10.SH NAME 10.SH NAME
11.P 11.P
@@ -31,6 +31,7 @@ Show notifications to the user that a screenshot has been taken.\&
31Save the screenshot into a regular file.\& Grimshot will write images 31Save the screenshot into a regular file.\& Grimshot will write images
32files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined 32files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined
33in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& 33in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
34Set FILE to '-' to pipe the output to STDOUT.\&
34.P 35.P
35.RE 36.RE
36\fBcopy\fR 37\fBcopy\fR
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd
index 4ab58532..d2a57759 100644
--- a/contrib/grimshot.1.scd
+++ b/contrib/grimshot.1.scd
@@ -19,6 +19,7 @@ grimshot - a helper for screenshots within sway
19 Save the screenshot into a regular file. Grimshot will write images 19 Save the screenshot into a regular file. Grimshot will write images
20 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined 20 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
21 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. 21 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
22 Set FILE to '-' to pipe the output to STDOUT.
22 23
23*copy* 24*copy*
24 Copy the screenshot data (as image/png) into the clipboard. 25 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/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..4be40870 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 */
@@ -282,6 +282,7 @@ sway_cmd output_cmd_dpms;
282sway_cmd output_cmd_enable; 282sway_cmd output_cmd_enable;
283sway_cmd output_cmd_max_render_time; 283sway_cmd output_cmd_max_render_time;
284sway_cmd output_cmd_mode; 284sway_cmd output_cmd_mode;
285sway_cmd output_cmd_modeline;
285sway_cmd output_cmd_position; 286sway_cmd output_cmd_position;
286sway_cmd output_cmd_scale; 287sway_cmd output_cmd_scale;
287sway_cmd output_cmd_scale_filter; 288sway_cmd output_cmd_scale_filter;
diff --git a/include/sway/config.h b/include/sway/config.h
index 59f22ae2..52867fa6 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"
@@ -257,6 +258,7 @@ struct output_config {
257 int width, height; 258 int width, height;
258 float refresh_rate; 259 float refresh_rate;
259 int custom_mode; 260 int custom_mode;
261 drmModeModeInfo drm_mode;
260 int x, y; 262 int x, y;
261 float scale; 263 float scale;
262 enum scale_filter_mode scale_filter; 264 enum scale_filter_mode scale_filter;
@@ -292,6 +294,12 @@ struct workspace_config {
292 struct side_gaps gaps_outer; 294 struct side_gaps gaps_outer;
293}; 295};
294 296
297enum pango_markup_config {
298 PANGO_MARKUP_DISABLED = false,
299 PANGO_MARKUP_ENABLED = true,
300 PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix)
301};
302
295struct bar_config { 303struct bar_config {
296 char *swaybar_command; 304 char *swaybar_command;
297 struct wl_client *client; 305 struct wl_client *client;
@@ -323,7 +331,7 @@ struct bar_config {
323 char *position; 331 char *position;
324 list_t *bindings; 332 list_t *bindings;
325 char *status_command; 333 char *status_command;
326 bool pango_markup; 334 enum pango_markup_config pango_markup;
327 char *font; 335 char *font;
328 int height; // -1 not defined 336 int height; // -1 not defined
329 bool workspace_buttons; 337 bool workspace_buttons;
@@ -480,8 +488,8 @@ struct sway_config {
480 enum sway_container_layout default_orientation; 488 enum sway_container_layout default_orientation;
481 enum sway_container_layout default_layout; 489 enum sway_container_layout default_layout;
482 char *font; 490 char *font;
483 size_t font_height; 491 int font_height;
484 size_t font_baseline; 492 int font_baseline;
485 bool pango_markup; 493 bool pango_markup;
486 int titlebar_border_thickness; 494 int titlebar_border_thickness;
487 int titlebar_h_padding; 495 int titlebar_h_padding;
@@ -559,7 +567,7 @@ struct sway_config {
559 struct sway_node *node; 567 struct sway_node *node;
560 struct sway_container *container; 568 struct sway_container *container;
561 struct sway_workspace *workspace; 569 struct sway_workspace *workspace;
562 bool using_criteria; 570 bool node_overridden; // True if the node is selected by means other than focus
563 struct { 571 struct {
564 int argc; 572 int argc;
565 char **argv; 573 char **argv;
@@ -690,14 +698,13 @@ void free_bar_binding(struct bar_binding *binding);
690void free_workspace_config(struct workspace_config *wsc); 698void free_workspace_config(struct workspace_config *wsc);
691 699
692/** 700/**
693 * Updates the value of config->font_height based on the max title height 701 * 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 702 * font as reported by pango.
695 * recalculate their heights before reporting. 703 *
696 *
697 * If the height has changed, all containers will be rearranged to take on the 704 * If the height has changed, all containers will be rearranged to take on the
698 * new size. 705 * new size.
699 */ 706 */
700void config_update_font_height(bool recalculate); 707void config_update_font_height(void);
701 708
702/** 709/**
703 * Convert bindsym into bindcode using the first configured layout. 710 * Convert bindsym into bindcode using the first configured layout.
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..7d66e699 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -52,7 +52,9 @@ struct sway_cursor {
52 struct wl_listener touch_down; 52 struct wl_listener touch_down;
53 struct wl_listener touch_up; 53 struct wl_listener touch_up;
54 struct wl_listener touch_motion; 54 struct wl_listener touch_motion;
55 struct wl_listener touch_frame;
55 bool simulating_pointer_from_touch; 56 bool simulating_pointer_from_touch;
57 bool pointer_touch_up;
56 int32_t pointer_touch_id; 58 int32_t pointer_touch_id;
57 59
58 struct wl_listener tool_axis; 60 struct wl_listener tool_axis;
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..77c2278d 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -239,7 +239,10 @@ enum wlr_edges find_resize_edge(struct sway_container *cont,
239void seatop_begin_default(struct sway_seat *seat); 239void seatop_begin_default(struct sway_seat *seat);
240 240
241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
242 uint32_t time_msec, int sx, int sy); 242 uint32_t time_msec, double sx, double sy);
243
244void seatop_begin_down_on_surface(struct sway_seat *seat,
245 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy);
243 246
244void seatop_begin_move_floating(struct sway_seat *seat, 247void seatop_begin_move_floating(struct sway_seat *seat,
245 struct sway_container *con); 248 struct sway_container *con);
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
index 6cf9bdb3..37744266 100644
--- a/include/sway/input/text_input.h
+++ b/include/sway/input/text_input.h
@@ -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..3c33c748 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -1,7 +1,6 @@
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>
5#include <wlr/types/wlr_surface.h> 4#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
@@ -23,6 +22,7 @@ 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 struct wlr_box extent;
26 enum zwlr_layer_shell_v1_layer layer; 26 enum zwlr_layer_shell_v1_layer layer;
27}; 27};
28 28
diff --git a/include/sway/output.h b/include/sway/output.h
index 96986700..5dfe0fff 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"
@@ -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..88dda097 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -9,6 +9,7 @@
9#include <wlr/types/wlr_data_device.h> 9#include <wlr/types/wlr_data_device.h>
10#include <wlr/types/wlr_input_method_v2.h> 10#include <wlr/types/wlr_input_method_v2.h>
11#include <wlr/types/wlr_foreign_toplevel_management_v1.h> 11#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
12#include <wlr/types/wlr_drm_lease_v1.h>
12#include <wlr/types/wlr_layer_shell_v1.h> 13#include <wlr/types/wlr_layer_shell_v1.h>
13#include <wlr/types/wlr_output_management_v1.h> 14#include <wlr/types/wlr_output_management_v1.h>
14#include <wlr/types/wlr_output_power_management_v1.h> 15#include <wlr/types/wlr_output_power_management_v1.h>
@@ -23,6 +24,8 @@
23#include "sway/xwayland.h" 24#include "sway/xwayland.h"
24#endif 25#endif
25 26
27struct sway_transaction;
28
26struct sway_server { 29struct sway_server {
27 struct wl_display *wl_display; 30 struct wl_display *wl_display;
28 struct wl_event_loop *wl_event_loop; 31 struct wl_event_loop *wl_event_loop;
@@ -70,6 +73,9 @@ struct sway_server {
70 struct wl_listener xdg_decoration; 73 struct wl_listener xdg_decoration;
71 struct wl_list xdg_decorations; // sway_xdg_decoration::link 74 struct wl_list xdg_decorations; // sway_xdg_decoration::link
72 75
76 struct wlr_drm_lease_v1_manager *drm_lease_manager;
77 struct wl_listener drm_lease_request;
78
73 struct wlr_presentation *presentation; 79 struct wlr_presentation *presentation;
74 80
75 struct wlr_pointer_constraints_v1 *pointer_constraints; 81 struct wlr_pointer_constraints_v1 *pointer_constraints;
@@ -85,8 +91,25 @@ struct sway_server {
85 struct wlr_text_input_manager_v3 *text_input; 91 struct wlr_text_input_manager_v3 *text_input;
86 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 92 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
87 93
94 struct wlr_xdg_activation_v1 *xdg_activation_v1;
95 struct wl_listener xdg_activation_v1_request_activate;
96
97 // The timeout for transactions, after which a transaction is applied
98 // regardless of readiness.
88 size_t txn_timeout_ms; 99 size_t txn_timeout_ms;
89 list_t *transactions; 100
101 // Stores a transaction after it has been committed, but is waiting for
102 // views to ack the new dimensions before being applied. A queued
103 // transaction is frozen and must not have new instructions added to it.
104 struct sway_transaction *queued_transaction;
105
106 // Stores a pending transaction that will be committed once the existing
107 // queued transaction is applied and freed. The pending transaction can be
108 // updated with new instructions as needed.
109 struct sway_transaction *pending_transaction;
110
111 // Stores the nodes that have been marked as "dirty" and will be put into
112 // the pending transaction.
90 list_t *dirty_nodes; 113 list_t *dirty_nodes;
91}; 114};
92 115
@@ -96,6 +119,7 @@ struct sway_debug {
96 bool noatomic; // Ignore atomic layout updates 119 bool noatomic; // Ignore atomic layout updates
97 bool txn_timings; // Log verbose messages about transactions 120 bool txn_timings; // Log verbose messages about transactions
98 bool txn_wait; // Always wait for the timeout before applying 121 bool txn_wait; // Always wait for the timeout before applying
122 bool noscanout; // Disable direct scan-out
99 123
100 enum { 124 enum {
101 DAMAGE_DEFAULT, // Default behaviour 125 DAMAGE_DEFAULT, // Default behaviour
@@ -125,5 +149,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data);
125void handle_server_decoration(struct wl_listener *listener, void *data); 149void handle_server_decoration(struct wl_listener *listener, void *data);
126void handle_xdg_decoration(struct wl_listener *listener, void *data); 150void handle_xdg_decoration(struct wl_listener *listener, void *data);
127void handle_pointer_constraint(struct wl_listener *listener, void *data); 151void handle_pointer_constraint(struct wl_listener *listener, void *data);
152void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
153 void *data);
128 154
129#endif 155#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 7e9df59f..97fa98c1 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -2,7 +2,6 @@
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>
6#include <wlr/types/wlr_surface.h> 5#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"
@@ -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
@@ -141,8 +119,6 @@ struct sway_container {
141 struct wlr_texture *title_focused_inactive; 119 struct wlr_texture *title_focused_inactive;
142 struct wlr_texture *title_unfocused; 120 struct wlr_texture *title_unfocused;
143 struct wlr_texture *title_urgent; 121 struct wlr_texture *title_urgent;
144 size_t title_height;
145 size_t title_baseline;
146 122
147 list_t *marks; // char * 123 list_t *marks; // char *
148 struct wlr_texture *marks_focused; 124 struct wlr_texture *marks_focused;
@@ -185,6 +161,11 @@ void container_for_each_child(struct sway_container *container,
185 void (*f)(struct sway_container *container, void *data), void *data); 161 void (*f)(struct sway_container *container, void *data), void *data);
186 162
187/** 163/**
164 * Returns the fullscreen container obstructing this container if it exists.
165 */
166struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container);
167
168/**
188 * Returns true if the given container is an ancestor of this container. 169 * Returns true if the given container is an ancestor of this container.
189 */ 170 */
190bool container_has_ancestor(struct sway_container *container, 171bool container_has_ancestor(struct sway_container *container,
@@ -200,11 +181,6 @@ struct sway_container *container_flatten(struct sway_container *container);
200 181
201void container_update_title_textures(struct sway_container *container); 182void container_update_title_textures(struct sway_container *container);
202 183
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, 184size_t container_build_representation(enum sway_container_layout layout,
209 list_t *children, char *buffer); 185 list_t *children, char *buffer);
210 186
@@ -231,10 +207,17 @@ void container_set_geometry_from_content(struct sway_container *con);
231/** 207/**
232 * Determine if the given container is itself floating. 208 * Determine if the given container is itself floating.
233 * This will return false for any descendants of a floating container. 209 * This will return false for any descendants of a floating container.
210 *
211 * Uses pending container state.
234 */ 212 */
235bool container_is_floating(struct sway_container *container); 213bool container_is_floating(struct sway_container *container);
236 214
237/** 215/**
216 * Same as above, but for current container state.
217 */
218bool container_is_current_floating(struct sway_container *container);
219
220/**
238 * Get a container's box in layout coordinates. 221 * Get a container's box in layout coordinates.
239 */ 222 */
240void container_get_box(struct sway_container *container, struct wlr_box *box); 223void container_get_box(struct sway_container *container, struct wlr_box *box);
@@ -299,6 +282,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container);
299/** 282/**
300 * Return the output which will be used for scale purposes. 283 * Return the output which will be used for scale purposes.
301 * This is the most recently entered output. 284 * This is the most recently entered output.
285 * If the container is not on any output, return NULL.
302 */ 286 */
303struct sway_output *container_get_effective_output(struct sway_container *con); 287struct sway_output *container_get_effective_output(struct sway_container *con);
304 288
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index e071e6c9..008361f7 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -112,7 +112,6 @@ struct sway_view {
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;
@@ -184,7 +182,7 @@ struct sway_xwayland_unmanaged {
184struct sway_view_child; 182struct sway_view_child;
185 183
186struct sway_view_child_impl { 184struct sway_view_child_impl {
187 void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy); 185 void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy);
188 void (*destroy)(struct sway_view_child *child); 186 void (*destroy)(struct sway_view_child *child);
189}; 187};
190 188
@@ -311,12 +309,22 @@ void view_destroy(struct sway_view *view);
311 309
312void view_begin_destroy(struct sway_view *view); 310void view_begin_destroy(struct sway_view *view);
313 311
312/**
313 * Map a view, ie. make it visible in the tree.
314 *
315 * `fullscreen` should be set to true (and optionally `fullscreen_output`
316 * should be populated) if the view should be made fullscreen immediately.
317 *
318 * `decoration` should be set to true if the client prefers CSD. The client's
319 * preference may be ignored.
320 */
314void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, 321void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
315 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); 322 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
316 323
317void view_unmap(struct sway_view *view); 324void view_unmap(struct sway_view *view);
318 325
319void view_update_size(struct sway_view *view, int width, int height); 326void view_update_size(struct sway_view *view);
327void view_center_surface(struct sway_view *view);
320 328
321void view_child_init(struct sway_view_child *child, 329void view_child_init(struct sway_view_child *child,
322 const struct sway_view_child_impl *impl, struct sway_view *view, 330 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/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/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..436b84d1 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.6',
5 license: 'MIT', 5 license: 'MIT',
6 meson_version: '>=0.53.0', 6 meson_version: '>=0.59.0',
7 default_options: [ 7 default_options: [
8 'c_std=c11', 8 'c_std=c11',
9 'warning_level=2', 9 'warning_level=2',
@@ -35,57 +35,54 @@ 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') 38jsonc = dependency('json-c', version: '>=0.13')
39pcre = dependency('libpcre') 39pcre = dependency('libpcre')
40wayland_server = dependency('wayland-server') 40wayland_server = dependency('wayland-server')
41wayland_client = dependency('wayland-client') 41wayland_client = dependency('wayland-client')
42wayland_cursor = dependency('wayland-cursor') 42wayland_cursor = dependency('wayland-cursor')
43wayland_egl = dependency('wayland-egl') 43wayland_egl = dependency('wayland-egl')
44wayland_protos = dependency('wayland-protocols', version: '>=1.14') 44wayland_protos = dependency('wayland-protocols', version: '>=1.14')
45xkbcommon = dependency('xkbcommon') 45xkbcommon = dependency('xkbcommon')
46cairo = dependency('cairo') 46cairo = dependency('cairo')
47pango = dependency('pango') 47pango = dependency('pango')
48pangocairo = dependency('pangocairo') 48pangocairo = dependency('pangocairo')
49gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) 49gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
50pixman = dependency('pixman-1') 50pixman = dependency('pixman-1')
51glesv2 = dependency('glesv2') 51glesv2 = dependency('glesv2')
52libevdev = dependency('libevdev') 52libevdev = dependency('libevdev')
53libinput = dependency('libinput', version: '>=1.6.0') 53libinput = dependency('libinput', version: '>=1.6.0')
54xcb = dependency('xcb', required: get_option('xwayland')) 54xcb = dependency('xcb', required: get_option('xwayland'))
55bash_comp = dependency('bash-completion', required: false) 55drm_full = dependency('libdrm') # only needed for drm_fourcc.h
56fish_comp = dependency('fish', required: false) 56drm = drm_full.partial_dependency(compile_args: true, includes: true)
57math = cc.find_library('m') 57libudev = dependency('libudev')
58rt = cc.find_library('rt') 58bash_comp = dependency('bash-completion', required: false)
59fish_comp = dependency('fish', required: false)
60math = cc.find_library('m')
61rt = cc.find_library('rt')
59 62
60# Try first to find wlroots as a subproject, then as a system dependency 63# Try first to find wlroots as a subproject, then as a system dependency
61wlroots_version = ['>=0.12.0', '<0.13.0'] 64wlroots_version = ['>=0.15.0', '<0.16.0']
62wlroots_proj = subproject( 65wlroots_proj = subproject(
63 'wlroots', 66 'wlroots',
64 default_options: ['examples=false'], 67 default_options: ['examples=false'],
65 required: false, 68 required: false,
66 version: wlroots_version, 69 version: wlroots_version,
67) 70)
68wlroots_features = {
69 'xwayland': false,
70 'systemd': false,
71 'elogind': false,
72 'libseat': false,
73}
74if wlroots_proj.found() 71if wlroots_proj.found()
75 wlroots = wlroots_proj.get_variable('wlroots') 72 wlroots = wlroots_proj.get_variable('wlroots')
76 wlroots_conf = wlroots_proj.get_variable('conf_data')
77 foreach name, _ : wlroots_features
78 has = wlroots_conf.get('WLR_HAS_' + name.to_upper()) == 1
79 wlroots_features += { name: has }
80 endforeach
81else 73else
82 wlroots = dependency('wlroots', version: wlroots_version) 74 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 75endif
88 76
77wlroots_features = {
78 'xwayland': false,
79}
80foreach name, _ : wlroots_features
81 var_name = 'have_' + name.underscorify()
82 have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
83 wlroots_features += { name: have }
84endforeach
85
89if get_option('xwayland').enabled() and not wlroots_features['xwayland'] 86if get_option('xwayland').enabled() and not wlroots_features['xwayland']
90 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') 87 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
91endif 88endif
@@ -131,8 +128,7 @@ conf_data.set10('HAVE_TRAY', have_tray)
131 128
132scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) 129scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
133if scdoc.found() 130if scdoc.found()
134 scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) 131 scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true)
135 sh = find_program('sh', native: true)
136 mandir = get_option('mandir') 132 mandir = get_option('mandir')
137 man_files = [ 133 man_files = [
138 'sway/sway.1.scd', 134 'sway/sway.1.scd',
@@ -143,9 +139,15 @@ if scdoc.found()
143 'sway/sway-output.5.scd', 139 'sway/sway-output.5.scd',
144 'swaybar/swaybar-protocol.7.scd', 140 'swaybar/swaybar-protocol.7.scd',
145 'swaymsg/swaymsg.1.scd', 141 'swaymsg/swaymsg.1.scd',
146 'swaynag/swaynag.1.scd',
147 'swaynag/swaynag.5.scd',
148 ] 142 ]
143
144 if get_option('swaynag')
145 man_files += [
146 'swaynag/swaynag.1.scd',
147 'swaynag/swaynag.5.scd',
148 ]
149 endif
150
149 foreach filename : man_files 151 foreach filename : man_files
150 topic = filename.split('.')[-3].split('/')[-1] 152 topic = filename.split('.')[-3].split('/')[-1]
151 section = filename.split('.')[-2] 153 section = filename.split('.')[-2]
@@ -155,10 +157,10 @@ if scdoc.found()
155 output, 157 output,
156 input: filename, 158 input: filename,
157 output: output, 159 output: output,
158 command: [ 160 command: scdoc_prog,
159 sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
160 ],
161 install: true, 161 install: true,
162 feed: true,
163 capture: true,
162 install_dir: '@0@/man@1@'.format(mandir, section) 164 install_dir: '@0@/man@1@'.format(mandir, section)
163 ) 165 )
164 endforeach 166 endforeach
@@ -183,7 +185,7 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
183 185
184# Compute the relative path used by compiler invocations. 186# Compute the relative path used by compiler invocations.
185source_root = meson.current_source_dir().split('/') 187source_root = meson.current_source_dir().split('/')
186build_root = meson.build_root().split('/') 188build_root = meson.global_build_root().split('/')
187relative_dir_parts = [] 189relative_dir_parts = []
188i = 0 190i = 0
189in_prefix = true 191in_prefix = true
@@ -227,9 +229,15 @@ subdir('common')
227subdir('sway') 229subdir('sway')
228subdir('swaymsg') 230subdir('swaymsg')
229 231
230subdir('client') 232if get_option('swaybar') or get_option('swaynag')
231subdir('swaybar') 233 subdir('client')
232subdir('swaynag') 234endif
235if get_option('swaybar')
236 subdir('swaybar')
237endif
238if get_option('swaynag')
239 subdir('swaynag')
240endif
233 241
234config = configuration_data() 242config = configuration_data()
235config.set('datadir', join_paths(prefix, datadir)) 243config.set('datadir', join_paths(prefix, datadir))
@@ -277,13 +285,17 @@ endif
277if get_option('bash-completions') 285if get_option('bash-completions')
278 bash_files = files( 286 bash_files = files(
279 'completions/bash/sway', 287 'completions/bash/sway',
280 'completions/bash/swaybar',
281 'completions/bash/swaymsg', 288 'completions/bash/swaymsg',
282 ) 289 )
290
291 if get_option('swaybar')
292 bash_files += files('completions/bash/swaybar')
293 endif
294
283 if bash_comp.found() 295 if bash_comp.found()
284 bash_install_dir = bash_comp.get_pkgconfig_variable( 296 bash_install_dir = bash_comp.get_variable(
285 'completionsdir', 297 pkgconfig: 'completionsdir',
286 define_variable: ['datadir', datadir] 298 pkgconfig_define: ['datadir', datadir]
287 ) 299 )
288 else 300 else
289 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') 301 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
@@ -296,12 +308,16 @@ if get_option('fish-completions')
296 fish_files = files( 308 fish_files = files(
297 'completions/fish/sway.fish', 309 'completions/fish/sway.fish',
298 'completions/fish/swaymsg.fish', 310 'completions/fish/swaymsg.fish',
299 'completions/fish/swaynag.fish',
300 ) 311 )
312
313 if get_option('swaynag')
314 fish_files += files('completions/fish/swaynag.fish')
315 endif
316
301 if fish_comp.found() 317 if fish_comp.found()
302 fish_install_dir = fish_comp.get_pkgconfig_variable( 318 fish_install_dir = fish_comp.get_variable(
303 'completionsdir', 319 pkgconfig: 'completionsdir',
304 define_variable: ['datadir', datadir] 320 pkgconfig_define: ['datadir', datadir]
305 ) 321 )
306 else 322 else
307 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') 323 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
@@ -313,12 +329,7 @@ endif
313summary({ 329summary({
314 'xwayland': have_xwayland, 330 'xwayland': have_xwayland,
315 'gdk-pixbuf': gdk_pixbuf.found(), 331 'gdk-pixbuf': gdk_pixbuf.found(),
316 'sd-bus': sdbus.found(),
317 'tray': have_tray, 332 'tray': have_tray,
318 'man-pages': scdoc.found(), 333 'man-pages': scdoc.found(),
319}, bool_yn: true) 334}, bool_yn: true)
320 335
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..8e9e65be 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
diff --git a/sway/commands.c b/sway/commands.c
index fe1e98b5..b09a04c7 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 },
@@ -98,7 +98,7 @@ static struct cmd_handler handlers[] = {
98}; 98};
99 99
100/* Config-time only commands. Keep alphabetized */ 100/* Config-time only commands. Keep alphabetized */
101static struct cmd_handler config_handlers[] = { 101static const struct cmd_handler config_handlers[] = {
102 { "default_orientation", cmd_default_orientation }, 102 { "default_orientation", cmd_default_orientation },
103 { "include", cmd_include }, 103 { "include", cmd_include },
104 { "swaybg_command", cmd_swaybg_command }, 104 { "swaybg_command", cmd_swaybg_command },
@@ -108,7 +108,7 @@ static struct cmd_handler config_handlers[] = {
108}; 108};
109 109
110/* Runtime-only commands. Keep alphabetized */ 110/* Runtime-only commands. Keep alphabetized */
111static struct cmd_handler command_handlers[] = { 111static const struct cmd_handler command_handlers[] = {
112 { "border", cmd_border }, 112 { "border", cmd_border },
113 { "create_output", cmd_create_output }, 113 { "create_output", cmd_create_output },
114 { "exit", cmd_exit }, 114 { "exit", cmd_exit },
@@ -144,22 +144,22 @@ static int handler_compare(const void *_a, const void *_b) {
144 return strcasecmp(a->command, b->command); 144 return strcasecmp(a->command, b->command);
145} 145}
146 146
147struct cmd_handler *find_handler(char *line, struct cmd_handler *handlers, 147const struct cmd_handler *find_handler(char *line,
148 size_t handlers_size) { 148 const struct cmd_handler *handlers, size_t handlers_size) {
149 if (!handlers || !handlers_size) { 149 if (!handlers || !handlers_size) {
150 return NULL; 150 return NULL;
151 } 151 }
152 struct cmd_handler query = { .command = line }; 152 const struct cmd_handler query = { .command = line };
153 return bsearch(&query, handlers, 153 return bsearch(&query, handlers,
154 handlers_size / sizeof(struct cmd_handler), 154 handlers_size / sizeof(struct cmd_handler),
155 sizeof(struct cmd_handler), handler_compare); 155 sizeof(struct cmd_handler), handler_compare);
156} 156}
157 157
158static struct cmd_handler *find_handler_ex(char *line, 158static const struct cmd_handler *find_handler_ex(char *line,
159 struct cmd_handler *config_handlers, size_t config_handlers_size, 159 const struct cmd_handler *config_handlers, size_t config_handlers_size,
160 struct cmd_handler *command_handlers, size_t command_handlers_size, 160 const struct cmd_handler *command_handlers, size_t command_handlers_size,
161 struct cmd_handler *handlers, size_t handlers_size) { 161 const struct cmd_handler *handlers, size_t handlers_size) {
162 struct cmd_handler *handler = NULL; 162 const struct cmd_handler *handler = NULL;
163 if (config->reading) { 163 if (config->reading) {
164 handler = find_handler(line, config_handlers, config_handlers_size); 164 handler = find_handler(line, config_handlers, config_handlers_size);
165 } else if (config->active) { 165 } else if (config->active) {
@@ -168,16 +168,17 @@ static struct cmd_handler *find_handler_ex(char *line,
168 return handler ? handler : find_handler(line, handlers, handlers_size); 168 return handler ? handler : find_handler(line, handlers, handlers_size);
169} 169}
170 170
171static struct cmd_handler *find_core_handler(char *line) { 171static const struct cmd_handler *find_core_handler(char *line) {
172 return find_handler_ex(line, config_handlers, sizeof(config_handlers), 172 return find_handler_ex(line, config_handlers, sizeof(config_handlers),
173 command_handlers, sizeof(command_handlers), 173 command_handlers, sizeof(command_handlers),
174 handlers, sizeof(handlers)); 174 handlers, sizeof(handlers));
175} 175}
176 176
177static void set_config_node(struct sway_node *node) { 177static void set_config_node(struct sway_node *node, bool node_overridden) {
178 config->handler_context.node = node; 178 config->handler_context.node = node;
179 config->handler_context.container = NULL; 179 config->handler_context.container = NULL;
180 config->handler_context.workspace = NULL; 180 config->handler_context.workspace = NULL;
181 config->handler_context.node_overridden = node_overridden;
181 182
182 if (node == NULL) { 183 if (node == NULL) {
183 return; 184 return;
@@ -186,7 +187,7 @@ static void set_config_node(struct sway_node *node) {
186 switch (node->type) { 187 switch (node->type) {
187 case N_CONTAINER: 188 case N_CONTAINER:
188 config->handler_context.container = node->sway_container; 189 config->handler_context.container = node->sway_container;
189 config->handler_context.workspace = node->sway_container->workspace; 190 config->handler_context.workspace = node->sway_container->pending.workspace;
190 break; 191 break;
191 case N_WORKSPACE: 192 case N_WORKSPACE:
192 config->handler_context.workspace = node->sway_workspace; 193 config->handler_context.workspace = node->sway_workspace;
@@ -202,6 +203,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
202 char *cmd; 203 char *cmd;
203 char matched_delim = ';'; 204 char matched_delim = ';';
204 list_t *containers = NULL; 205 list_t *containers = NULL;
206 bool using_criteria = false;
205 207
206 if (seat == NULL) { 208 if (seat == NULL) {
207 // passing a NULL seat means we just pick the default seat 209 // passing a NULL seat means we just pick the default seat
@@ -225,7 +227,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
225 for (; isspace(*head); ++head) {} 227 for (; isspace(*head); ++head) {}
226 // Extract criteria (valid for this command list only). 228 // Extract criteria (valid for this command list only).
227 if (matched_delim == ';') { 229 if (matched_delim == ';') {
228 config->handler_context.using_criteria = false; 230 using_criteria = false;
229 if (*head == '[') { 231 if (*head == '[') {
230 char *error = NULL; 232 char *error = NULL;
231 struct criteria *criteria = criteria_parse(head, &error); 233 struct criteria *criteria = criteria_parse(head, &error);
@@ -239,7 +241,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
239 containers = criteria_get_containers(criteria); 241 containers = criteria_get_containers(criteria);
240 head += strlen(criteria->raw); 242 head += strlen(criteria->raw);
241 criteria_destroy(criteria); 243 criteria_destroy(criteria);
242 config->handler_context.using_criteria = true; 244 using_criteria = true;
243 // Skip leading whitespace 245 // Skip leading whitespace
244 for (; isspace(*head); ++head) {} 246 for (; isspace(*head); ++head) {}
245 } 247 }
@@ -265,7 +267,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
265 } 267 }
266 } 268 }
267 } 269 }
268 struct cmd_handler *handler = find_core_handler(argv[0]); 270 const struct cmd_handler *handler = find_core_handler(argv[0]);
269 if (!handler) { 271 if (!handler) {
270 list_add(res_list, cmd_results_new(CMD_INVALID, 272 list_add(res_list, cmd_results_new(CMD_INVALID,
271 "Unknown/invalid command '%s'", argv[0])); 273 "Unknown/invalid command '%s'", argv[0]));
@@ -278,11 +280,14 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
278 argv[i] = do_var_replacement(argv[i]); 280 argv[i] = do_var_replacement(argv[i]);
279 } 281 }
280 282
281 if (!config->handler_context.using_criteria) { 283
282 // The container or workspace which this command will run on. 284 if (!using_criteria) {
283 struct sway_node *node = con ? &con->node : 285 if (con) {
284 seat_get_focus_inactive(seat, &root->node); 286 set_config_node(&con->node, true);
285 set_config_node(node); 287 } else {
288 set_config_node(seat_get_focus_inactive(seat, &root->node),
289 false);
290 }
286 struct cmd_results *res = handler->handle(argc-1, argv+1); 291 struct cmd_results *res = handler->handle(argc-1, argv+1);
287 list_add(res_list, res); 292 list_add(res_list, res);
288 if (res->status == CMD_INVALID) { 293 if (res->status == CMD_INVALID) {
@@ -296,7 +301,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
296 struct cmd_results *fail_res = NULL; 301 struct cmd_results *fail_res = NULL;
297 for (int i = 0; i < containers->length; ++i) { 302 for (int i = 0; i < containers->length; ++i) {
298 struct sway_container *container = containers->items[i]; 303 struct sway_container *container = containers->items[i];
299 set_config_node(&container->node); 304 set_config_node(&container->node, true);
300 struct cmd_results *res = handler->handle(argc-1, argv+1); 305 struct cmd_results *res = handler->handle(argc-1, argv+1);
301 if (res->status == CMD_SUCCESS) { 306 if (res->status == CMD_SUCCESS) {
302 free_cmd_results(res); 307 free_cmd_results(res);
@@ -370,7 +375,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
370 375
371 // Determine the command handler 376 // Determine the command handler
372 sway_log(SWAY_INFO, "Config command: %s", exec); 377 sway_log(SWAY_INFO, "Config command: %s", exec);
373 struct cmd_handler *handler = find_core_handler(argv[0]); 378 const struct cmd_handler *handler = find_core_handler(argv[0]);
374 if (!handler || !handler->handle) { 379 if (!handler || !handler->handle) {
375 const char *error = handler 380 const char *error = handler
376 ? "Command '%s' is shimmed, but unimplemented" 381 ? "Command '%s' is shimmed, but unimplemented"
@@ -418,12 +423,12 @@ cleanup:
418} 423}
419 424
420struct cmd_results *config_subcommand(char **argv, int argc, 425struct cmd_results *config_subcommand(char **argv, int argc,
421 struct cmd_handler *handlers, size_t handlers_size) { 426 const struct cmd_handler *handlers, size_t handlers_size) {
422 char *command = join_args(argv, argc); 427 char *command = join_args(argv, argc);
423 sway_log(SWAY_DEBUG, "Subcommand: %s", command); 428 sway_log(SWAY_DEBUG, "Subcommand: %s", command);
424 free(command); 429 free(command);
425 430
426 struct cmd_handler *handler = find_handler(argv[0], handlers, 431 const struct cmd_handler *handler = find_handler(argv[0], handlers,
427 handlers_size); 432 handlers_size);
428 if (!handler) { 433 if (!handler) {
429 return cmd_results_new(CMD_INVALID, 434 return cmd_results_new(CMD_INVALID,
@@ -453,7 +458,7 @@ struct cmd_results *config_commands_command(char *exec) {
453 goto cleanup; 458 goto cleanup;
454 } 459 }
455 460
456 struct cmd_handler *handler = find_handler(cmd, NULL, 0); 461 const struct cmd_handler *handler = find_handler(cmd, NULL, 0);
457 if (!handler && strcmp(cmd, "*") != 0) { 462 if (!handler && strcmp(cmd, "*") != 0) {
458 results = cmd_results_new(CMD_INVALID, 463 results = cmd_results_new(CMD_INVALID,
459 "Unknown/invalid command '%s'", cmd); 464 "Unknown/invalid command '%s'", cmd);
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/bind.c b/sway/commands/bind.c
index f6e58d99..25be415e 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"
@@ -559,8 +560,8 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
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/exec_always.c b/sway/commands/exec_always.c
index 39e48a44..fce337d5 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -26,7 +26,7 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
26 26
27struct cmd_results *cmd_exec_process(int argc, char **argv) { 27struct cmd_results *cmd_exec_process(int argc, char **argv) {
28 struct cmd_results *error = NULL; 28 struct cmd_results *error = NULL;
29 char *tmp = NULL; 29 char *cmd = NULL;
30 if (strcmp(argv[0], "--no-startup-id") == 0) { 30 if (strcmp(argv[0], "--no-startup-id") == 0) {
31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored.");
32 --argc; ++argv; 32 --argc; ++argv;
@@ -36,17 +36,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
36 } 36 }
37 37
38 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { 38 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) {
39 tmp = strdup(argv[0]); 39 cmd = strdup(argv[0]);
40 strip_quotes(tmp); 40 strip_quotes(cmd);
41 } else { 41 } else {
42 tmp = join_args(argv, argc); 42 cmd = join_args(argv, argc);
43 } 43 }
44 44
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); 45 sway_log(SWAY_DEBUG, "Executing %s", cmd);
51 46
52 int fd[2]; 47 int fd[2];
@@ -62,11 +57,13 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
62 sigset_t set; 57 sigset_t set;
63 sigemptyset(&set); 58 sigemptyset(&set);
64 sigprocmask(SIG_SETMASK, &set, NULL); 59 sigprocmask(SIG_SETMASK, &set, NULL);
60 signal(SIGPIPE, SIG_DFL);
65 close(fd[0]); 61 close(fd[0]);
66 if ((child = fork()) == 0) { 62 if ((child = fork()) == 0) {
67 close(fd[1]); 63 close(fd[1]);
68 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); 64 execlp("sh", "sh", "-c", cmd, (void *)NULL);
69 _exit(0); 65 sway_log_errno(SWAY_ERROR, "execlp failed");
66 _exit(1);
70 } 67 }
71 ssize_t s = 0; 68 ssize_t s = 0;
72 while ((size_t)s < sizeof(pid_t)) { 69 while ((size_t)s < sizeof(pid_t)) {
@@ -75,10 +72,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
75 close(fd[1]); 72 close(fd[1]);
76 _exit(0); // Close child process 73 _exit(0); // Close child process
77 } else if (pid < 0) { 74 } else if (pid < 0) {
75 free(cmd);
78 close(fd[0]); 76 close(fd[0]);
79 close(fd[1]); 77 close(fd[1]);
80 return cmd_results_new(CMD_FAILURE, "fork() failed"); 78 return cmd_results_new(CMD_FAILURE, "fork() failed");
81 } 79 }
80 free(cmd);
82 close(fd[1]); // close write 81 close(fd[1]); // close write
83 ssize_t s = 0; 82 ssize_t s = 0;
84 while ((size_t)s < sizeof(pid_t)) { 83 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..6771ca2f 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -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 }
@@ -334,7 +334,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
334static struct cmd_results *focus_parent(void) { 334static struct cmd_results *focus_parent(void) {
335 struct sway_seat *seat = config->handler_context.seat; 335 struct sway_seat *seat = config->handler_context.seat;
336 struct sway_container *con = config->handler_context.container; 336 struct sway_container *con = config->handler_context.container;
337 if (!con || con->fullscreen_mode) { 337 if (!con || con->pending.fullscreen_mode) {
338 return cmd_results_new(CMD_SUCCESS, NULL); 338 return cmd_results_new(CMD_SUCCESS, NULL);
339 } 339 }
340 struct sway_node *parent = node_get_parent(&con->node); 340 struct sway_node *parent = node_get_parent(&con->node);
@@ -377,6 +377,13 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
377 if (container_is_scratchpad_hidden_or_child(container)) { 377 if (container_is_scratchpad_hidden_or_child(container)) {
378 root_scratchpad_show(container); 378 root_scratchpad_show(container);
379 } 379 }
380 // if we are switching to a container under a fullscreen window, we first
381 // need to exit fullscreen so that the newly focused container becomes visible
382 struct sway_container *obstructing = container_obstructing_fullscreen_container(container);
383 if (obstructing) {
384 container_fullscreen_disable(obstructing);
385 arrange_root();
386 }
380 seat_set_focus_container(seat, container); 387 seat_set_focus_container(seat, container);
381 seat_consider_warp_to_focus(seat); 388 seat_consider_warp_to_focus(seat);
382 container_raise_floating(container); 389 container_raise_floating(container);
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/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..f2702fa1 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,8 +781,8 @@ 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(
@@ -792,11 +792,11 @@ static struct cmd_results *cmd_move_to_position_pointer(
792 wlr_output_layout_get_box(root->output_layout, output); 792 wlr_output_layout_get_box(root->output_layout, output);
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);
@@ -886,7 +886,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
886 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 886 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
887 } 887 }
888 888
889 struct sway_workspace *ws = container->workspace; 889 struct sway_workspace *ws = container->pending.workspace;
890 if (!ws) { 890 if (!ws) {
891 struct sway_seat *seat = config->handler_context.seat; 891 struct sway_seat *seat = config->handler_context.seat;
892 ws = seat_get_focused_workspace(seat); 892 ws = seat_get_focused_workspace(seat);
@@ -960,14 +960,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) {
960 // If the container is in a floating split container, 960 // If the container is in a floating split container,
961 // operate on the split container instead of the child. 961 // operate on the split container instead of the child.
962 if (container_is_floating_or_child(con)) { 962 if (container_is_floating_or_child(con)) {
963 while (con->parent) { 963 while (con->pending.parent) {
964 con = con->parent; 964 con = con->pending.parent;
965 } 965 }
966 } 966 }
967 967
968 if (!con->scratchpad) { 968 if (!con->scratchpad) {
969 root_scratchpad_add_container(con, NULL); 969 root_scratchpad_add_container(con, NULL);
970 } else if (con->workspace) { 970 } else if (con->pending.workspace) {
971 root_scratchpad_hide(con); 971 root_scratchpad_hide(con);
972 } 972 }
973 return cmd_results_new(CMD_SUCCESS, NULL); 973 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 5186a2ba..d8ef2885 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,6 +15,7 @@ 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 },
20 { "res", output_cmd_mode }, 21 { "res", output_cmd_mode },
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/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/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..ce5e5128 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,8 +125,8 @@ 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; 129 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
130 if (fs1) { 130 if (fs1) {
131 container_fullscreen_disable(con1); 131 container_fullscreen_disable(con1);
132 } 132 }
@@ -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;
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..e3daacda 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;
@@ -338,35 +337,62 @@ static bool file_exists(const char *path) {
338 return path && access(path, R_OK) != -1; 337 return path && access(path, R_OK) != -1;
339} 338}
340 339
340static char *config_path(const char *prefix, const char *config_folder) {
341 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
342 return NULL;
343 }
344
345 const char *filename = "config";
346
347 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
348 char *path = calloc(size, sizeof(char));
349 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
350 return path;
351}
352
341static char *get_config_path(void) { 353static char *get_config_path(void) {
342 static const char *config_paths[] = { 354 char *path = NULL;
343 "$HOME/.sway/config", 355 const char *home = getenv("HOME");
344 "$XDG_CONFIG_HOME/sway/config", 356 char *config_home_fallback = NULL;
345 "$HOME/.i3/config", 357
346 "$XDG_CONFIG_HOME/i3/config", 358 const char *config_home = getenv("XDG_CONFIG_HOME");
347 SYSCONFDIR "/sway/config", 359 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
348 SYSCONFDIR "/i3/config", 360 size_t size_fallback = 1 + strlen(home) + strlen("/.config");
361 config_home_fallback = calloc(size_fallback, sizeof(char));
362 if (config_home_fallback != NULL)
363 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
364 config_home = config_home_fallback;
365 }
366
367 struct config_path {
368 const char *prefix;
369 const char *config_folder;
349 }; 370 };
350 371
351 char *config_home = getenv("XDG_CONFIG_HOME"); 372 struct config_path config_paths[] = {
352 if (!config_home || !*config_home) { 373 { .prefix = home, .config_folder = ".sway"},
353 config_paths[1] = "$HOME/.config/sway/config"; 374 { .prefix = config_home, .config_folder = "sway"},
354 config_paths[3] = "$HOME/.config/i3/config"; 375 { .prefix = home, .config_folder = ".i3"},
355 } 376 { .prefix = config_home, .config_folder = "i3"},
377 { .prefix = SYSCONFDIR, .config_folder = "sway"},
378 { .prefix = SYSCONFDIR, .config_folder = "i3"}
379 };
356 380
357 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { 381 size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);
358 wordexp_t p; 382 for (size_t i = 0; i < num_config_paths; i++) {
359 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { 383 path = config_path(config_paths[i].prefix, config_paths[i].config_folder);
360 char *path = strdup(p.we_wordv[0]); 384 if (!path) {
361 wordfree(&p); 385 continue;
362 if (file_exists(path)) { 386 }
363 return path; 387 if (file_exists(path)) {
364 } 388 break;
365 free(path);
366 } 389 }
390 free(path);
391 path = NULL;
367 } 392 }
368 393
369 return NULL; 394 free(config_home_fallback);
395 return path;
370} 396}
371 397
372static bool load_config(const char *path, struct sway_config *config, 398static bool load_config(const char *path, struct sway_config *config,
@@ -514,6 +540,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
514 return success; 540 return success;
515 } 541 }
516 542
543 // Only really necessary if not explicitly `font` is set in the config.
544 config_update_font_height();
545
517 if (is_active && !validating) { 546 if (is_active && !validating) {
518 input_manager_verify_fallback_seat(); 547 input_manager_verify_fallback_seat();
519 548
@@ -964,31 +993,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
964 return lenient_strcmp(wsa->workspace, wsb->workspace); 993 return lenient_strcmp(wsa->workspace, wsb->workspace);
965} 994}
966 995
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 996
985void config_update_font_height(bool recalculate) { 997void config_update_font_height(void) {
986 size_t prev_max_height = config->font_height; 998 int prev_max_height = config->font_height;
987 config->font_height = 0;
988 config->font_baseline = 0;
989 999
990 root_for_each_container(find_baseline_iterator, &recalculate); 1000 get_text_metrics(config->font, &config->font_height, &config->font_baseline);
991 root_for_each_container(find_font_height_iterator, NULL);
992 1001
993 if (config->font_height != prev_max_height) { 1002 if (config->font_height != prev_max_height) {
994 arrange_root(); 1003 arrange_root();
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 767534a6..e09add44 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,7 @@ 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);
220 221
221 pid = fork(); 222 pid = fork();
222 if (pid < 0) { 223 if (pid < 0) {
diff --git a/sway/config/output.c b/sway/config/output.c
index c9ec6745..9fff79fd 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -8,6 +8,7 @@
8#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_output_layout.h> 9#include <wlr/types/wlr_output_layout.h>
10#include <wlr/types/wlr_output.h> 10#include <wlr/types/wlr_output.h>
11#include <wlr/backend/drm.h>
11#include "sway/config.h" 12#include "sway/config.h"
12#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
13#include "sway/output.h" 14#include "sway/output.h"
@@ -58,6 +59,7 @@ struct output_config *new_output_config(const char *name) {
58 oc->width = oc->height = -1; 59 oc->width = oc->height = -1;
59 oc->refresh_rate = -1; 60 oc->refresh_rate = -1;
60 oc->custom_mode = -1; 61 oc->custom_mode = -1;
62 oc->drm_mode.type = -1;
61 oc->x = oc->y = -1; 63 oc->x = oc->y = -1;
62 oc->scale = -1; 64 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT; 65 oc->scale_filter = SCALE_FILTER_DEFAULT;
@@ -99,6 +101,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 101 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 102 dst->custom_mode = src->custom_mode;
101 } 103 }
104 if (src->drm_mode.type != (uint32_t) -1) {
105 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
106 }
102 if (src->transform != -1) { 107 if (src->transform != -1) {
103 dst->transform = src->transform; 108 dst->transform = src->transform;
104 } 109 }
@@ -271,6 +276,18 @@ static void set_mode(struct wlr_output *output, int width, int height,
271 wlr_output_set_mode(output, best); 276 wlr_output_set_mode(output, best);
272} 277}
273 278
279static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
280 if (!wlr_output_is_drm(output)) {
281 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
282 return;
283 }
284 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
285 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
286 if (mode) {
287 wlr_output_set_mode(output, mode);
288 }
289}
290
274/* Some manufacturers hardcode the aspect-ratio of the output in the physical 291/* Some manufacturers hardcode the aspect-ratio of the output in the physical
275 * size field. */ 292 * size field. */
276static bool phys_size_is_aspect_ratio(struct wlr_output *output) { 293static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
@@ -351,14 +368,36 @@ static void queue_output_config(struct output_config *oc,
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 368 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 369 wlr_output_enable(wlr_output, true);
353 370
354 if (oc && oc->width > 0 && oc->height > 0) { 371 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
372 sway_log(SWAY_DEBUG, "Set %s modeline",
373 wlr_output->name);
374 set_modeline(wlr_output, &oc->drm_mode);
375 } else if (oc && oc->width > 0 && oc->height > 0) {
355 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 376 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 377 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 378 set_mode(wlr_output, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 379 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 380 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 381 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 382 struct wlr_output_mode *preferred_mode =
383 wlr_output_preferred_mode(wlr_output);
384 wlr_output_set_mode(wlr_output, preferred_mode);
385
386 if (!wlr_output_test(wlr_output)) {
387 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
388 "falling back to another mode");
389 struct wlr_output_mode *mode;
390 wl_list_for_each(mode, &wlr_output->modes, link) {
391 if (mode == preferred_mode) {
392 continue;
393 }
394
395 wlr_output_set_mode(wlr_output, mode);
396 if (wlr_output_test(wlr_output)) {
397 break;
398 }
399 }
400 }
362 } 401 }
363 402
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 403 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -483,6 +522,8 @@ 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 522 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present. 523 // dependent on an output being present.
485 input_manager_configure_all_inputs(); 524 input_manager_configure_all_inputs();
525 // Reconfigure the cursor images, since the scale may have changed.
526 input_manager_configure_xcursor();
486 return true; 527 return true;
487} 528}
488 529
diff --git a/sway/criteria.c b/sway/criteria.c
index 409160c5..d2a5566f 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -351,7 +351,7 @@ static bool criteria_matches_view(struct criteria *criteria,
351 } 351 }
352 352
353 if (criteria->workspace) { 353 if (criteria->workspace) {
354 struct sway_workspace *ws = view->container->workspace; 354 struct sway_workspace *ws = view->container->pending.workspace;
355 if (!ws) { 355 if (!ws) {
356 return false; 356 return false;
357 } 357 }
@@ -359,7 +359,7 @@ static bool criteria_matches_view(struct criteria *criteria,
359 switch (criteria->workspace->match_type) { 359 switch (criteria->workspace->match_type) {
360 case PATTERN_FOCUSED: 360 case PATTERN_FOCUSED:
361 if (focused && 361 if (focused &&
362 strcmp(ws->name, focused->container->workspace->name)) { 362 strcmp(ws->name, focused->container->pending.workspace->name)) {
363 return false; 363 return false;
364 } 364 }
365 break; 365 break;
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index a5cfd5b2..a6ad7166 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;
@@ -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..2b4b2027 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,7 +2,6 @@
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>
@@ -115,9 +114,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
115 // Horizontal axis 114 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT 115 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; 116 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if ((state->anchor & both_horiz) && box.width == 0) { 117 if (box.width == 0) {
119 box.x = bounds.x; 118 box.x = bounds.x;
120 box.width = bounds.width; 119 } else if ((state->anchor & both_horiz) == both_horiz) {
120 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
122 box.x = bounds.x; 122 box.x = bounds.x;
123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
@@ -128,9 +128,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
128 // Vertical axis 128 // Vertical axis
129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP 129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; 130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
131 if ((state->anchor & both_vert) && box.height == 0) { 131 if (box.height == 0) {
132 box.y = bounds.y; 132 box.y = bounds.y;
133 box.height = bounds.height; 133 } else if ((state->anchor & both_vert) == both_vert) {
134 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
134 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 135 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
135 box.y = bounds.y; 136 box.y = bounds.y;
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 137 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
@@ -139,29 +140,37 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
139 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); 140 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
140 } 141 }
141 // Margin 142 // Margin
142 if ((state->anchor & both_horiz) == both_horiz) { 143 if (box.width == 0) {
143 box.x += state->margin.left; 144 box.x += state->margin.left;
144 box.width -= state->margin.left + state->margin.right; 145 box.width = bounds.width -
146 (state->margin.left + state->margin.right);
147 } else if ((state->anchor & both_horiz) == both_horiz) {
148 // don't apply margins
145 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 149 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
146 box.x += state->margin.left; 150 box.x += state->margin.left;
147 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 151 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
148 box.x -= state->margin.right; 152 box.x -= state->margin.right;
149 } 153 }
150 if ((state->anchor & both_vert) == both_vert) { 154 if (box.height == 0) {
151 box.y += state->margin.top; 155 box.y += state->margin.top;
152 box.height -= state->margin.top + state->margin.bottom; 156 box.height = bounds.height -
157 (state->margin.top + state->margin.bottom);
158 } else if ((state->anchor & both_vert) == both_vert) {
159 // don't apply margins
153 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 160 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
154 box.y += state->margin.top; 161 box.y += state->margin.top;
155 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 162 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
156 box.y -= state->margin.bottom; 163 box.y -= state->margin.bottom;
157 } 164 }
158 if (box.width < 0 || box.height < 0) { 165 if (!sway_assert(box.width >= 0 && box.height >= 0,
159 // TODO: Bubble up a protocol error? 166 "Expected layer surface to have positive size")) {
160 wlr_layer_surface_v1_close(layer);
161 continue; 167 continue;
162 } 168 }
163 // Apply 169 // Apply
164 sway_layer->geo = box; 170 sway_layer->geo = box;
171 wlr_surface_get_extends(layer->surface, &sway_layer->extent);
172 sway_layer->extent.x += box.x;
173 sway_layer->extent.y += box.y;
165 apply_exclusive(usable_area, state->anchor, state->exclusive_zone, 174 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
166 state->margin.top, state->margin.right, 175 state->margin.top, state->margin.right,
167 state->margin.bottom, state->margin.left); 176 state->margin.bottom, state->margin.left);
@@ -191,7 +200,7 @@ void arrange_layers(struct sway_output *output) {
191 arrange_output(output); 200 arrange_output(output);
192 } 201 }
193 202
194 // Arrange non-exlusive surfaces from top->bottom 203 // Arrange non-exclusive surfaces from top->bottom
195 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 204 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
196 &usable_area, false); 205 &usable_area, false);
197 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 206 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
@@ -277,7 +286,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
277 } 286 }
278 287
279 sway_layer->layer_surface->output = NULL; 288 sway_layer->layer_surface->output = NULL;
280 wlr_layer_surface_v1_close(sway_layer->layer_surface); 289 wlr_layer_surface_v1_destroy(sway_layer->layer_surface);
281} 290}
282 291
283static void handle_surface_commit(struct wl_listener *listener, void *data) { 292static void handle_surface_commit(struct wl_listener *listener, void *data) {
@@ -288,13 +297,17 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
288 if (wlr_output == NULL) { 297 if (wlr_output == NULL) {
289 return; 298 return;
290 } 299 }
300 if (layer_surface->current.committed == 0) {
301 // The layer surface state didn't change
302 return;
303 }
291 304
292 struct sway_output *output = wlr_output->data; 305 struct sway_output *output = wlr_output->data;
293 struct wlr_box old_geo = layer->geo; 306 struct wlr_box old_extent = layer->extent;
294 arrange_layers(output); 307 arrange_layers(output);
295 308
296 bool geo_changed = 309 bool extent_changed =
297 memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; 310 memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0;
298 bool layer_changed = layer->layer != layer_surface->current.layer; 311 bool layer_changed = layer->layer != layer_surface->current.layer;
299 if (layer_changed) { 312 if (layer_changed) {
300 wl_list_remove(&layer->link); 313 wl_list_remove(&layer->link);
@@ -302,9 +315,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
302 &layer->link); 315 &layer->link);
303 layer->layer = layer_surface->current.layer; 316 layer->layer = layer_surface->current.layer;
304 } 317 }
305 if (geo_changed || layer_changed) { 318 if (extent_changed || layer_changed) {
306 output_damage_surface(output, old_geo.x, old_geo.y, 319 output_damage_box(output, &old_extent);
307 layer_surface->surface, true);
308 output_damage_surface(output, layer->geo.x, layer->geo.y, 320 output_damage_surface(output, layer->geo.x, layer->geo.y,
309 layer_surface->surface, true); 321 layer_surface->surface, true);
310 } else { 322 } else {
@@ -429,7 +441,7 @@ static struct sway_layer_subsurface *create_subsurface(
429 struct wlr_subsurface *wlr_subsurface, 441 struct wlr_subsurface *wlr_subsurface,
430 struct sway_layer_surface *layer_surface) { 442 struct sway_layer_surface *layer_surface) {
431 struct sway_layer_subsurface *subsurface = 443 struct sway_layer_subsurface *subsurface =
432 calloc(1, sizeof(struct sway_layer_surface)); 444 calloc(1, sizeof(struct sway_layer_subsurface));
433 if (subsurface == NULL) { 445 if (subsurface == NULL) {
434 return NULL; 446 return NULL;
435 } 447 }
@@ -590,14 +602,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 602 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32
591 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", 603 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",",
592 layer_surface->namespace, 604 layer_surface->namespace,
593 layer_surface->client_pending.layer, 605 layer_surface->pending.layer,
594 layer_surface->client_pending.anchor, 606 layer_surface->pending.anchor,
595 layer_surface->client_pending.desired_width, 607 layer_surface->pending.desired_width,
596 layer_surface->client_pending.desired_height, 608 layer_surface->pending.desired_height,
597 layer_surface->client_pending.margin.top, 609 layer_surface->pending.margin.top,
598 layer_surface->client_pending.margin.right, 610 layer_surface->pending.margin.right,
599 layer_surface->client_pending.margin.bottom, 611 layer_surface->pending.margin.bottom,
600 layer_surface->client_pending.margin.left); 612 layer_surface->pending.margin.left);
601 613
602 if (!layer_surface->output) { 614 if (!layer_surface->output) {
603 // Assign last active output 615 // Assign last active output
@@ -614,7 +626,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
614 sway_log(SWAY_ERROR, 626 sway_log(SWAY_ERROR,
615 "no output to auto-assign layer surface '%s' to", 627 "no output to auto-assign layer surface '%s' to",
616 layer_surface->namespace); 628 layer_surface->namespace);
617 wlr_layer_surface_v1_close(layer_surface); 629 wlr_layer_surface_v1_destroy(layer_surface);
618 return; 630 return;
619 } 631 }
620 output = root->outputs->items[0]; 632 output = root->outputs->items[0];
@@ -651,13 +663,13 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
651 sway_layer->output_destroy.notify = handle_output_destroy; 663 sway_layer->output_destroy.notify = handle_output_destroy;
652 wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); 664 wl_signal_add(&output->events.destroy, &sway_layer->output_destroy);
653 665
654 wl_list_insert(&output->layers[layer_surface->client_pending.layer], 666 wl_list_insert(&output->layers[layer_surface->pending.layer],
655 &sway_layer->link); 667 &sway_layer->link);
656 668
657 // Temporarily set the layer's current state to client_pending 669 // Temporarily set the layer's current state to pending
658 // So that we can easily arrange it 670 // So that we can easily arrange it
659 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 671 struct wlr_layer_surface_v1_state old_state = layer_surface->current;
660 layer_surface->current = layer_surface->client_pending; 672 layer_surface->current = layer_surface->pending;
661 arrange_layers(output); 673 arrange_layers(output);
662 layer_surface->current = old_state; 674 layer_surface->current = old_state;
663} 675}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 5edc8f96..8cdd47f5 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -4,9 +4,10 @@
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>
7#include <wlr/render/wlr_renderer.h> 8#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_buffer.h> 9#include <wlr/types/wlr_buffer.h>
10#include <wlr/types/wlr_drm_lease_v1.h>
10#include <wlr/types/wlr_matrix.h> 11#include <wlr/types/wlr_matrix.h>
11#include <wlr/types/wlr_output_damage.h> 12#include <wlr/types/wlr_output_damage.h>
12#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
@@ -56,26 +57,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
56 return NULL; 57 return NULL;
57} 58}
58 59
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 { 60struct surface_iterator_data {
80 sway_surface_iterator_func_t user_iterator; 61 sway_surface_iterator_func_t user_iterator;
81 void *user_data; 62 void *user_data;
@@ -84,7 +65,6 @@ struct surface_iterator_data {
84 struct sway_view *view; 65 struct sway_view *view;
85 double ox, oy; 66 double ox, oy;
86 int width, height; 67 int width, height;
87 float rotation;
88}; 68};
89 69
90static bool get_surface_box(struct surface_iterator_data *data, 70static bool get_surface_box(struct surface_iterator_data *data,
@@ -99,14 +79,9 @@ static bool get_surface_box(struct surface_iterator_data *data,
99 int sw = surface->current.width; 79 int sw = surface->current.width;
100 int sh = surface->current.height; 80 int sh = surface->current.height;
101 81
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 = { 82 struct wlr_box box = {
108 .x = data->ox + _sx, 83 .x = floor(data->ox + sx),
109 .y = data->oy + _sy, 84 .y = floor(data->oy + sy),
110 .width = sw, 85 .width = sw,
111 .height = sh, 86 .height = sh,
112 }; 87 };
@@ -114,16 +89,13 @@ static bool get_surface_box(struct surface_iterator_data *data,
114 memcpy(surface_box, &box, sizeof(struct wlr_box)); 89 memcpy(surface_box, &box, sizeof(struct wlr_box));
115 } 90 }
116 91
117 struct wlr_box rotated_box;
118 wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
119
120 struct wlr_box output_box = { 92 struct wlr_box output_box = {
121 .width = output->width, 93 .width = output->width,
122 .height = output->height, 94 .height = output->height,
123 }; 95 };
124 96
125 struct wlr_box intersection; 97 struct wlr_box intersection;
126 return wlr_box_intersection(&intersection, &output_box, &rotated_box); 98 return wlr_box_intersection(&intersection, &output_box, &box);
127} 99}
128 100
129static void output_for_each_surface_iterator(struct wlr_surface *surface, 101static void output_for_each_surface_iterator(struct wlr_surface *surface,
@@ -136,7 +108,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface,
136 return; 108 return;
137 } 109 }
138 110
139 data->user_iterator(data->output, data->view, surface, &box, data->rotation, 111 data->user_iterator(data->output, data->view, surface, &box,
140 data->user_data); 112 data->user_data);
141} 113}
142 114
@@ -152,7 +124,6 @@ void output_surface_for_each_surface(struct sway_output *output,
152 .oy = oy, 124 .oy = oy,
153 .width = surface->current.width, 125 .width = surface->current.width,
154 .height = surface->current.height, 126 .height = surface->current.height,
155 .rotation = 0,
156 }; 127 };
157 128
158 wlr_surface_for_each_surface(surface, 129 wlr_surface_for_each_surface(surface,
@@ -173,7 +144,6 @@ void output_view_for_each_surface(struct sway_output *output,
173 - view->geometry.y, 144 - view->geometry.y,
174 .width = view->container->current.content_width, 145 .width = view->container->current.content_width,
175 .height = view->container->current.content_height, 146 .height = view->container->current.content_height,
176 .rotation = 0, // TODO
177 }; 147 };
178 148
179 view_for_each_surface(view, output_for_each_surface_iterator, &data); 149 view_for_each_surface(view, output_for_each_surface_iterator, &data);
@@ -193,7 +163,6 @@ void output_view_for_each_popup_surface(struct sway_output *output,
193 - view->geometry.y, 163 - view->geometry.y,
194 .width = view->container->current.content_width, 164 .width = view->container->current.content_width,
195 .height = view->container->current.content_height, 165 .height = view->container->current.content_height,
196 .rotation = 0, // TODO
197 }; 166 };
198 167
199 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); 168 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
@@ -206,40 +175,19 @@ void output_layer_for_each_surface(struct sway_output *output,
206 wl_list_for_each(layer_surface, layer_surfaces, link) { 175 wl_list_for_each(layer_surface, layer_surfaces, link) {
207 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 176 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
208 layer_surface->layer_surface; 177 layer_surface->layer_surface;
209 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, 178 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
210 layer_surface->geo.x, layer_surface->geo.y, iterator, 179 struct surface_iterator_data data = {
211 user_data); 180 .user_iterator = iterator,
212 181 .user_data = user_data,
213 struct wlr_xdg_popup *state; 182 .output = output,
214 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 183 .view = NULL,
215 struct wlr_xdg_surface *popup = state->base; 184 .ox = layer_surface->geo.x,
216 if (!popup->configured) { 185 .oy = layer_surface->geo.y,
217 continue; 186 .width = surface->current.width,
218 } 187 .height = surface->current.height,
219 188 };
220 double popup_sx, popup_sy; 189 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
221 popup_sx = layer_surface->geo.x + 190 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 } 191 }
244} 192}
245 193
@@ -264,37 +212,19 @@ void output_layer_for_each_popup_surface(struct sway_output *output,
264 wl_list_for_each(layer_surface, layer_surfaces, link) { 212 wl_list_for_each(layer_surface, layer_surfaces, link) {
265 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 213 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
266 layer_surface->layer_surface; 214 layer_surface->layer_surface;
267 215 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
268 struct wlr_xdg_popup *state; 216 struct surface_iterator_data data = {
269 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 217 .user_iterator = iterator,
270 struct wlr_xdg_surface *popup = state->base; 218 .user_data = user_data,
271 if (!popup->configured) { 219 .output = output,
272 continue; 220 .view = NULL,
273 } 221 .ox = layer_surface->geo.x,
274 222 .oy = layer_surface->geo.y,
275 double popup_sx, popup_sy; 223 .width = surface->current.width,
276 popup_sx = layer_surface->geo.x + 224 .height = surface->current.height,
277 popup->popup->geometry.x - popup->geometry.x; 225 };
278 popup_sy = layer_surface->geo.y + 226 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
279 popup->popup->geometry.y - popup->geometry.y; 227 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 } 228 }
299} 229}
300 230
@@ -463,9 +393,9 @@ struct send_frame_done_data {
463 int msec_until_refresh; 393 int msec_until_refresh;
464}; 394};
465 395
466static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, 396static void send_frame_done_iterator(struct sway_output *output,
467 struct wlr_surface *surface, struct wlr_box *box, float rotation, 397 struct sway_view *view, struct wlr_surface *surface,
468 void *user_data) { 398 struct wlr_box *box, void *user_data) {
469 int view_max_render_time = 0; 399 int view_max_render_time = 0;
470 if (view != NULL) { 400 if (view != NULL) {
471 view_max_render_time = view->max_render_time; 401 view_max_render_time = view->max_render_time;
@@ -488,9 +418,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); 418 output_for_each_surface(output, send_frame_done_iterator, data);
489} 419}
490 420
491static void count_surface_iterator(struct sway_output *output, struct sway_view *view, 421static void count_surface_iterator(struct sway_output *output,
492 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 422 struct sway_view *view, struct wlr_surface *surface,
493 void *data) { 423 struct wlr_box *box, void *data) {
494 size_t *n = data; 424 size_t *n = data;
495 (*n)++; 425 (*n)++;
496} 426}
@@ -577,7 +507,7 @@ static int output_repaint_timer_handler(void *data) {
577 fullscreen_con = workspace->current.fullscreen; 507 fullscreen_con = workspace->current.fullscreen;
578 } 508 }
579 509
580 if (fullscreen_con && fullscreen_con->view) { 510 if (fullscreen_con && fullscreen_con->view && !debug.noscanout) {
581 // Try to scan-out the fullscreen view 511 // Try to scan-out the fullscreen view
582 static bool last_scanned_out = false; 512 static bool last_scanned_out = false;
583 bool scanned_out = 513 bool scanned_out =
@@ -590,6 +520,7 @@ static int output_repaint_timer_handler(void *data) {
590 if (last_scanned_out && !scanned_out) { 520 if (last_scanned_out && !scanned_out) {
591 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 521 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s",
592 output->wlr_output->name); 522 output->wlr_output->name);
523 output_damage_whole(output);
593 } 524 }
594 last_scanned_out = scanned_out; 525 last_scanned_out = scanned_out;
595 526
@@ -693,18 +624,15 @@ void output_damage_whole(struct sway_output *output) {
693 } 624 }
694} 625}
695 626
696static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, 627static void damage_surface_iterator(struct sway_output *output,
697 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 628 struct sway_view *view, struct wlr_surface *surface,
698 void *_data) { 629 struct wlr_box *_box, void *_data) {
699 bool *data = _data; 630 bool *data = _data;
700 bool whole = *data; 631 bool whole = *data;
701 632
702 struct wlr_box box = *_box; 633 struct wlr_box box = *_box;
703 scale_box(&box, output->wlr_output->scale); 634 scale_box(&box, output->wlr_output->scale);
704 635
705 int center_x = box.x + box.width/2;
706 int center_y = box.y + box.height/2;
707
708 if (pixman_region32_not_empty(&surface->buffer_damage)) { 636 if (pixman_region32_not_empty(&surface->buffer_damage)) {
709 pixman_region32_t damage; 637 pixman_region32_t damage;
710 pixman_region32_init(&damage); 638 pixman_region32_init(&damage);
@@ -717,14 +645,11 @@ static void damage_surface_iterator(struct sway_output *output, struct sway_view
717 ceil(output->wlr_output->scale) - surface->current.scale); 645 ceil(output->wlr_output->scale) - surface->current.scale);
718 } 646 }
719 pixman_region32_translate(&damage, box.x, box.y); 647 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); 648 wlr_output_damage_add(output->damage, &damage);
723 pixman_region32_fini(&damage); 649 pixman_region32_fini(&damage);
724 } 650 }
725 651
726 if (whole) { 652 if (whole) {
727 wlr_box_rotated_bounds(&box, &box, rotation);
728 wlr_output_damage_add_box(output->damage, &box); 653 wlr_output_damage_add_box(output->damage, &box);
729 } 654 }
730 655
@@ -816,7 +741,7 @@ static void update_output_manager_config(struct sway_server *server) {
816 struct wlr_box *output_box = wlr_output_layout_get_box( 741 struct wlr_box *output_box = wlr_output_layout_get_box(
817 root->output_layout, output->wlr_output); 742 root->output_layout, output->wlr_output);
818 // We mark the output enabled even if it is switched off by DPMS 743 // We mark the output enabled even if it is switched off by DPMS
819 config_head->state.enabled = output->enabled; 744 config_head->state.enabled = output->current_mode != NULL && output->enabled;
820 config_head->state.mode = output->current_mode; 745 config_head->state.mode = output->current_mode;
821 if (output_box) { 746 if (output_box) {
822 config_head->state.x = output_box->x; 747 config_head->state.x = output_box->x;
@@ -913,7 +838,17 @@ static void handle_present(struct wl_listener *listener, void *data) {
913void handle_new_output(struct wl_listener *listener, void *data) { 838void handle_new_output(struct wl_listener *listener, void *data) {
914 struct sway_server *server = wl_container_of(listener, server, new_output); 839 struct sway_server *server = wl_container_of(listener, server, new_output);
915 struct wlr_output *wlr_output = data; 840 struct wlr_output *wlr_output = data;
916 sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 841 sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)",
842 wlr_output, wlr_output->name, wlr_output->non_desktop);
843
844 if (wlr_output->non_desktop) {
845 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
846 if (server->drm_lease_manager) {
847 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
848 wlr_output);
849 }
850 return;
851 }
917 852
918 struct sway_output *output = output_create(wlr_output); 853 struct sway_output *output = output_create(wlr_output);
919 if (!output) { 854 if (!output) {
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index bd85282c..17fc8f6f 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -7,7 +7,6 @@
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>
@@ -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/**
@@ -104,9 +104,6 @@ static void render_texture(struct wlr_output *wlr_output,
104 wlr_backend_get_renderer(wlr_output->backend); 104 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data; 105 struct sway_output *output = wlr_output->data;
106 106
107 struct wlr_gles2_texture_attribs attribs;
108 wlr_gles2_texture_get_attribs(texture, &attribs);
109
110 pixman_region32_t damage; 107 pixman_region32_t damage;
111 pixman_region32_init(&damage); 108 pixman_region32_init(&damage);
112 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, 109 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
@@ -133,9 +130,9 @@ damage_finish:
133 pixman_region32_fini(&damage); 130 pixman_region32_fini(&damage);
134} 131}
135 132
136static void render_surface_iterator(struct sway_output *output, struct sway_view *view, 133static void render_surface_iterator(struct sway_output *output,
137 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 134 struct sway_view *view, struct wlr_surface *surface,
138 void *_data) { 135 struct wlr_box *_box, void *_data) {
139 struct render_data *data = _data; 136 struct render_data *data = _data;
140 struct wlr_output *wlr_output = output->wlr_output; 137 struct wlr_output *wlr_output = output->wlr_output;
141 pixman_region32_t *output_damage = data->damage; 138 pixman_region32_t *output_damage = data->damage;
@@ -149,15 +146,23 @@ static void render_surface_iterator(struct sway_output *output, struct sway_view
149 struct wlr_fbox src_box; 146 struct wlr_fbox src_box;
150 wlr_surface_get_buffer_source_box(surface, &src_box); 147 wlr_surface_get_buffer_source_box(surface, &src_box);
151 148
152 struct wlr_box dst_box = *_box; 149 struct wlr_box proj_box = *_box;
153 scale_box(&dst_box, wlr_output->scale); 150 scale_box(&proj_box, wlr_output->scale);
154 151
155 float matrix[9]; 152 float matrix[9];
156 enum wl_output_transform transform = 153 enum wl_output_transform transform =
157 wlr_output_transform_invert(surface->current.transform); 154 wlr_output_transform_invert(surface->current.transform);
158 wlr_matrix_project_box(matrix, &dst_box, transform, rotation, 155 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
159 wlr_output->transform_matrix); 156 wlr_output->transform_matrix);
160 157
158 struct wlr_box dst_box = *_box;
159 struct wlr_box *clip_box = data->clip_box;
160 if (clip_box != NULL) {
161 dst_box.width = fmin(dst_box.width, clip_box->width);
162 dst_box.height = fmin(dst_box.height, clip_box->height);
163 }
164 scale_box(&dst_box, wlr_output->scale);
165
161 render_texture(wlr_output, output_damage, texture, 166 render_texture(wlr_output, output_damage, texture,
162 &src_box, &dst_box, matrix, alpha); 167 &src_box, &dst_box, matrix, alpha);
163 168
@@ -256,6 +261,14 @@ static void render_view_toplevels(struct sway_view *view,
256 .damage = damage, 261 .damage = damage,
257 .alpha = alpha, 262 .alpha = alpha,
258 }; 263 };
264 struct wlr_box clip_box;
265 if (!container_is_current_floating(view->container)) {
266 // As we pass the geometry offsets to the surface iterator, we will
267 // need to account for the offsets in the clip dimensions.
268 clip_box.width = view->container->current.content_width + view->geometry.x;
269 clip_box.height = view->container->current.content_height + view->geometry.y;
270 data.clip_box = &clip_box;
271 }
259 // Render all toplevels without descending into popups 272 // Render all toplevels without descending into popups
260 double ox = view->container->surface_x - 273 double ox = view->container->surface_x -
261 output->lx - view->geometry.x; 274 output->lx - view->geometry.x;
@@ -282,17 +295,18 @@ static void render_saved_view(struct sway_view *view,
282 if (wl_list_empty(&view->saved_buffers)) { 295 if (wl_list_empty(&view->saved_buffers)) {
283 return; 296 return;
284 } 297 }
298
299 bool floating = container_is_current_floating(view->container);
300
285 struct sway_saved_buffer *saved_buf; 301 struct sway_saved_buffer *saved_buf;
286 wl_list_for_each(saved_buf, &view->saved_buffers, link) { 302 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
287 if (!saved_buf->buffer->texture) { 303 if (!saved_buf->buffer->texture) {
288 continue; 304 continue;
289 } 305 }
290 306
291 struct wlr_box box = { 307 struct wlr_box proj_box = {
292 .x = view->container->surface_x - output->lx - 308 .x = saved_buf->x - view->saved_geometry.x - output->lx,
293 view->saved_geometry.x + saved_buf->x, 309 .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, 310 .width = saved_buf->width,
297 .height = saved_buf->height, 311 .height = saved_buf->height,
298 }; 312 };
@@ -303,20 +317,31 @@ static void render_saved_view(struct sway_view *view,
303 }; 317 };
304 318
305 struct wlr_box intersection; 319 struct wlr_box intersection;
306 bool intersects = wlr_box_intersection(&intersection, &output_box, &box); 320 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
307 if (!intersects) { 321 if (!intersects) {
308 continue; 322 continue;
309 } 323 }
310 324
311 scale_box(&box, wlr_output->scale); 325 struct wlr_box dst_box = proj_box;
326 scale_box(&proj_box, wlr_output->scale);
312 327
313 float matrix[9]; 328 float matrix[9];
314 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); 329 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
315 wlr_matrix_project_box(matrix, &box, transform, 0, 330 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
316 wlr_output->transform_matrix); 331 wlr_output->transform_matrix);
317 332
333 if (!floating) {
334 dst_box.width = fmin(dst_box.width,
335 view->container->current.content_width -
336 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
337 dst_box.height = fmin(dst_box.height,
338 view->container->current.content_height -
339 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
340 }
341 scale_box(&dst_box, wlr_output->scale);
342
318 render_texture(wlr_output, damage, saved_buf->buffer->texture, 343 render_texture(wlr_output, damage, saved_buf->buffer->texture,
319 &saved_buf->source_box, &box, matrix, alpha); 344 &saved_buf->source_box, &dst_box, matrix, alpha);
320 } 345 }
321 346
322 // FIXME: we should set the surface that this saved buffer originates from 347 // FIXME: we should set the surface that this saved buffer originates from
@@ -348,8 +373,8 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
348 if (state->border_left) { 373 if (state->border_left) {
349 memcpy(&color, colors->child_border, sizeof(float) * 4); 374 memcpy(&color, colors->child_border, sizeof(float) * 4);
350 premultiply_alpha(color, con->alpha); 375 premultiply_alpha(color, con->alpha);
351 box.x = state->x; 376 box.x = floor(state->x);
352 box.y = state->content_y; 377 box.y = floor(state->content_y);
353 box.width = state->border_thickness; 378 box.width = state->border_thickness;
354 box.height = state->content_height; 379 box.height = state->content_height;
355 scale_box(&box, output_scale); 380 scale_box(&box, output_scale);
@@ -361,14 +386,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
361 container_current_parent_layout(con); 386 container_current_parent_layout(con);
362 387
363 if (state->border_right) { 388 if (state->border_right) {
364 if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { 389 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
365 memcpy(&color, colors->indicator, sizeof(float) * 4); 390 memcpy(&color, colors->indicator, sizeof(float) * 4);
366 } else { 391 } else {
367 memcpy(&color, colors->child_border, sizeof(float) * 4); 392 memcpy(&color, colors->child_border, sizeof(float) * 4);
368 } 393 }
369 premultiply_alpha(color, con->alpha); 394 premultiply_alpha(color, con->alpha);
370 box.x = state->content_x + state->content_width; 395 box.x = floor(state->content_x + state->content_width);
371 box.y = state->content_y; 396 box.y = floor(state->content_y);
372 box.width = state->border_thickness; 397 box.width = state->border_thickness;
373 box.height = state->content_height; 398 box.height = state->content_height;
374 scale_box(&box, output_scale); 399 scale_box(&box, output_scale);
@@ -376,14 +401,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
376 } 401 }
377 402
378 if (state->border_bottom) { 403 if (state->border_bottom) {
379 if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { 404 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
380 memcpy(&color, colors->indicator, sizeof(float) * 4); 405 memcpy(&color, colors->indicator, sizeof(float) * 4);
381 } else { 406 } else {
382 memcpy(&color, colors->child_border, sizeof(float) * 4); 407 memcpy(&color, colors->child_border, sizeof(float) * 4);
383 } 408 }
384 premultiply_alpha(color, con->alpha); 409 premultiply_alpha(color, con->alpha);
385 box.x = state->x; 410 box.x = floor(state->x);
386 box.y = state->content_y + state->content_height; 411 box.y = floor(state->content_y + state->content_height);
387 box.width = state->width; 412 box.width = state->width;
388 box.height = state->border_thickness; 413 box.height = state->border_thickness;
389 scale_box(&box, output_scale); 414 scale_box(&box, output_scale);
@@ -464,9 +489,10 @@ static void render_titlebar(struct sway_output *output,
464 int ob_marks_x = 0; // output-buffer-local 489 int ob_marks_x = 0; // output-buffer-local
465 int ob_marks_width = 0; // output-buffer-local 490 int ob_marks_width = 0; // output-buffer-local
466 if (config->show_marks && marks_texture) { 491 if (config->show_marks && marks_texture) {
467 struct wlr_box texture_box; 492 struct wlr_box texture_box = {
468 wlr_texture_get_size(marks_texture, 493 .width = marks_texture->width,
469 &texture_box.width, &texture_box.height); 494 .height = marks_texture->height,
495 };
470 ob_marks_width = texture_box.width; 496 ob_marks_width = texture_box.width;
471 497
472 // The marks texture might be shorter than the config->font_height, in 498 // The marks texture might be shorter than the config->font_height, in
@@ -517,15 +543,23 @@ static void render_titlebar(struct sway_output *output,
517 int ob_title_x = 0; // output-buffer-local 543 int ob_title_x = 0; // output-buffer-local
518 int ob_title_width = 0; // output-buffer-local 544 int ob_title_width = 0; // output-buffer-local
519 if (title_texture) { 545 if (title_texture) {
520 struct wlr_box texture_box; 546 struct wlr_box texture_box = {
521 wlr_texture_get_size(title_texture, 547 .width = title_texture->width,
522 &texture_box.width, &texture_box.height); 548 .height = title_texture->height,
549 };
550
551 // The effective output may be NULL when con is not on any output.
552 // This can happen because we render all children of containers,
553 // even those that are out of the bounds of any output.
554 struct sway_output *effective = container_get_effective_output(con);
555 float title_scale = effective ? effective->wlr_output->scale : output_scale;
556 texture_box.width = texture_box.width * output_scale / title_scale;
557 texture_box.height = texture_box.height * output_scale / title_scale;
523 ob_title_width = texture_box.width; 558 ob_title_width = texture_box.width;
524 559
525 // The title texture might be shorter than the config->font_height, 560 // The title texture might be shorter than the config->font_height,
526 // in which case we need to pad it above and below. 561 // in which case we need to pad it above and below.
527 int ob_padding_above = round((config->font_baseline - 562 int ob_padding_above = round((titlebar_v_padding -
528 con->title_baseline + titlebar_v_padding -
529 titlebar_border_thickness) * output_scale); 563 titlebar_border_thickness) * output_scale);
530 int ob_padding_below = ob_bg_height - ob_padding_above - 564 int ob_padding_below = ob_bg_height - ob_padding_above -
531 texture_box.height; 565 texture_box.height;
@@ -660,8 +694,8 @@ static void render_top_border(struct sway_output *output,
660 // Child border - top edge 694 // Child border - top edge
661 memcpy(&color, colors->child_border, sizeof(float) * 4); 695 memcpy(&color, colors->child_border, sizeof(float) * 4);
662 premultiply_alpha(color, con->alpha); 696 premultiply_alpha(color, con->alpha);
663 box.x = state->x; 697 box.x = floor(state->x);
664 box.y = state->y; 698 box.y = floor(state->y);
665 box.width = state->width; 699 box.width = state->width;
666 box.height = state->border_thickness; 700 box.height = state->border_thickness;
667 scale_box(&box, output_scale); 701 scale_box(&box, output_scale);
@@ -716,8 +750,8 @@ static void render_containers_linear(struct sway_output *output,
716 } 750 }
717 751
718 if (state->border == B_NORMAL) { 752 if (state->border == B_NORMAL) {
719 render_titlebar(output, damage, child, state->x, 753 render_titlebar(output, damage, child, floor(state->x),
720 state->y, state->width, colors, 754 floor(state->y), state->width, colors,
721 title_texture, marks_texture); 755 title_texture, marks_texture);
722 } else if (state->border == B_PIXEL) { 756 } else if (state->border == B_PIXEL) {
723 render_top_border(output, damage, child, colors); 757 render_top_border(output, damage, child, colors);
@@ -771,7 +805,7 @@ static void render_containers_tabbed(struct sway_output *output,
771 marks_texture = child->marks_unfocused; 805 marks_texture = child->marks_unfocused;
772 } 806 }
773 807
774 int x = cstate->x + tab_width * i; 808 int x = floor(cstate->x + tab_width * i);
775 809
776 // Make last tab use the remaining width of the parent 810 // Make last tab use the remaining width of the parent
777 if (i == parent->children->length - 1) { 811 if (i == parent->children->length - 1) {
@@ -884,8 +918,8 @@ static void render_container(struct sway_output *output,
884 struct parent_data data = { 918 struct parent_data data = {
885 .layout = con->current.layout, 919 .layout = con->current.layout,
886 .box = { 920 .box = {
887 .x = con->current.x, 921 .x = floor(con->current.x),
888 .y = con->current.y, 922 .y = floor(con->current.y),
889 .width = con->current.width, 923 .width = con->current.width,
890 .height = con->current.height, 924 .height = con->current.height,
891 }, 925 },
@@ -901,8 +935,8 @@ static void render_workspace(struct sway_output *output,
901 struct parent_data data = { 935 struct parent_data data = {
902 .layout = ws->current.layout, 936 .layout = ws->current.layout,
903 .box = { 937 .box = {
904 .x = ws->current.x, 938 .x = floor(ws->current.x),
905 .y = ws->current.y, 939 .y = floor(ws->current.y),
906 .width = ws->current.width, 940 .width = ws->current.width,
907 .height = ws->current.height, 941 .height = ws->current.height,
908 }, 942 },
@@ -936,8 +970,8 @@ static void render_floating_container(struct sway_output *soutput,
936 } 970 }
937 971
938 if (con->current.border == B_NORMAL) { 972 if (con->current.border == B_NORMAL) {
939 render_titlebar(soutput, damage, con, con->current.x, 973 render_titlebar(soutput, damage, con, floor(con->current.x),
940 con->current.y, con->current.width, colors, 974 floor(con->current.y), con->current.width, colors,
941 title_texture, marks_texture); 975 title_texture, marks_texture);
942 } else if (con->current.border == B_PIXEL) { 976 } else if (con->current.border == B_PIXEL) {
943 render_top_border(soutput, damage, con, colors); 977 render_top_border(soutput, damage, con, colors);
@@ -959,7 +993,7 @@ static void render_floating(struct sway_output *soutput,
959 } 993 }
960 for (int k = 0; k < ws->current.floating->length; ++k) { 994 for (int k = 0; k < ws->current.floating->length; ++k) {
961 struct sway_container *floater = ws->current.floating->items[k]; 995 struct sway_container *floater = ws->current.floating->items[k];
962 if (floater->fullscreen_mode != FULLSCREEN_NONE) { 996 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
963 continue; 997 continue;
964 } 998 }
965 render_floating_container(soutput, damage, floater); 999 render_floating_container(soutput, damage, floater);
@@ -999,6 +1033,12 @@ void output_render(struct sway_output *output, struct timespec *when,
999 1033
1000 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); 1034 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1001 1035
1036 if (debug.damage == DAMAGE_RERENDER) {
1037 int width, height;
1038 wlr_output_transformed_resolution(wlr_output, &width, &height);
1039 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1040 }
1041
1002 if (!pixman_region32_not_empty(damage)) { 1042 if (!pixman_region32_not_empty(damage)) {
1003 // Output isn't damaged but needs buffer swap 1043 // Output isn't damaged but needs buffer swap
1004 goto renderer_end; 1044 goto renderer_end;
@@ -1006,10 +1046,6 @@ void output_render(struct sway_output *output, struct timespec *when,
1006 1046
1007 if (debug.damage == DAMAGE_HIGHLIGHT) { 1047 if (debug.damage == DAMAGE_HIGHLIGHT) {
1008 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); 1048 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1009 } else if (debug.damage == DAMAGE_RERENDER) {
1010 int width, height;
1011 wlr_output_transformed_resolution(wlr_output, &width, &height);
1012 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1013 } 1049 }
1014 1050
1015 if (output_has_opaque_overlay_layer_surface(output)) { 1051 if (output_has_opaque_overlay_layer_surface(output)) {
@@ -1110,7 +1146,7 @@ renderer_end:
1110 wlr_region_transform(&frame_damage, &output->damage->current, 1146 wlr_region_transform(&frame_damage, &output->damage->current,
1111 transform, width, height); 1147 transform, width, height);
1112 1148
1113 if (debug.damage == DAMAGE_HIGHLIGHT) { 1149 if (debug.damage != DAMAGE_DEFAULT) {
1114 pixman_region32_union_rect(&frame_damage, &frame_damage, 1150 pixman_region32_union_rect(&frame_damage, &frame_damage,
1115 0, 0, wlr_output->width, wlr_output->height); 1151 0, 0, wlr_output->width, wlr_output->height);
1116 } 1152 }
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index eac38991..b1f3fb32 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) &&
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..7c78a897 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_surface *surface = popup->wlr_xdg_surface;
28 28
29 int x_offset = -child->view->geometry.x - surface->geometry.x;
30 int y_offset = -child->view->geometry.y - surface->geometry.y;
31
32 wlr_xdg_popup_get_toplevel_coords(surface->popup, 29 wlr_xdg_popup_get_toplevel_coords(surface->popup,
33 x_offset + surface->popup->geometry.x, 30 surface->popup->geometry.x - surface->geometry.x,
34 y_offset + surface->popup->geometry.y, 31 surface->popup->geometry.y - surface->geometry.y,
35 root_sx, root_sy); 32 sx, 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
@@ -70,13 +67,13 @@ static 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_surface->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,
79 .y = output->ly - view->container->content_y, 76 .y = output->ly - view->container->pending.content_y,
80 .width = output->width, 77 .width = output->width,
81 .height = output->height, 78 .height = output->height,
82 }; 79 };
@@ -293,19 +290,23 @@ static void handle_commit(struct wl_listener *listener, void *data) {
293 new_geo.y != view->geometry.y; 290 new_geo.y != view->geometry.y;
294 291
295 if (new_size) { 292 if (new_size) {
296 // The view has unexpectedly sent a new size 293 // The client changed its surface size in this commit. For floating
294 // containers, we resize the container to match. For tiling containers,
295 // we only recenter the surface.
297 desktop_damage_view(view); 296 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)); 297 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
298 if (container_is_floating(view->container)) {
299 view_update_size(view);
300 transaction_commit_dirty_client();
301 } else {
302 view_center_surface(view);
303 }
300 desktop_damage_view(view); 304 desktop_damage_view(view);
301 transaction_commit_dirty();
302 } 305 }
303 306
304 if (view->container->node.instruction) { 307 if (view->container->node.instruction) {
305 transaction_notify_view_ready_by_serial(view, 308 transaction_notify_view_ready_by_serial(view,
306 xdg_surface->configure_serial); 309 xdg_surface->configure_serial);
307 } else if (new_size) {
308 transaction_notify_view_ready_immediately(view);
309 } 310 }
310 311
311 view_damage_from(view); 312 view_damage_from(view);
@@ -354,7 +355,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
354 if (e->fullscreen && e->output && e->output->data) { 355 if (e->fullscreen && e->output && e->output->data) {
355 struct sway_output *output = e->output->data; 356 struct sway_output *output = e->output->data;
356 struct sway_workspace *ws = output_get_active_workspace(output); 357 struct sway_workspace *ws = output_get_active_workspace(output);
357 if (ws && !container_is_scratchpad_hidden(container)) { 358 if (ws && !container_is_scratchpad_hidden(container) &&
359 container->pending.workspace != ws) {
358 if (container_is_floating(container)) { 360 if (container_is_floating(container)) {
359 workspace_add_floating(ws, container); 361 workspace_add_floating(ws, container);
360 } else { 362 } else {
@@ -369,11 +371,6 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
369 transaction_commit_dirty(); 371 transaction_commit_dirty();
370} 372}
371 373
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) { 374static void handle_request_move(struct wl_listener *listener, void *data) {
378 struct sway_xdg_shell_view *xdg_shell_view = 375 struct sway_xdg_shell_view *xdg_shell_view =
379 wl_container_of(listener, xdg_shell_view, request_move); 376 wl_container_of(listener, xdg_shell_view, request_move);
@@ -416,7 +413,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
416 wl_list_remove(&xdg_shell_view->commit.link); 413 wl_list_remove(&xdg_shell_view->commit.link);
417 wl_list_remove(&xdg_shell_view->new_popup.link); 414 wl_list_remove(&xdg_shell_view->new_popup.link);
418 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 415 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); 416 wl_list_remove(&xdg_shell_view->request_move.link);
421 wl_list_remove(&xdg_shell_view->request_resize.link); 417 wl_list_remove(&xdg_shell_view->request_resize.link);
422 wl_list_remove(&xdg_shell_view->set_title.link); 418 wl_list_remove(&xdg_shell_view->set_title.link);
@@ -438,17 +434,20 @@ static void handle_map(struct wl_listener *listener, void *data) {
438 434
439 bool csd = false; 435 bool csd = false;
440 436
441 if (!view->xdg_decoration) { 437 if (view->xdg_decoration) {
438 enum wlr_xdg_toplevel_decoration_v1_mode mode =
439 view->xdg_decoration->wlr_xdg_decoration->client_pending_mode;
440 csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
441 } else {
442 struct sway_server_decoration *deco = 442 struct sway_server_decoration *deco =
443 decoration_from_surface(xdg_surface->surface); 443 decoration_from_surface(xdg_surface->surface);
444 csd = !deco || deco->wlr_server_decoration->mode == 444 csd = !deco || deco->wlr_server_decoration->mode ==
445 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 445 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
446
447 } 446 }
448 447
449 view_map(view, view->wlr_xdg_surface->surface, 448 view_map(view, view->wlr_xdg_surface->surface,
450 xdg_surface->toplevel->client_pending.fullscreen, 449 xdg_surface->toplevel->requested.fullscreen,
451 xdg_surface->toplevel->client_pending.fullscreen_output, 450 xdg_surface->toplevel->requested.fullscreen_output,
452 csd); 451 csd);
453 452
454 transaction_commit_dirty(); 453 transaction_commit_dirty();
@@ -465,10 +464,6 @@ static void handle_map(struct wl_listener *listener, void *data) {
465 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, 464 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
466 &xdg_shell_view->request_fullscreen); 465 &xdg_shell_view->request_fullscreen);
467 466
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; 467 xdg_shell_view->request_move.notify = handle_request_move;
473 wl_signal_add(&xdg_surface->toplevel->events.request_move, 468 wl_signal_add(&xdg_surface->toplevel->events.request_move,
474 &xdg_shell_view->request_move); 469 &xdg_shell_view->request_move);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index e1a2e463..1af8d248 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -105,14 +105,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
106 // This simply returns focus to the parent surface if there's one available. 106 // This simply returns focus to the parent surface if there's one available.
107 // This seems to handle JetBrains issues. 107 // This seems to handle JetBrains issues.
108 if (xsurface->parent && xsurface->parent->surface && 108 if (xsurface->parent && xsurface->parent->surface
109 wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { 109 && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) {
110 struct wlr_xwayland_surface *next_surface = 110 seat_set_focus_surface(seat, xsurface->parent->surface, false);
111 wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); 111 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 } 112 }
117 113
118 // Restore focus 114 // Restore focus
@@ -258,6 +254,7 @@ static void set_activated(struct sway_view *view, bool activated) {
258 } 254 }
259 255
260 wlr_xwayland_surface_activate(surface, activated); 256 wlr_xwayland_surface_activate(surface, activated);
257 wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE);
261} 258}
262 259
263static void set_tiled(struct sway_view *view, bool tiled) { 260static void set_tiled(struct sway_view *view, bool tiled) {
@@ -399,30 +396,31 @@ static void handle_commit(struct wl_listener *listener, void *data) {
399 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 396 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
400 struct wlr_surface_state *state = &xsurface->surface->current; 397 struct wlr_surface_state *state = &xsurface->surface->current;
401 398
399 struct wlr_box new_geo;
400 get_geometry(view, &new_geo);
401 bool new_size = new_geo.width != view->geometry.width ||
402 new_geo.height != view->geometry.height ||
403 new_geo.x != view->geometry.x ||
404 new_geo.y != view->geometry.y;
405
406 if (new_size) {
407 // The client changed its surface size in this commit. For floating
408 // containers, we resize the container to match. For tiling containers,
409 // we only recenter the surface.
410 desktop_damage_view(view);
411 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
412 if (container_is_floating(view->container)) {
413 view_update_size(view);
414 transaction_commit_dirty_client();
415 } else {
416 view_center_surface(view);
417 }
418 desktop_damage_view(view);
419 }
420
402 if (view->container->node.instruction) { 421 if (view->container->node.instruction) {
403 get_geometry(view, &view->geometry);
404 transaction_notify_view_ready_by_geometry(view, 422 transaction_notify_view_ready_by_geometry(view,
405 xsurface->x, xsurface->y, state->width, state->height); 423 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 } 424 }
427 425
428 view_damage_from(view); 426 view_damage_from(view);
@@ -527,10 +525,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
527 view->natural_height = ev->height; 525 view->natural_height = ev->height;
528 container_floating_resize_and_center(view->container); 526 container_floating_resize_and_center(view->container);
529 527
530 configure(view, view->container->content_x, 528 configure(view, view->container->pending.content_x,
531 view->container->content_y, 529 view->container->pending.content_y,
532 view->container->content_width, 530 view->container->pending.content_width,
533 view->container->content_height); 531 view->container->pending.content_height);
534 node_set_dirty(&view->container->node); 532 node_set_dirty(&view->container->node);
535 } else { 533 } else {
536 configure(view, view->container->current.content_x, 534 configure(view, view->container->current.content_x,
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index fa604426..2fe5b202 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;
@@ -383,7 +388,6 @@ static void handle_pointer_motion_relative(
383 388
384 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, 389 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y,
385 e->unaccel_dx, e->unaccel_dy); 390 e->unaccel_dx, e->unaccel_dy);
386 transaction_commit_dirty();
387} 391}
388 392
389static void handle_pointer_motion_absolute( 393static void handle_pointer_motion_absolute(
@@ -401,7 +405,6 @@ static void handle_pointer_motion_absolute(
401 double dy = ly - cursor->cursor->y; 405 double dy = ly - cursor->cursor->y;
402 406
403 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 407 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
404 transaction_commit_dirty();
405} 408}
406 409
407void dispatch_cursor_button(struct sway_cursor *cursor, 410void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -431,7 +434,6 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
431 cursor_handle_activity_from_device(cursor, event->device); 434 cursor_handle_activity_from_device(cursor, event->device);
432 dispatch_cursor_button(cursor, event->device, 435 dispatch_cursor_button(cursor, event->device,
433 event->time_msec, event->button, event->state); 436 event->time_msec, event->button, event->state);
434 transaction_commit_dirty();
435} 437}
436 438
437void dispatch_cursor_axis(struct sway_cursor *cursor, 439void dispatch_cursor_axis(struct sway_cursor *cursor,
@@ -444,7 +446,6 @@ static void handle_pointer_axis(struct wl_listener *listener, void *data) {
444 struct wlr_event_pointer_axis *event = data; 446 struct wlr_event_pointer_axis *event = data;
445 cursor_handle_activity_from_device(cursor, event->device); 447 cursor_handle_activity_from_device(cursor, event->device);
446 dispatch_cursor_axis(cursor, event); 448 dispatch_cursor_axis(cursor, event);
447 transaction_commit_dirty();
448} 449}
449 450
450static void handle_pointer_frame(struct wl_listener *listener, void *data) { 451static void handle_pointer_frame(struct wl_listener *listener, void *data) {
@@ -494,8 +495,6 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
494 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 495 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
495 dispatch_cursor_button(cursor, event->device, event->time_msec, 496 dispatch_cursor_button(cursor, event->device, event->time_msec,
496 BTN_LEFT, WLR_BUTTON_PRESSED); 497 BTN_LEFT, WLR_BUTTON_PRESSED);
497 wlr_seat_pointer_notify_frame(wlr_seat);
498 transaction_commit_dirty();
499 } 498 }
500} 499}
501 500
@@ -508,11 +507,9 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
508 507
509 if (cursor->simulating_pointer_from_touch) { 508 if (cursor->simulating_pointer_from_touch) {
510 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 509 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
511 cursor->simulating_pointer_from_touch = false; 510 cursor->pointer_touch_up = true;
512 dispatch_cursor_button(cursor, event->device, event->time_msec, 511 dispatch_cursor_button(cursor, event->device, event->time_msec,
513 BTN_LEFT, WLR_BUTTON_RELEASED); 512 BTN_LEFT, WLR_BUTTON_RELEASED);
514 wlr_seat_pointer_notify_frame(wlr_seat);
515 transaction_commit_dirty();
516 } 513 }
517 } else { 514 } else {
518 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 515 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id);
@@ -553,7 +550,6 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
553 dx = lx - cursor->cursor->x; 550 dx = lx - cursor->cursor->x;
554 dy = ly - cursor->cursor->y; 551 dy = ly - cursor->cursor->y;
555 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 552 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
556 transaction_commit_dirty();
557 } 553 }
558 } else if (surface) { 554 } else if (surface) {
559 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 555 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec,
@@ -561,6 +557,24 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
561 } 557 }
562} 558}
563 559
560static void handle_touch_frame(struct wl_listener *listener, void *data) {
561 struct sway_cursor *cursor =
562 wl_container_of(listener, cursor, touch_frame);
563
564 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
565
566 if (cursor->simulating_pointer_from_touch) {
567 wlr_seat_pointer_notify_frame(wlr_seat);
568
569 if (cursor->pointer_touch_up) {
570 cursor->pointer_touch_up = false;
571 cursor->simulating_pointer_from_touch = false;
572 }
573 } else {
574 wlr_seat_touch_notify_frame(wlr_seat);
575 }
576}
577
564static double apply_mapping_from_coord(double low, double high, double value) { 578static double apply_mapping_from_coord(double low, double high, double value) {
565 if (isnan(value)) { 579 if (isnan(value)) {
566 return value; 580 return value;
@@ -639,8 +653,6 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
639 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 653 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);
640 pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); 654 pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy);
641 } 655 }
642
643 transaction_commit_dirty();
644} 656}
645 657
646static void handle_tool_axis(struct wl_listener *listener, void *data) { 658static void handle_tool_axis(struct wl_listener *listener, void *data) {
@@ -720,7 +732,6 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
720 dispatch_cursor_button(cursor, event->device, event->time_msec, 732 dispatch_cursor_button(cursor, event->device, event->time_msec,
721 BTN_LEFT, WLR_BUTTON_RELEASED); 733 BTN_LEFT, WLR_BUTTON_RELEASED);
722 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 734 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)) { 735 } 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 736 // 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 737 // tablet v2, we should notify that surface if it gets released over a
@@ -733,7 +744,6 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
733 dispatch_cursor_button(cursor, event->device, event->time_msec, 744 dispatch_cursor_button(cursor, event->device, event->time_msec,
734 BTN_LEFT, WLR_BUTTON_PRESSED); 745 BTN_LEFT, WLR_BUTTON_PRESSED);
735 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 746 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
736 transaction_commit_dirty();
737 } 747 }
738 } else { 748 } else {
739 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); 749 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state);
@@ -820,7 +830,6 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
820 break; 830 break;
821 } 831 }
822 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 832 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
823 transaction_commit_dirty();
824 return; 833 return;
825 } 834 }
826 835
@@ -837,8 +846,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
837 846
838 struct sway_container *con = view->container; 847 struct sway_container *con = view->container;
839 848
840 double sx = cursor->cursor->x - con->content_x + view->geometry.x; 849 double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x;
841 double sy = cursor->cursor->y - con->content_y + view->geometry.y; 850 double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y;
842 851
843 if (!pixman_region32_contains_point(region, 852 if (!pixman_region32_contains_point(region,
844 floor(sx), floor(sy), NULL)) { 853 floor(sx), floor(sy), NULL)) {
@@ -849,8 +858,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
849 double sy = (boxes[0].y1 + boxes[0].y2) / 2.; 858 double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
850 859
851 wlr_cursor_warp_closest(cursor->cursor, NULL, 860 wlr_cursor_warp_closest(cursor->cursor, NULL,
852 sx + con->content_x - view->geometry.x, 861 sx + con->pending.content_x - view->geometry.x,
853 sy + con->content_y - view->geometry.y); 862 sy + con->pending.content_y - view->geometry.y);
854 863
855 cursor_rebase(cursor); 864 cursor_rebase(cursor);
856 } 865 }
@@ -1051,6 +1060,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1051 wl_list_remove(&cursor->touch_down.link); 1060 wl_list_remove(&cursor->touch_down.link);
1052 wl_list_remove(&cursor->touch_up.link); 1061 wl_list_remove(&cursor->touch_up.link);
1053 wl_list_remove(&cursor->touch_motion.link); 1062 wl_list_remove(&cursor->touch_motion.link);
1063 wl_list_remove(&cursor->touch_frame.link);
1054 wl_list_remove(&cursor->tool_axis.link); 1064 wl_list_remove(&cursor->tool_axis.link);
1055 wl_list_remove(&cursor->tool_tip.link); 1065 wl_list_remove(&cursor->tool_tip.link);
1056 wl_list_remove(&cursor->tool_button.link); 1066 wl_list_remove(&cursor->tool_button.link);
@@ -1126,6 +1136,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1126 &cursor->touch_motion); 1136 &cursor->touch_motion);
1127 cursor->touch_motion.notify = handle_touch_motion; 1137 cursor->touch_motion.notify = handle_touch_motion;
1128 1138
1139 wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame);
1140 cursor->touch_frame.notify = handle_touch_frame;
1141
1129 wl_signal_add(&wlr_cursor->events.tablet_tool_axis, 1142 wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
1130 &cursor->tool_axis); 1143 &cursor->tool_axis);
1131 cursor->tool_axis.notify = handle_tool_axis; 1144 cursor->tool_axis.notify = handle_tool_axis;
@@ -1170,8 +1183,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
1170 return; 1183 return;
1171 } 1184 }
1172 1185
1173 double x = container->x + container->width / 2.0; 1186 double x = container->pending.x + container->pending.width / 2.0;
1174 double y = container->y + container->height / 2.0; 1187 double y = container->pending.y + container->pending.height / 2.0;
1175 1188
1176 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1189 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1177 cursor_unhide(cursor); 1190 cursor_unhide(cursor);
@@ -1284,8 +1297,8 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1284 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1297 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1285 struct sway_container *con = view->container; 1298 struct sway_container *con = view->container;
1286 1299
1287 double lx = sx + con->content_x - view->geometry.x; 1300 double lx = sx + con->pending.content_x - view->geometry.x;
1288 double ly = sy + con->content_y - view->geometry.y; 1301 double ly = sy + con->pending.content_y - view->geometry.y;
1289 1302
1290 wlr_cursor_warp(cursor->cursor, NULL, lx, ly); 1303 wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
1291 1304
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index ce259eb2..f258ac7d 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,6 +378,28 @@ 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_event_keyboard_key *event) {
384 struct sway_seat *seat = keyboard->seat_device->sway_seat; 405 struct sway_seat *seat = keyboard->seat_device->sway_seat;
@@ -489,18 +510,42 @@ static void handle_key_event(struct sway_keyboard *keyboard,
489 keyinfo.raw_keysyms_len); 510 keyinfo.raw_keysyms_len);
490 } 511 }
491 512
492 if (!handled || event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { 513 if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
514 // If the pressed event was sent to a client, also send the released
515 // event. In particular, don't send the released event to the IM grab.
493 bool pressed_sent = update_shortcut_state( 516 bool pressed_sent = update_shortcut_state(
494 &keyboard->state_pressed_sent, event->keycode, event->state, 517 &keyboard->state_pressed_sent, event->keycode,
495 keyinfo.keycode, 0); 518 event->state, keyinfo.keycode, 0);
496 if (pressed_sent || event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 519 if (pressed_sent) {
497 wlr_seat_set_keyboard(wlr_seat, wlr_device); 520 wlr_seat_set_keyboard(wlr_seat, wlr_device);
498 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 521 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
499 event->keycode, event->state); 522 event->keycode, event->state);
523 handled = true;
524 }
525 }
526
527 if (!handled) {
528 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
529
530 if (kb_grab) {
531 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,
532 wlr_device->keyboard);
533 wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
534 event->time_msec, event->keycode, event->state);
535 handled = true;
500 } 536 }
501 } 537 }
502 538
503 transaction_commit_dirty(); 539 if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) {
540 // If a released event failed pressed sent test, and not in sent to
541 // keyboard grab, it is still not handled. Don't handle released here.
542 update_shortcut_state(
543 &keyboard->state_pressed_sent, event->keycode, event->state,
544 keyinfo.keycode, 0);
545 wlr_seat_set_keyboard(wlr_seat, wlr_device);
546 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
547 event->keycode, event->state);
548 }
504 549
505 free(device_identifier); 550 free(device_identifier);
506} 551}
@@ -587,7 +632,6 @@ static int handle_keyboard_repeat(void *data) {
587 632
588 seat_execute_command(keyboard->seat_device->sway_seat, 633 seat_execute_command(keyboard->seat_device->sway_seat,
589 keyboard->repeat_binding); 634 keyboard->repeat_binding);
590 transaction_commit_dirty();
591 } 635 }
592 return 0; 636 return 0;
593} 637}
@@ -617,10 +661,19 @@ static void handle_modifier_event(struct sway_keyboard *keyboard) {
617 struct wlr_input_device *wlr_device = 661 struct wlr_input_device *wlr_device =
618 keyboard->seat_device->input_device->wlr_device; 662 keyboard->seat_device->input_device->wlr_device;
619 if (!wlr_device->keyboard->group) { 663 if (!wlr_device->keyboard->group) {
620 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 664 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
621 wlr_seat_set_keyboard(wlr_seat, wlr_device); 665
622 wlr_seat_keyboard_notify_modifiers(wlr_seat, 666 if (kb_grab) {
623 &wlr_device->keyboard->modifiers); 667 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,
668 wlr_device->keyboard);
669 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
670 &wlr_device->keyboard->modifiers);
671 } else {
672 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
673 wlr_seat_set_keyboard(wlr_seat, wlr_device);
674 wlr_seat_keyboard_notify_modifiers(wlr_seat,
675 &wlr_device->keyboard->modifiers);
676 }
624 677
625 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); 678 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
626 determine_bar_visibility(modifiers); 679 determine_bar_visibility(modifiers);
diff --git a/sway/input/libinput.c b/sway/input/libinput.c
index 54520f9e..060a584a 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,25 @@ 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-";
336 return strncmp(id_path, prefix, strlen(prefix)) == 0;
337}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1f5865ee..2d714acd 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -20,6 +20,7 @@
20#include "sway/input/cursor.h" 20#include "sway/input/cursor.h"
21#include "sway/input/input-manager.h" 21#include "sway/input/input-manager.h"
22#include "sway/input/keyboard.h" 22#include "sway/input/keyboard.h"
23#include "sway/input/libinput.h"
23#include "sway/input/seat.h" 24#include "sway/input/seat.h"
24#include "sway/input/switch.h" 25#include "sway/input/switch.h"
25#include "sway/input/tablet.h" 26#include "sway/input/tablet.h"
@@ -309,8 +310,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
309 // Setting focus_inactive 310 // Setting focus_inactive
310 focus = seat_get_focus_inactive(seat, &root->node); 311 focus = seat_get_focus_inactive(seat, &root->node);
311 seat_set_raw_focus(seat, next_focus); 312 seat_set_raw_focus(seat, next_focus);
312 if (focus->type == N_CONTAINER && focus->sway_container->workspace) { 313 if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) {
313 seat_set_raw_focus(seat, &focus->sway_container->workspace->node); 314 seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node);
314 } 315 }
315 seat_set_raw_focus(seat, focus); 316 seat_set_raw_focus(seat, focus);
316 } 317 }
@@ -666,6 +667,40 @@ static void seat_reset_input_config(struct sway_seat *seat,
666 sway_device->input_device->wlr_device, NULL); 667 sway_device->input_device->wlr_device, NULL);
667} 668}
668 669
670static bool has_prefix(const char *str, const char *prefix) {
671 return strncmp(str, prefix, strlen(prefix)) == 0;
672}
673
674/**
675 * Get the name of the built-in output, if any. Returns NULL if there isn't
676 * exactly one built-in output.
677 */
678static const char *get_builtin_output_name(void) {
679 const char *match = NULL;
680 for (int i = 0; i < root->outputs->length; ++i) {
681 struct sway_output *output = root->outputs->items[i];
682 const char *name = output->wlr_output->name;
683 if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") ||
684 has_prefix(name, "DSI-")) {
685 if (match != NULL) {
686 return NULL;
687 }
688 match = name;
689 }
690 }
691 return match;
692}
693
694static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {
695 switch (seat_device->input_device->wlr_device->type) {
696 case WLR_INPUT_DEVICE_TOUCH:
697 case WLR_INPUT_DEVICE_TABLET_TOOL:
698 return true;
699 default:
700 return false;
701 }
702}
703
669static void seat_apply_input_config(struct sway_seat *seat, 704static void seat_apply_input_config(struct sway_seat *seat,
670 struct sway_seat_device *sway_device) { 705 struct sway_seat_device *sway_device) {
671 struct input_config *ic = 706 struct input_config *ic =
@@ -681,7 +716,21 @@ static void seat_apply_input_config(struct sway_seat *seat,
681 716
682 switch (mapped_to) { 717 switch (mapped_to) {
683 case MAPPED_TO_DEFAULT: 718 case MAPPED_TO_DEFAULT:
719 /*
720 * If the wlroots backend provides an output name, use that.
721 *
722 * Otherwise, try to map built-in touch and tablet tool devices to the
723 * built-in output.
724 */
684 mapped_to_output = sway_device->input_device->wlr_device->output_name; 725 mapped_to_output = sway_device->input_device->wlr_device->output_name;
726 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
727 sway_libinput_device_is_builtin(sway_device->input_device)) {
728 mapped_to_output = get_builtin_output_name();
729 if (mapped_to_output) {
730 sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'",
731 mapped_to_output, sway_device->input_device->identifier);
732 }
733 }
685 if (mapped_to_output == NULL) { 734 if (mapped_to_output == NULL) {
686 return; 735 return;
687 } 736 }
@@ -1086,30 +1135,19 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1086 } 1135 }
1087 1136
1088 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? 1137 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
1089 node->sway_workspace : node->sway_container->workspace; 1138 node->sway_workspace : node->sway_container->pending.workspace;
1090 struct sway_container *container = node->type == N_CONTAINER ? 1139 struct sway_container *container = node->type == N_CONTAINER ?
1091 node->sway_container : NULL; 1140 node->sway_container : NULL;
1092 1141
1093 // Deny setting focus to a view which is hidden by a fullscreen container 1142 // Deny setting focus to a view which is hidden by a fullscreen container or global
1094 if (new_workspace && new_workspace->fullscreen && container && 1143 if (container && container_obstructing_fullscreen_container(container)) {
1095 !container_is_fullscreen_or_child(container)) { 1144 return;
1096 // Unless it's a transient container
1097 if (!container_is_transient_for(container, new_workspace->fullscreen)) {
1098 return;
1099 }
1100 } 1145 }
1146
1101 // Deny setting focus to a workspace node when using fullscreen global 1147 // Deny setting focus to a workspace node when using fullscreen global
1102 if (root->fullscreen_global && !container && new_workspace) { 1148 if (root->fullscreen_global && !container && new_workspace) {
1103 return; 1149 return;
1104 } 1150 }
1105 // Deny setting focus to a view which is hidden by a fullscreen global
1106 if (root->fullscreen_global && container != root->fullscreen_global &&
1107 !container_has_ancestor(container, root->fullscreen_global)) {
1108 // Unless it's a transient container
1109 if (!container_is_transient_for(container, root->fullscreen_global)) {
1110 return;
1111 }
1112 }
1113 1151
1114 struct sway_output *new_output = 1152 struct sway_output *new_output =
1115 new_workspace ? new_workspace->output : NULL; 1153 new_workspace ? new_workspace->output : NULL;
@@ -1135,10 +1173,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 1173 // Put the container parents on the focus stack, then the workspace, then
1136 // the focused container. 1174 // the focused container.
1137 if (container) { 1175 if (container) {
1138 struct sway_container *parent = container->parent; 1176 struct sway_container *parent = container->pending.parent;
1139 while (parent) { 1177 while (parent) {
1140 seat_set_raw_focus(seat, &parent->node); 1178 seat_set_raw_focus(seat, &parent->node);
1141 parent = parent->parent; 1179 parent = parent->pending.parent;
1142 } 1180 }
1143 } 1181 }
1144 if (new_workspace) { 1182 if (new_workspace) {
@@ -1234,6 +1272,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1234 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); 1272 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
1235 } 1273 }
1236 1274
1275 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1237 seat_tablet_pads_notify_enter(seat, surface); 1276 seat_tablet_pads_notify_enter(seat, surface);
1238} 1277}
1239 1278
@@ -1326,7 +1365,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
1326 struct sway_node *node = current->node; 1365 struct sway_node *node = current->node;
1327 if (node->type == N_CONTAINER && 1366 if (node->type == N_CONTAINER &&
1328 !container_is_floating_or_child(node->sway_container) && 1367 !container_is_floating_or_child(node->sway_container) &&
1329 node->sway_container->workspace == workspace) { 1368 node->sway_container->pending.workspace == workspace) {
1330 return node->sway_container; 1369 return node->sway_container;
1331 } 1370 }
1332 } 1371 }
@@ -1343,7 +1382,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
1343 struct sway_node *node = current->node; 1382 struct sway_node *node = current->node;
1344 if (node->type == N_CONTAINER && 1383 if (node->type == N_CONTAINER &&
1345 container_is_floating_or_child(node->sway_container) && 1384 container_is_floating_or_child(node->sway_container) &&
1346 node->sway_container->workspace == workspace) { 1385 node->sway_container->pending.workspace == workspace) {
1347 return node->sway_container; 1386 return node->sway_container;
1348 } 1387 }
1349 } 1388 }
@@ -1391,7 +1430,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) {
1391 return NULL; 1430 return NULL;
1392 } 1431 }
1393 if (focus->type == N_CONTAINER) { 1432 if (focus->type == N_CONTAINER) {
1394 return focus->sway_container->workspace; 1433 return focus->sway_container->pending.workspace;
1395 } 1434 }
1396 if (focus->type == N_WORKSPACE) { 1435 if (focus->type == N_WORKSPACE) {
1397 return focus->sway_workspace; 1436 return focus->sway_workspace;
@@ -1404,8 +1443,8 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) {
1404 wl_list_for_each(current, &seat->focus_stack, link) { 1443 wl_list_for_each(current, &seat->focus_stack, link) {
1405 struct sway_node *node = current->node; 1444 struct sway_node *node = current->node;
1406 if (node->type == N_CONTAINER && 1445 if (node->type == N_CONTAINER &&
1407 node->sway_container->workspace) { 1446 node->sway_container->pending.workspace) {
1408 return node->sway_container->workspace; 1447 return node->sway_container->pending.workspace;
1409 } else if (node->type == N_WORKSPACE) { 1448 } else if (node->type == N_WORKSPACE) {
1410 return node->sway_workspace; 1449 return node->sway_workspace;
1411 } 1450 }
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index a583ed62..4320a3b4 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) {
@@ -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}
@@ -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..ecc34fea 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -5,10 +5,14 @@
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};
@@ -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..8400a4b3 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}
@@ -116,23 +118,24 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
116 118
117 // Determine the amounts we need to bump everything relative to the current 119 // Determine the amounts we need to bump everything relative to the current
118 // size. 120 // size.
119 int relative_grow_width = width - con->width; 121 int relative_grow_width = width - con->pending.width;
120 int relative_grow_height = height - con->height; 122 int relative_grow_height = height - con->pending.height;
121 int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; 123 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; 124 int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y;
123 125
124 // Actually resize stuff 126 // Actually resize stuff
125 con->x += relative_grow_x; 127 con->pending.x += relative_grow_x;
126 con->y += relative_grow_y; 128 con->pending.y += relative_grow_y;
127 con->width += relative_grow_width; 129 con->pending.width += relative_grow_width;
128 con->height += relative_grow_height; 130 con->pending.height += relative_grow_height;
129 131
130 con->content_x += relative_grow_x; 132 con->pending.content_x += relative_grow_x;
131 con->content_y += relative_grow_y; 133 con->pending.content_y += relative_grow_y;
132 con->content_width += relative_grow_width; 134 con->pending.content_width += relative_grow_width;
133 con->content_height += relative_grow_height; 135 con->pending.content_height += relative_grow_height;
134 136
135 arrange_container(con); 137 arrange_container(con);
138 transaction_commit_dirty();
136} 139}
137 140
138static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 141static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -166,16 +169,17 @@ void seatop_begin_resize_floating(struct sway_seat *seat,
166 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; 169 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
167 e->ref_lx = seat->cursor->cursor->x; 170 e->ref_lx = seat->cursor->cursor->x;
168 e->ref_ly = seat->cursor->cursor->y; 171 e->ref_ly = seat->cursor->cursor->y;
169 e->ref_con_lx = con->x; 172 e->ref_con_lx = con->pending.x;
170 e->ref_con_ly = con->y; 173 e->ref_con_ly = con->pending.y;
171 e->ref_width = con->width; 174 e->ref_width = con->pending.width;
172 e->ref_height = con->height; 175 e->ref_height = con->pending.height;
173 176
174 seat->seatop_impl = &seatop_impl; 177 seat->seatop_impl = &seatop_impl;
175 seat->seatop_data = e; 178 seat->seatop_data = e;
176 179
177 container_set_resizing(con, true); 180 container_set_resizing(con, true);
178 container_raise_floating(con); 181 container_raise_floating(con);
182 transaction_commit_dirty();
179 183
180 const char *image = edge == WLR_EDGE_NONE ? 184 const char *image = edge == WLR_EDGE_NONE ?
181 "se-resize" : wlr_xcursor_get_resize_name(edge); 185 "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..9ea87a1a 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"
@@ -61,9 +60,6 @@ static void execute_binding(struct sway_switch *sway_switch) {
61 seat_execute_command(seat, dummy_binding); 60 seat_execute_command(seat, dummy_binding);
62 free(dummy_binding); 61 free(dummy_binding);
63 } 62 }
64
65 transaction_commit_dirty();
66
67} 63}
68 64
69static void handle_switch_toggle(struct wl_listener *listener, void *data) { 65static void handle_switch_toggle(struct wl_listener *listener, void *data) {
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index f83726ee..b8c19c17 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -55,6 +55,37 @@ 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 wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab,
81 &active_keyboard->modifiers);
82
83 wl_signal_add(&keyboard_grab->events.destroy,
84 &relay->input_method_keyboard_grab_destroy);
85 relay->input_method_keyboard_grab_destroy.notify =
86 handle_im_keyboard_grab_destroy;
87}
88
58static void text_input_set_pending_focused_surface( 89static void text_input_set_pending_focused_surface(
59 struct sway_text_input *text_input, struct wlr_surface *surface) { 90 struct sway_text_input *text_input, struct wlr_surface *surface) {
60 wl_list_remove(&text_input->pending_focused_surface_destroy.link); 91 wl_list_remove(&text_input->pending_focused_surface_destroy.link);
@@ -92,13 +123,18 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
92 return; 123 return;
93 } 124 }
94 // TODO: only send each of those if they were modified 125 // TODO: only send each of those if they were modified
95 wlr_input_method_v2_send_surrounding_text(input_method, 126 if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {
96 input->current.surrounding.text, input->current.surrounding.cursor, 127 wlr_input_method_v2_send_surrounding_text(input_method,
97 input->current.surrounding.anchor); 128 input->current.surrounding.text, input->current.surrounding.cursor,
129 input->current.surrounding.anchor);
130 }
98 wlr_input_method_v2_send_text_change_cause(input_method, 131 wlr_input_method_v2_send_text_change_cause(input_method,
99 input->current.text_change_cause); 132 input->current.text_change_cause);
100 wlr_input_method_v2_send_content_type(input_method, 133 if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) {
101 input->current.content_type.hint, input->current.content_type.purpose); 134 wlr_input_method_v2_send_content_type(input_method,
135 input->current.content_type.hint,
136 input->current.content_type.purpose);
137 }
102 wlr_input_method_v2_send_done(input_method); 138 wlr_input_method_v2_send_done(input_method);
103 // TODO: pass intent, display popup size 139 // TODO: pass intent, display popup size
104} 140}
@@ -144,6 +180,10 @@ static void handle_text_input_disable(struct wl_listener *listener,
144 void *data) { 180 void *data) {
145 struct sway_text_input *text_input = wl_container_of(listener, text_input, 181 struct sway_text_input *text_input = wl_container_of(listener, text_input,
146 text_input_disable); 182 text_input_disable);
183 if (text_input->input->focused_surface == NULL) {
184 sway_log(SWAY_DEBUG, "Disabling text input, but no longer focused");
185 return;
186 }
147 relay_disable_text_input(text_input->relay, text_input); 187 relay_disable_text_input(text_input->relay, text_input);
148} 188}
149 189
@@ -236,6 +276,9 @@ static void relay_handle_input_method(struct wl_listener *listener,
236 wl_signal_add(&relay->input_method->events.commit, 276 wl_signal_add(&relay->input_method->events.commit,
237 &relay->input_method_commit); 277 &relay->input_method_commit);
238 relay->input_method_commit.notify = handle_im_commit; 278 relay->input_method_commit.notify = handle_im_commit;
279 wl_signal_add(&relay->input_method->events.grab_keyboard,
280 &relay->input_method_grab_keyboard);
281 relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard;
239 wl_signal_add(&relay->input_method->events.destroy, 282 wl_signal_add(&relay->input_method->events.destroy,
240 &relay->input_method_destroy); 283 &relay->input_method_destroy);
241 relay->input_method_destroy.notify = handle_im_destroy; 284 relay->input_method_destroy.notify = handle_im_destroy;
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index fceee84d..1b64f86e 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,35 +226,25 @@ 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) {
231 json_object_object_add(object, "type", json_object_new_string("root"));
232}
233
234static void ipc_json_describe_output(struct sway_output *output, 245static void ipc_json_describe_output(struct sway_output *output,
235 json_object *object) { 246 json_object *object) {
236 struct wlr_output *wlr_output = output->wlr_output; 247 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)); 248 json_object_object_add(object, "active", json_object_new_boolean(true));
239 json_object_object_add(object, "dpms", 249 json_object_object_add(object, "dpms",
240 json_object_new_boolean(wlr_output->enabled)); 250 json_object_new_boolean(wlr_output->enabled));
@@ -369,11 +379,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
369 json_object_new_int(container->node.id)); 379 json_object_new_int(container->node.id));
370 } 380 }
371 381
372 json_object *workspace = ipc_json_create_node(i3_scratch_id, 382 json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace",
373 "__i3_scratch", false, workspace_focus, &box); 383 "__i3_scratch", false, workspace_focus, &box);
374 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); 384 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 385
378 // List all hidden scratchpad containers as floating nodes 386 // List all hidden scratchpad containers as floating nodes
379 json_object *floating_array = json_object_new_array(); 387 json_object *floating_array = json_object_new_array();
@@ -390,10 +398,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
390 json_object *output_focus = json_object_new_array(); 398 json_object *output_focus = json_object_new_array();
391 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); 399 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
392 400
393 json_object *output = ipc_json_create_node(i3_output_id, 401 json_object *output = ipc_json_create_node(i3_output_id, "output",
394 "__i3", false, output_focus, &box); 402 "__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", 403 json_object_object_add(output, "layout",
398 json_object_new_string("output")); 404 json_object_new_string("output"));
399 405
@@ -423,7 +429,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
423 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); 429 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1));
424 json_object_object_add(object, "output", workspace->output ? 430 json_object_object_add(object, "output", workspace->output ?
425 json_object_new_string(workspace->output->wlr_output->name) : NULL); 431 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", 432 json_object_object_add(object, "urgent",
428 json_object_new_boolean(workspace->urgent)); 433 json_object_new_boolean(workspace->urgent));
429 json_object_object_add(object, "representation", workspace->representation ? 434 json_object_object_add(object, "representation", workspace->representation ?
@@ -451,27 +456,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; 456 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED;
452 if (((!tab_or_stack || container_is_floating(c)) && 457 if (((!tab_or_stack || container_is_floating(c)) &&
453 c->current.border != B_NORMAL) || 458 c->current.border != B_NORMAL) ||
454 c->fullscreen_mode != FULLSCREEN_NONE || 459 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
455 c->workspace == NULL) { 460 c->pending.workspace == NULL) {
456 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; 461 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0;
457 return; 462 return;
458 } 463 }
459 464
460 if (c->parent) { 465 if (c->pending.parent) {
461 deco_rect->x = c->x - c->parent->x; 466 deco_rect->x = c->pending.x - c->pending.parent->pending.x;
462 deco_rect->y = c->y - c->parent->y; 467 deco_rect->y = c->pending.y - c->pending.parent->pending.y;
463 } else { 468 } else {
464 deco_rect->x = c->x - c->workspace->x; 469 deco_rect->x = c->pending.x - c->pending.workspace->x;
465 deco_rect->y = c->y - c->workspace->y; 470 deco_rect->y = c->pending.y - c->pending.workspace->y;
466 } 471 }
467 deco_rect->width = c->width; 472 deco_rect->width = c->pending.width;
468 deco_rect->height = container_titlebar_height(); 473 deco_rect->height = container_titlebar_height();
469 474
470 if (!container_is_floating(c)) { 475 if (!container_is_floating(c)) {
471 if (parent_layout == L_TABBED) { 476 if (parent_layout == L_TABBED) {
472 deco_rect->width = c->parent 477 deco_rect->width = c->pending.parent
473 ? c->parent->width / c->parent->children->length 478 ? c->pending.parent->pending.width / c->pending.parent->pending.children->length
474 : c->workspace->width / c->workspace->tiling->length; 479 : c->pending.workspace->width / c->pending.workspace->tiling->length;
475 deco_rect->x += deco_rect->width * container_sibling_index(c); 480 deco_rect->x += deco_rect->width * container_sibling_index(c);
476 } else if (parent_layout == L_STACKED) { 481 } else if (parent_layout == L_STACKED) {
477 if (!c->view) { 482 if (!c->view) {
@@ -494,10 +499,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)); 499 json_object_object_add(object, "visible", json_object_new_boolean(visible));
495 500
496 struct wlr_box window_box = { 501 struct wlr_box window_box = {
497 c->content_x - c->x, 502 c->pending.content_x - c->pending.x,
498 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 503 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0,
499 c->content_width, 504 c->pending.content_width,
500 c->content_height 505 c->pending.content_height
501 }; 506 };
502 507
503 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); 508 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box));
@@ -583,16 +588,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) { 588static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
584 json_object_object_add(object, "name", 589 json_object_object_add(object, "name",
585 c->title ? json_object_new_string(c->title) : NULL); 590 c->title ? json_object_new_string(c->title) : NULL);
586 json_object_object_add(object, "type", 591 if (container_is_floating(c)) {
587 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 592 json_object_object_add(object, "type",
593 json_object_new_string("floating_con"));
594 }
588 595
589 json_object_object_add(object, "layout", 596 json_object_object_add(object, "layout",
590 json_object_new_string( 597 json_object_new_string(
591 ipc_json_layout_description(c->layout))); 598 ipc_json_layout_description(c->pending.layout)));
592 599
593 json_object_object_add(object, "orientation", 600 json_object_object_add(object, "orientation",
594 json_object_new_string( 601 json_object_new_string(
595 ipc_json_orientation_description(c->layout))); 602 ipc_json_orientation_description(c->pending.layout)));
596 603
597 bool urgent = c->view ? 604 bool urgent = c->view ?
598 view_is_urgent(c->view) : container_has_urgent_child(c); 605 view_is_urgent(c->view) : container_has_urgent_child(c);
@@ -600,7 +607,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)); 607 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
601 608
602 json_object_object_add(object, "fullscreen_mode", 609 json_object_object_add(object, "fullscreen_mode",
603 json_object_new_int(c->fullscreen_mode)); 610 json_object_new_int(c->pending.fullscreen_mode));
604 611
605 struct sway_node *parent = node_get_parent(&c->node); 612 struct sway_node *parent = node_get_parent(&c->node);
606 struct wlr_box parent_box = {0, 0, 0, 0}; 613 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -610,8 +617,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
610 } 617 }
611 618
612 if (parent_box.width != 0 && parent_box.height != 0) { 619 if (parent_box.width != 0 && parent_box.height != 0) {
613 double percent = ((double)c->width / parent_box.width) 620 double percent = ((double)c->pending.width / parent_box.width)
614 * ((double)c->height / parent_box.height); 621 * ((double)c->pending.height / parent_box.height);
615 json_object_object_add(object, "percent", json_object_new_double(percent)); 622 json_object_object_add(object, "percent", json_object_new_double(percent));
616 } 623 }
617 624
@@ -692,12 +699,11 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
692 }; 699 };
693 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 700 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
694 701
695 json_object *object = ipc_json_create_node( 702 json_object *object = ipc_json_create_node((int)node->id,
696 (int)node->id, name, focused, focus, &box); 703 ipc_json_node_type_description(node->type), name, focused, focus, &box);
697 704
698 switch (node->type) { 705 switch (node->type) {
699 case N_ROOT: 706 case N_ROOT:
700 ipc_json_describe_root(root, object);
701 break; 707 break;
702 case N_OUTPUT: 708 case N_OUTPUT:
703 ipc_json_describe_output(node->sway_output, object); 709 ipc_json_describe_output(node->sway_output, object);
@@ -743,10 +749,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
743 } 749 }
744 break; 750 break;
745 case N_CONTAINER: 751 case N_CONTAINER:
746 if (node->sway_container->children) { 752 if (node->sway_container->pending.children) {
747 for (i = 0; i < node->sway_container->children->length; ++i) { 753 for (i = 0; i < node->sway_container->pending.children->length; ++i) {
748 struct sway_container *child = 754 struct sway_container *child =
749 node->sway_container->children->items[i]; 755 node->sway_container->pending.children->items[i];
750 json_object_array_add(children, 756 json_object_array_add(children,
751 ipc_json_describe_node_recursive(&child->node)); 757 ipc_json_describe_node_recursive(&child->node));
752 } 758 }
@@ -996,6 +1002,17 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
996 } 1002 }
997 } 1003 }
998 1004
1005 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1006 struct input_config *ic = input_device_get_config(device);
1007 float scroll_factor = 1.0f;
1008 if (ic != NULL && !isnan(ic->scroll_factor) &&
1009 ic->scroll_factor != FLT_MIN) {
1010 scroll_factor = ic->scroll_factor;
1011 }
1012 json_object_object_add(object, "scroll_factor",
1013 json_object_new_double(scroll_factor));
1014 }
1015
999 if (wlr_input_device_is_libinput(device->wlr_device)) { 1016 if (wlr_input_device_is_libinput(device->wlr_device)) {
1000 struct libinput_device *libinput_dev; 1017 struct libinput_device *libinput_dev;
1001 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1018 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
@@ -1109,7 +1126,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
1109 json_object_object_add(json, "verbose", 1126 json_object_object_add(json, "verbose",
1110 json_object_new_boolean(bar->verbose)); 1127 json_object_new_boolean(bar->verbose));
1111 json_object_object_add(json, "pango_markup", 1128 json_object_object_add(json, "pango_markup",
1112 json_object_new_boolean(bar->pango_markup)); 1129 json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT
1130 ? config->pango_markup
1131 : bar->pango_markup));
1113 1132
1114 json_object *colors = json_object_new_object(); 1133 json_object *colors = json_object_new_object();
1115 json_object_object_add(colors, "background", 1134 json_object_object_add(colors, "background",
diff --git a/sway/main.c b/sway/main.c
index 0c219fb3..e960c4e2 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -12,6 +12,7 @@
12#include <sys/un.h> 12#include <sys/un.h>
13#include <unistd.h> 13#include <unistd.h>
14#include <wlr/util/log.h> 14#include <wlr/util/log.h>
15#include <wlr/version.h>
15#include "sway/commands.h" 16#include "sway/commands.h"
16#include "sway/config.h" 17#include "sway/config.h"
17#include "sway/server.h" 18#include "sway/server.h"
@@ -46,43 +47,6 @@ void sig_handler(int signal) {
46 sway_terminate(EXIT_SUCCESS); 47 sway_terminate(EXIT_SUCCESS);
47} 48}
48 49
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) { 50void detect_proprietary(int allow_unsupported_gpu) {
87 FILE *f = fopen("/proc/modules", "r"); 51 FILE *f = fopen("/proc/modules", "r");
88 if (!f) { 52 if (!f) {
@@ -218,6 +182,8 @@ void enable_debug_flag(const char *flag) {
218 debug.txn_timings = true; 182 debug.txn_timings = true;
219 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 183 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
220 server.txn_timeout_ms = atoi(&flag[12]); 184 server.txn_timeout_ms = atoi(&flag[12]);
185 } else if (strcmp(flag, "noscanout") == 0) {
186 debug.noscanout = true;
221 } else { 187 } else {
222 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 188 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
223 } 189 }
@@ -245,7 +211,7 @@ static void handle_wlr_log(enum wlr_log_importance importance,
245int main(int argc, char **argv) { 211int main(int argc, char **argv) {
246 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 212 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0;
247 213
248 static struct option long_options[] = { 214 static const struct option long_options[] = {
249 {"help", no_argument, NULL, 'h'}, 215 {"help", no_argument, NULL, 'h'},
250 {"config", required_argument, NULL, 'c'}, 216 {"config", required_argument, NULL, 'c'},
251 {"validate", no_argument, NULL, 'C'}, 217 {"validate", no_argument, NULL, 'C'},
@@ -344,11 +310,11 @@ int main(int argc, char **argv) {
344 } 310 }
345 311
346 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); 312 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION);
313 sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR);
347 log_kernel(); 314 log_kernel();
348 log_distro(); 315 log_distro();
349 log_env(); 316 log_env();
350 detect_proprietary(allow_unsupported_gpu); 317 detect_proprietary(allow_unsupported_gpu);
351 detect_raspi();
352 318
353 if (optind < argc) { // Behave as IPC client 319 if (optind < argc) { // Behave as IPC client
354 if (optind != 1) { 320 if (optind != 1) {
diff --git a/sway/meson.build b/sway/meson.build
index 6e138101..1402db15 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -8,6 +8,7 @@ sway_sources = files(
8 'main.c', 8 'main.c',
9 'server.c', 9 'server.c',
10 'swaynag.c', 10 'swaynag.c',
11 'xdg_activation_v1.c',
11 'xdg_decoration.c', 12 'xdg_decoration.c',
12 13
13 'desktop/desktop.c', 14 'desktop/desktop.c',
@@ -204,9 +205,11 @@ sway_sources = files(
204 205
205sway_deps = [ 206sway_deps = [
206 cairo, 207 cairo,
208 drm,
207 jsonc, 209 jsonc,
208 libevdev, 210 libevdev,
209 libinput, 211 libinput,
212 libudev,
210 math, 213 math,
211 pango, 214 pango,
212 pcre, 215 pcre,
diff --git a/sway/server.c b/sway/server.c
index f51fcfe2..b187fcd5 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -9,9 +9,11 @@
9#include <wlr/backend/multi.h> 9#include <wlr/backend/multi.h>
10#include <wlr/backend/noop.h> 10#include <wlr/backend/noop.h>
11#include <wlr/backend/session.h> 11#include <wlr/backend/session.h>
12#include <wlr/config.h>
12#include <wlr/render/wlr_renderer.h> 13#include <wlr/render/wlr_renderer.h>
13#include <wlr/types/wlr_compositor.h> 14#include <wlr/types/wlr_compositor.h>
14#include <wlr/types/wlr_data_control_v1.h> 15#include <wlr/types/wlr_data_control_v1.h>
16#include <wlr/types/wlr_drm_lease_v1.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>
@@ -24,7 +26,11 @@
24#include <wlr/types/wlr_tablet_v2.h> 26#include <wlr/types/wlr_tablet_v2.h>
25#include <wlr/types/wlr_viewporter.h> 27#include <wlr/types/wlr_viewporter.h>
26#include <wlr/types/wlr_xcursor_manager.h> 28#include <wlr/types/wlr_xcursor_manager.h>
29#include <wlr/types/wlr_xdg_activation_v1.h>
27#include <wlr/types/wlr_xdg_decoration_v1.h> 30#include <wlr/types/wlr_xdg_decoration_v1.h>
31#include <wlr/types/wlr_xdg_foreign_registry.h>
32#include <wlr/types/wlr_xdg_foreign_v1.h>
33#include <wlr/types/wlr_xdg_foreign_v2.h>
28#include <wlr/types/wlr_xdg_output_v1.h> 34#include <wlr/types/wlr_xdg_output_v1.h>
29#include "config.h" 35#include "config.h"
30#include "list.h" 36#include "list.h"
@@ -52,6 +58,18 @@ bool server_privileged_prepare(struct sway_server *server) {
52 return true; 58 return true;
53} 59}
54 60
61static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
62 /* We only offer non-desktop outputs, but in the future we might want to do
63 * more logic here. */
64
65 struct wlr_drm_lease_request_v1 *req = data;
66 struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
67 if (!lease) {
68 sway_log(SWAY_ERROR, "Failed to grant lease request");
69 wlr_drm_lease_request_v1_reject(req);
70 }
71}
72
55bool server_init(struct sway_server *server) { 73bool server_init(struct sway_server *server) {
56 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 74 sway_log(SWAY_DEBUG, "Initializing Wayland server");
57 75
@@ -144,12 +162,34 @@ bool server_init(struct sway_server *server) {
144 server->foreign_toplevel_manager = 162 server->foreign_toplevel_manager =
145 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 163 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
146 164
165 server->drm_lease_manager=
166 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
167 if (server->drm_lease_manager) {
168 server->drm_lease_request.notify = handle_drm_lease_request;
169 wl_signal_add(&server->drm_lease_manager->events.request,
170 &server->drm_lease_request);
171 } else {
172 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
173 sway_log(SWAY_INFO, "VR will not be available");
174 }
175
147 wlr_export_dmabuf_manager_v1_create(server->wl_display); 176 wlr_export_dmabuf_manager_v1_create(server->wl_display);
148 wlr_screencopy_manager_v1_create(server->wl_display); 177 wlr_screencopy_manager_v1_create(server->wl_display);
149 wlr_data_control_manager_v1_create(server->wl_display); 178 wlr_data_control_manager_v1_create(server->wl_display);
150 wlr_primary_selection_v1_device_manager_create(server->wl_display); 179 wlr_primary_selection_v1_device_manager_create(server->wl_display);
151 wlr_viewporter_create(server->wl_display); 180 wlr_viewporter_create(server->wl_display);
152 181
182 struct wlr_xdg_foreign_registry *foreign_registry =
183 wlr_xdg_foreign_registry_create(server->wl_display);
184 wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);
185 wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry);
186
187 server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display);
188 server->xdg_activation_v1_request_activate.notify =
189 xdg_activation_v1_handle_request_activate;
190 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
191 &server->xdg_activation_v1_request_activate);
192
153 // Avoid using "wayland-0" as display socket 193 // Avoid using "wayland-0" as display socket
154 char name_candidate[16]; 194 char name_candidate[16];
155 for (int i = 1; i <= 32; ++i) { 195 for (int i = 1; i <= 32; ++i) {
@@ -186,7 +226,6 @@ bool server_init(struct sway_server *server) {
186 } 226 }
187 227
188 server->dirty_nodes = create_list(); 228 server->dirty_nodes = create_list();
189 server->transactions = create_list();
190 229
191 server->input = input_manager_create(server); 230 server->input = input_manager_create(server);
192 input_manager_get_default_seat(); // create seat0 231 input_manager_get_default_seat(); // create seat0
@@ -202,7 +241,6 @@ void server_fini(struct sway_server *server) {
202 wl_display_destroy_clients(server->wl_display); 241 wl_display_destroy_clients(server->wl_display);
203 wl_display_destroy(server->wl_display); 242 wl_display_destroy(server->wl_display);
204 list_free(server->dirty_nodes); 243 list_free(server->dirty_nodes);
205 list_free(server->transactions);
206} 244}
207 245
208bool server_start(struct sway_server *server) { 246bool server_start(struct sway_server *server) {
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..25f6de18 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
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 1b855959..d90fe97a 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*
@@ -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..55d8f719 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,7 +152,7 @@ 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.
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 02592b5f..202cd13f 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
@@ -215,7 +215,7 @@ set|plus|minus <amount>
215 If unspecified, the default is 10 pixels. Pixels are ignored when moving 215 If unspecified, the default is 10 pixels. Pixels are ignored when moving
216 tiled containers. 216 tiled containers.
217 217
218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] 218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
219 Moves the focused container to the specified position in the workspace. 219 Moves the focused container to the specified position in the workspace.
220 The position can be specified in pixels or percentage points, omitting 220 The position can be specified in pixels or percentage points, omitting
221 the unit defaults to pixels. If _absolute_ is used, the position is 221 the unit defaults to pixels. If _absolute_ is used, the position is
@@ -319,8 +319,10 @@ set|plus|minus <amount>
319 established by the *seat* subcommand of the same name. See 319 established by the *seat* subcommand of the same name. See
320 *sway-input*(5) for more ways to affect inhibitors. 320 *sway-input*(5) for more ways to affect inhibitors.
321 321
322*split* vertical|v|horizontal|h|toggle|t 322*split* vertical|v|horizontal|h|none|n|toggle|t
323 Splits the current container, vertically or horizontally. When _toggle_ is 323 Splits the current container, vertically or horizontally. When _none_ is
324 specified, the effect of a previous split is undone if the current
325 container is the only child of a split parent. When _toggle_ is
324 specified, the current container is split opposite to the parent 326 specified, the current container is split opposite to the parent
325 container's layout. 327 container's layout.
326 328
@@ -630,7 +632,7 @@ The default colors are:
630 should be used instead. Regardless of whether pango markup is enabled, 632 should be used instead. Regardless of whether pango markup is enabled,
631 _font_ should be specified as a pango font description. For more 633 _font_ should be specified as a pango font description. For more
632 information on pango font descriptions, see 634 information on pango font descriptions, see
633 https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 635 https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
634 636
635*force_display_urgency_hint* <timeout> [ms] 637*force_display_urgency_hint* <timeout> [ms]
636 If an application on another workspace sets an urgency hint, switching to this 638 If an application on another workspace sets an urgency hint, switching to this
diff --git a/sway/swaynag.c b/sway/swaynag.c
index db5a919a..ba582989 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -87,8 +87,8 @@ bool swaynag_spawn(const char *swaynag_command,
87 size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; 87 size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2;
88 char *cmd = malloc(length); 88 char *cmd = malloc(length);
89 snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); 89 snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args);
90 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 90 execlp("sh", "sh", "-c", cmd, NULL);
91 sway_log_errno(SWAY_ERROR, "execl failed"); 91 sway_log_errno(SWAY_ERROR, "execlp failed");
92 _exit(EXIT_FAILURE); 92 _exit(EXIT_FAILURE);
93 } 93 }
94 _exit(EXIT_SUCCESS); 94 _exit(EXIT_SUCCESS);
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index bac9f2fa..4aa82c35 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;
@@ -337,10 +337,10 @@ void arrange_root(void) {
337 337
338 if (root->fullscreen_global) { 338 if (root->fullscreen_global) {
339 struct sway_container *fs = root->fullscreen_global; 339 struct sway_container *fs = root->fullscreen_global;
340 fs->x = root->x; 340 fs->pending.x = root->x;
341 fs->y = root->y; 341 fs->pending.y = root->y;
342 fs->width = root->width; 342 fs->pending.width = root->width;
343 fs->height = root->height; 343 fs->pending.height = root->height;
344 arrange_container(fs); 344 arrange_container(fs);
345 } else { 345 } else {
346 for (int i = 0; i < root->outputs->length; ++i) { 346 for (int i = 0; i < root->outputs->length; ++i) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6a9ce1c4..6a01eab3 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,12 +1,13 @@
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>
7#include <wayland-server-core.h> 8#include <wayland-server-core.h>
8#include <wlr/types/wlr_output_layout.h> 9#include <wlr/types/wlr_output_layout.h>
9#include "cairo.h" 10#include "cairo_util.h"
10#include "pango.h" 11#include "pango.h"
11#include "sway/config.h" 12#include "sway/config.h"
12#include "sway/desktop.h" 13#include "sway/desktop.h"
@@ -19,6 +20,7 @@
19#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
20#include "sway/tree/view.h" 21#include "sway/tree/view.h"
21#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
23#include "sway/xdg_decoration.h"
22#include "list.h" 24#include "list.h"
23#include "log.h" 25#include "log.h"
24#include "stringop.h" 26#include "stringop.h"
@@ -30,12 +32,12 @@ struct sway_container *container_create(struct sway_view *view) {
30 return NULL; 32 return NULL;
31 } 33 }
32 node_init(&c->node, N_CONTAINER, c); 34 node_init(&c->node, N_CONTAINER, c);
33 c->layout = L_NONE; 35 c->pending.layout = L_NONE;
34 c->view = view; 36 c->view = view;
35 c->alpha = 1.0f; 37 c->alpha = 1.0f;
36 38
37 if (!view) { 39 if (!view) {
38 c->children = create_list(); 40 c->pending.children = create_list();
39 c->current.children = create_list(); 41 c->current.children = create_list();
40 } 42 }
41 c->marks = create_list(); 43 c->marks = create_list();
@@ -62,7 +64,7 @@ void container_destroy(struct sway_container *con) {
62 wlr_texture_destroy(con->title_focused_inactive); 64 wlr_texture_destroy(con->title_focused_inactive);
63 wlr_texture_destroy(con->title_unfocused); 65 wlr_texture_destroy(con->title_unfocused);
64 wlr_texture_destroy(con->title_urgent); 66 wlr_texture_destroy(con->title_urgent);
65 list_free(con->children); 67 list_free(con->pending.children);
66 list_free(con->current.children); 68 list_free(con->current.children);
67 list_free(con->outputs); 69 list_free(con->outputs);
68 70
@@ -90,10 +92,10 @@ void container_begin_destroy(struct sway_container *con) {
90 } 92 }
91 // The workspace must have the fullscreen pointer cleared so that the 93 // The workspace must have the fullscreen pointer cleared so that the
92 // seat code can find an appropriate new focus. 94 // seat code can find an appropriate new focus.
93 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { 95 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) {
94 con->workspace->fullscreen = NULL; 96 con->pending.workspace->fullscreen = NULL;
95 } 97 }
96 if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { 98 if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
97 container_fullscreen_disable(con); 99 container_fullscreen_disable(con);
98 } 100 }
99 101
@@ -108,11 +110,11 @@ void container_begin_destroy(struct sway_container *con) {
108 root_scratchpad_remove_container(con); 110 root_scratchpad_remove_container(con);
109 } 111 }
110 112
111 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 113 if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
112 container_fullscreen_disable(con); 114 container_fullscreen_disable(con);
113 } 115 }
114 116
115 if (con->parent || con->workspace) { 117 if (con->pending.parent || con->pending.workspace) {
116 container_detach(con); 118 container_detach(con);
117 } 119 }
118} 120}
@@ -121,12 +123,12 @@ void container_reap_empty(struct sway_container *con) {
121 if (con->view) { 123 if (con->view) {
122 return; 124 return;
123 } 125 }
124 struct sway_workspace *ws = con->workspace; 126 struct sway_workspace *ws = con->pending.workspace;
125 while (con) { 127 while (con) {
126 if (con->children->length) { 128 if (con->pending.children->length) {
127 return; 129 return;
128 } 130 }
129 struct sway_container *parent = con->parent; 131 struct sway_container *parent = con->pending.parent;
130 container_begin_destroy(con); 132 container_begin_destroy(con);
131 con = parent; 133 con = parent;
132 } 134 }
@@ -139,9 +141,9 @@ struct sway_container *container_flatten(struct sway_container *container) {
139 if (container->view) { 141 if (container->view) {
140 return NULL; 142 return NULL;
141 } 143 }
142 while (container && container->children->length == 1) { 144 while (container && container->pending.children->length == 1) {
143 struct sway_container *child = container->children->items[0]; 145 struct sway_container *child = container->pending.children->items[0];
144 struct sway_container *parent = container->parent; 146 struct sway_container *parent = container->pending.parent;
145 container_replace(container, child); 147 container_replace(container, child);
146 container_begin_destroy(container); 148 container_begin_destroy(container);
147 container = parent; 149 container = parent;
@@ -151,11 +153,11 @@ struct sway_container *container_flatten(struct sway_container *container) {
151 153
152struct sway_container *container_find_child(struct sway_container *container, 154struct sway_container *container_find_child(struct sway_container *container,
153 bool (*test)(struct sway_container *con, void *data), void *data) { 155 bool (*test)(struct sway_container *con, void *data), void *data) {
154 if (!container->children) { 156 if (!container->pending.children) {
155 return NULL; 157 return NULL;
156 } 158 }
157 for (int i = 0; i < container->children->length; ++i) { 159 for (int i = 0; i < container->pending.children->length; ++i) {
158 struct sway_container *child = container->children->items[i]; 160 struct sway_container *child = container->pending.children->items[i];
159 if (test(child, data)) { 161 if (test(child, data)) {
160 return child; 162 return child;
161 } 163 }
@@ -310,7 +312,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
310 return NULL; 312 return NULL;
311} 313}
312 314
313struct sway_container *view_container_at(struct sway_node *parent, 315static struct sway_container *view_container_content_at(struct sway_node *parent,
314 double lx, double ly, 316 double lx, double ly,
315 struct wlr_surface **surface, double *sx, double *sy) { 317 struct wlr_surface **surface, double *sx, double *sy) {
316 if (!sway_assert(node_is_view(parent), "Expected a view")) { 318 if (!sway_assert(node_is_view(parent), "Expected a view")) {
@@ -319,10 +321,33 @@ struct sway_container *view_container_at(struct sway_node *parent,
319 321
320 struct sway_container *container = parent->sway_container; 322 struct sway_container *container = parent->sway_container;
321 struct wlr_box box = { 323 struct wlr_box box = {
322 .x = container->x, 324 .x = container->pending.content_x,
323 .y = container->y, 325 .y = container->pending.content_y,
324 .width = container->width, 326 .width = container->pending.content_width,
325 .height = container->height, 327 .height = container->pending.content_height,
328 };
329
330 if (wlr_box_contains_point(&box, lx, ly)) {
331 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
332 return container;
333 }
334
335 return NULL;
336}
337
338static struct sway_container *view_container_at(struct sway_node *parent,
339 double lx, double ly,
340 struct wlr_surface **surface, double *sx, double *sy) {
341 if (!sway_assert(node_is_view(parent), "Expected a view")) {
342 return NULL;
343 }
344
345 struct sway_container *container = parent->sway_container;
346 struct wlr_box box = {
347 .x = container->pending.x,
348 .y = container->pending.y,
349 .width = container->pending.width,
350 .height = container->pending.height,
326 }; 351 };
327 352
328 if (wlr_box_contains_point(&box, lx, ly)) { 353 if (wlr_box_contains_point(&box, lx, ly)) {
@@ -394,7 +419,7 @@ struct sway_container *container_at(struct sway_workspace *workspace,
394 } 419 }
395 // Tiling (focused) 420 // Tiling (focused)
396 if (focus && focus->view && !is_floating) { 421 if (focus && focus->view && !is_floating) {
397 if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { 422 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
398 return c; 423 return c;
399 } 424 }
400 } 425 }
@@ -408,19 +433,41 @@ struct sway_container *container_at(struct sway_workspace *workspace,
408void container_for_each_child(struct sway_container *container, 433void container_for_each_child(struct sway_container *container,
409 void (*f)(struct sway_container *container, void *data), 434 void (*f)(struct sway_container *container, void *data),
410 void *data) { 435 void *data) {
411 if (container->children) { 436 if (container->pending.children) {
412 for (int i = 0; i < container->children->length; ++i) { 437 for (int i = 0; i < container->pending.children->length; ++i) {
413 struct sway_container *child = container->children->items[i]; 438 struct sway_container *child = container->pending.children->items[i];
414 f(child, data); 439 f(child, data);
415 container_for_each_child(child, f, data); 440 container_for_each_child(child, f, data);
416 } 441 }
417 } 442 }
418} 443}
419 444
445struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container)
446{
447 struct sway_workspace *workspace = container->pending.workspace;
448
449 if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) {
450 if (container_is_transient_for(container, workspace->fullscreen)) {
451 return NULL;
452 }
453 return workspace->fullscreen;
454 }
455
456 struct sway_container *fullscreen_global = root->fullscreen_global;
457 if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) {
458 if (container_is_transient_for(container, fullscreen_global)) {
459 return NULL;
460 }
461 return fullscreen_global;
462 }
463
464 return NULL;
465}
466
420bool container_has_ancestor(struct sway_container *descendant, 467bool container_has_ancestor(struct sway_container *descendant,
421 struct sway_container *ancestor) { 468 struct sway_container *ancestor) {
422 while (descendant) { 469 while (descendant) {
423 descendant = descendant->parent; 470 descendant = descendant->pending.parent;
424 if (descendant == ancestor) { 471 if (descendant == ancestor) {
425 return true; 472 return true;
426 } 473 }
@@ -446,23 +493,13 @@ struct sway_output *container_get_effective_output(struct sway_container *con) {
446 return con->outputs->items[con->outputs->length - 1]; 493 return con->outputs->items[con->outputs->length - 1];
447} 494}
448 495
449static void update_title_texture(struct sway_container *con, 496static void render_titlebar_text_texture(struct sway_output *output,
450 struct wlr_texture **texture, struct border_colors *class) { 497 struct sway_container *con, struct wlr_texture **texture,
451 struct sway_output *output = container_get_effective_output(con); 498 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; 499 double scale = output->wlr_output->scale;
464 int width = 0; 500 int width = 0;
465 int height = con->title_height * scale; 501 int height = config->font_height * scale;
502 int baseline;
466 503
467 // We must use a non-nil cairo_t for cairo_set_font_options to work. 504 // We must use a non-nil cairo_t for cairo_set_font_options to work.
468 // Therefore, we cannot use cairo_create(NULL). 505 // Therefore, we cannot use cairo_create(NULL).
@@ -480,11 +517,19 @@ static void update_title_texture(struct sway_container *con,
480 to_cairo_subpixel_order(output->wlr_output->subpixel)); 517 to_cairo_subpixel_order(output->wlr_output->subpixel));
481 } 518 }
482 cairo_set_font_options(c, fo); 519 cairo_set_font_options(c, fo);
483 get_text_size(c, config->font, &width, NULL, NULL, scale, 520 get_text_size(c, config->font, &width, NULL, &baseline, scale,
484 config->pango_markup, "%s", con->formatted_title); 521 config->pango_markup, "%s", text);
485 cairo_surface_destroy(dummy_surface); 522 cairo_surface_destroy(dummy_surface);
486 cairo_destroy(c); 523 cairo_destroy(c);
487 524
525 if (width == 0 || height == 0) {
526 return;
527 }
528
529 if (height > config->font_height * scale) {
530 height = config->font_height * scale;
531 }
532
488 cairo_surface_t *surface = cairo_image_surface_create( 533 cairo_surface_t *surface = cairo_image_surface_create(
489 CAIRO_FORMAT_ARGB32, width, height); 534 CAIRO_FORMAT_ARGB32, width, height);
490 cairo_t *cairo = cairo_create(surface); 535 cairo_t *cairo = cairo_create(surface);
@@ -497,23 +542,40 @@ static void update_title_texture(struct sway_container *con,
497 PangoContext *pango = pango_cairo_create_context(cairo); 542 PangoContext *pango = pango_cairo_create_context(cairo);
498 cairo_set_source_rgba(cairo, class->text[0], class->text[1], 543 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
499 class->text[2], class->text[3]); 544 class->text[2], class->text[3]);
500 cairo_move_to(cairo, 0, 0); 545 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
501 546
502 pango_printf(cairo, config->font, scale, config->pango_markup, 547 render_text(cairo, config->font, scale, pango_markup, "%s", text);
503 "%s", con->formatted_title);
504 548
505 cairo_surface_flush(surface); 549 cairo_surface_flush(surface);
506 unsigned char *data = cairo_image_surface_get_data(surface); 550 unsigned char *data = cairo_image_surface_get_data(surface);
507 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 551 int stride = cairo_image_surface_get_stride(surface);
508 struct wlr_renderer *renderer = wlr_backend_get_renderer( 552 struct wlr_renderer *renderer = wlr_backend_get_renderer(
509 output->wlr_output->backend); 553 output->wlr_output->backend);
510 *texture = wlr_texture_from_pixels( 554 *texture = wlr_texture_from_pixels(
511 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 555 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
512 cairo_surface_destroy(surface); 556 cairo_surface_destroy(surface);
513 g_object_unref(pango); 557 g_object_unref(pango);
514 cairo_destroy(cairo); 558 cairo_destroy(cairo);
515} 559}
516 560
561static void update_title_texture(struct sway_container *con,
562 struct wlr_texture **texture, struct border_colors *class) {
563 struct sway_output *output = container_get_effective_output(con);
564 if (!output) {
565 return;
566 }
567 if (*texture) {
568 wlr_texture_destroy(*texture);
569 *texture = NULL;
570 }
571 if (!con->formatted_title) {
572 return;
573 }
574
575 render_titlebar_text_texture(output, con, texture, class,
576 config->pango_markup, con->formatted_title);
577}
578
517void container_update_title_textures(struct sway_container *container) { 579void container_update_title_textures(struct sway_container *container) {
518 update_title_texture(container, &container->title_focused, 580 update_title_texture(container, &container->title_focused,
519 &config->border_colors.focused); 581 &config->border_colors.focused);
@@ -526,21 +588,6 @@ void container_update_title_textures(struct sway_container *container) {
526 container_damage_whole(container); 588 container_damage_whole(container);
527} 589}
528 590
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/** 591/**
545 * Calculate and return the length of the tree representation. 592 * Calculate and return the length of the tree representation.
546 * An example tree representation is: V[Terminal, Firefox] 593 * An example tree representation is: V[Terminal, Firefox]
@@ -596,23 +643,22 @@ size_t container_build_representation(enum sway_container_layout layout,
596 643
597void container_update_representation(struct sway_container *con) { 644void container_update_representation(struct sway_container *con) {
598 if (!con->view) { 645 if (!con->view) {
599 size_t len = container_build_representation(con->layout, 646 size_t len = container_build_representation(con->pending.layout,
600 con->children, NULL); 647 con->pending.children, NULL);
601 free(con->formatted_title); 648 free(con->formatted_title);
602 con->formatted_title = calloc(len + 1, sizeof(char)); 649 con->formatted_title = calloc(len + 1, sizeof(char));
603 if (!sway_assert(con->formatted_title, 650 if (!sway_assert(con->formatted_title,
604 "Unable to allocate title string")) { 651 "Unable to allocate title string")) {
605 return; 652 return;
606 } 653 }
607 container_build_representation(con->layout, con->children, 654 container_build_representation(con->pending.layout, con->pending.children,
608 con->formatted_title); 655 con->formatted_title);
609 container_calculate_title_height(con);
610 container_update_title_textures(con); 656 container_update_title_textures(con);
611 } 657 }
612 if (con->parent) { 658 if (con->pending.parent) {
613 container_update_representation(con->parent); 659 container_update_representation(con->pending.parent);
614 } else if (con->workspace) { 660 } else if (con->pending.workspace) {
615 workspace_update_representation(con->workspace); 661 workspace_update_representation(con->pending.workspace);
616 } 662 }
617} 663}
618 664
@@ -663,20 +709,20 @@ static void floating_natural_resize(struct sway_container *con) {
663 floating_calculate_constraints(&min_width, &max_width, 709 floating_calculate_constraints(&min_width, &max_width,
664 &min_height, &max_height); 710 &min_height, &max_height);
665 if (!con->view) { 711 if (!con->view) {
666 con->width = fmax(min_width, fmin(con->width, max_width)); 712 con->pending.width = fmax(min_width, fmin(con->pending.width, max_width));
667 con->height = fmax(min_height, fmin(con->height, max_height)); 713 con->pending.height = fmax(min_height, fmin(con->pending.height, max_height));
668 } else { 714 } else {
669 struct sway_view *view = con->view; 715 struct sway_view *view = con->view;
670 con->content_width = 716 con->pending.content_width =
671 fmax(min_width, fmin(view->natural_width, max_width)); 717 fmax(min_width, fmin(view->natural_width, max_width));
672 con->content_height = 718 con->pending.content_height =
673 fmax(min_height, fmin(view->natural_height, max_height)); 719 fmax(min_height, fmin(view->natural_height, max_height));
674 container_set_geometry_from_content(con); 720 container_set_geometry_from_content(con);
675 } 721 }
676} 722}
677 723
678void container_floating_resize_and_center(struct sway_container *con) { 724void container_floating_resize_and_center(struct sway_container *con) {
679 struct sway_workspace *ws = con->workspace; 725 struct sway_workspace *ws = con->pending.workspace;
680 if (!ws) { 726 if (!ws) {
681 // On scratchpad, just resize 727 // On scratchpad, just resize
682 floating_natural_resize(con); 728 floating_natural_resize(con);
@@ -687,42 +733,42 @@ void container_floating_resize_and_center(struct sway_container *con) {
687 ws->output->wlr_output); 733 ws->output->wlr_output);
688 if (!ob) { 734 if (!ob) {
689 // On NOOP output. Will be called again when moved to an output 735 // On NOOP output. Will be called again when moved to an output
690 con->x = 0; 736 con->pending.x = 0;
691 con->y = 0; 737 con->pending.y = 0;
692 con->width = 0; 738 con->pending.width = 0;
693 con->height = 0; 739 con->pending.height = 0;
694 return; 740 return;
695 } 741 }
696 742
697 floating_natural_resize(con); 743 floating_natural_resize(con);
698 if (!con->view) { 744 if (!con->view) {
699 if (con->width > ws->width || con->height > ws->height) { 745 if (con->pending.width > ws->width || con->pending.height > ws->height) {
700 con->x = ob->x + (ob->width - con->width) / 2; 746 con->pending.x = ob->x + (ob->width - con->pending.width) / 2;
701 con->y = ob->y + (ob->height - con->height) / 2; 747 con->pending.y = ob->y + (ob->height - con->pending.height) / 2;
702 } else { 748 } else {
703 con->x = ws->x + (ws->width - con->width) / 2; 749 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
704 con->y = ws->y + (ws->height - con->height) / 2; 750 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
705 } 751 }
706 } else { 752 } else {
707 if (con->content_width > ws->width 753 if (con->pending.content_width > ws->width
708 || con->content_height > ws->height) { 754 || con->pending.content_height > ws->height) {
709 con->content_x = ob->x + (ob->width - con->content_width) / 2; 755 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; 756 con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2;
711 } else { 757 } else {
712 con->content_x = ws->x + (ws->width - con->content_width) / 2; 758 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; 759 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
714 } 760 }
715 761
716 // If the view's border is B_NONE then these properties are ignored. 762 // If the view's border is B_NONE then these properties are ignored.
717 con->border_top = con->border_bottom = true; 763 con->pending.border_top = con->pending.border_bottom = true;
718 con->border_left = con->border_right = true; 764 con->pending.border_left = con->pending.border_right = true;
719 765
720 container_set_geometry_from_content(con); 766 container_set_geometry_from_content(con);
721 } 767 }
722} 768}
723 769
724void container_floating_set_default_size(struct sway_container *con) { 770void container_floating_set_default_size(struct sway_container *con) {
725 if (!sway_assert(con->workspace, "Expected a container on a workspace")) { 771 if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) {
726 return; 772 return;
727 } 773 }
728 774
@@ -730,16 +776,16 @@ void container_floating_set_default_size(struct sway_container *con) {
730 floating_calculate_constraints(&min_width, &max_width, 776 floating_calculate_constraints(&min_width, &max_width,
731 &min_height, &max_height); 777 &min_height, &max_height);
732 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 778 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
733 workspace_get_box(con->workspace, box); 779 workspace_get_box(con->pending.workspace, box);
734 780
735 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 781 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)); 782 double height = fmax(min_height, fmin(box->height * 0.75, max_height));
737 if (!con->view) { 783 if (!con->view) {
738 con->width = width; 784 con->pending.width = width;
739 con->height = height; 785 con->pending.height = height;
740 } else { 786 } else {
741 con->content_width = width; 787 con->pending.content_width = width;
742 con->content_height = height; 788 con->pending.content_height = height;
743 container_set_geometry_from_content(con); 789 container_set_geometry_from_content(con);
744 } 790 }
745 791
@@ -761,8 +807,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) {
761 con->view->impl->set_resizing(con->view, resizing); 807 con->view->impl->set_resizing(con->view, resizing);
762 } 808 }
763 } else { 809 } else {
764 for (int i = 0; i < con->children->length; ++i ) { 810 for (int i = 0; i < con->pending.children->length; ++i ) {
765 struct sway_container *child = con->children->items[i]; 811 struct sway_container *child = con->pending.children->items[i];
766 container_set_resizing(child, resizing); 812 container_set_resizing(child, resizing);
767 } 813 }
768 } 814 }
@@ -774,21 +820,33 @@ void container_set_floating(struct sway_container *container, bool enable) {
774 } 820 }
775 821
776 struct sway_seat *seat = input_manager_current_seat(); 822 struct sway_seat *seat = input_manager_current_seat();
777 struct sway_workspace *workspace = container->workspace; 823 struct sway_workspace *workspace = container->pending.workspace;
824 struct sway_container *focus = seat_get_focused_container(seat);
825 bool set_focus = focus == container;
778 826
779 if (enable) { 827 if (enable) {
780 struct sway_container *old_parent = container->parent; 828 struct sway_container *old_parent = container->pending.parent;
781 container_detach(container); 829 container_detach(container);
782 workspace_add_floating(workspace, container); 830 workspace_add_floating(workspace, container);
783 if (container->view) { 831 if (container->view) {
784 view_set_tiled(container->view, false); 832 view_set_tiled(container->view, false);
785 if (container->view->using_csd) { 833 if (container->view->using_csd) {
786 container->border = B_CSD; 834 container->saved_border = container->pending.border;
835 container->pending.border = B_CSD;
836 if (container->view->xdg_decoration) {
837 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
838 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
839 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
840 }
787 } 841 }
788 } 842 }
789 container_floating_set_default_size(container); 843 container_floating_set_default_size(container);
790 container_floating_resize_and_center(container); 844 container_floating_resize_and_center(container);
791 if (old_parent) { 845 if (old_parent) {
846 if (set_focus) {
847 seat_set_raw_focus(seat, &old_parent->node);
848 seat_set_raw_focus(seat, &container->node);
849 }
792 container_reap_empty(old_parent); 850 container_reap_empty(old_parent);
793 } 851 }
794 } else { 852 } else {
@@ -800,19 +858,28 @@ void container_set_floating(struct sway_container *container, bool enable) {
800 struct sway_container *reference = 858 struct sway_container *reference =
801 seat_get_focus_inactive_tiling(seat, workspace); 859 seat_get_focus_inactive_tiling(seat, workspace);
802 if (reference) { 860 if (reference) {
803 container_add_sibling(reference, container, 1); 861 if (reference->view) {
804 container->width = reference->width; 862 container_add_sibling(reference, container, 1);
805 container->height = reference->height; 863 } else {
864 container_add_child(reference, container);
865 }
866 container->pending.width = reference->pending.width;
867 container->pending.height = reference->pending.height;
806 } else { 868 } else {
807 struct sway_container *other = 869 struct sway_container *other =
808 workspace_add_tiling(workspace, container); 870 workspace_add_tiling(workspace, container);
809 other->width = workspace->width; 871 other->pending.width = workspace->width;
810 other->height = workspace->height; 872 other->pending.height = workspace->height;
811 } 873 }
812 if (container->view) { 874 if (container->view) {
813 view_set_tiled(container->view, true); 875 view_set_tiled(container->view, true);
814 if (container->view->using_csd) { 876 if (container->view->using_csd) {
815 container->border = container->saved_border; 877 container->pending.border = container->saved_border;
878 if (container->view->xdg_decoration) {
879 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
880 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
881 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
882 }
816 } 883 }
817 } 884 }
818 container->width_fraction = 0; 885 container->width_fraction = 0;
@@ -834,22 +901,33 @@ void container_set_geometry_from_content(struct sway_container *con) {
834 size_t border_width = 0; 901 size_t border_width = 0;
835 size_t top = 0; 902 size_t top = 0;
836 903
837 if (con->border != B_CSD) { 904 if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) {
838 border_width = con->border_thickness * (con->border != B_NONE); 905 border_width = con->pending.border_thickness * (con->pending.border != B_NONE);
839 top = con->border == B_NORMAL ? 906 top = con->pending.border == B_NORMAL ?
840 container_titlebar_height() : border_width; 907 container_titlebar_height() : border_width;
841 } 908 }
842 909
843 con->x = con->content_x - border_width; 910 con->pending.x = con->pending.content_x - border_width;
844 con->y = con->content_y - top; 911 con->pending.y = con->pending.content_y - top;
845 con->width = con->content_width + border_width * 2; 912 con->pending.width = con->pending.content_width + border_width * 2;
846 con->height = top + con->content_height + border_width; 913 con->pending.height = top + con->pending.content_height + border_width;
847 node_set_dirty(&con->node); 914 node_set_dirty(&con->node);
848} 915}
849 916
850bool container_is_floating(struct sway_container *container) { 917bool container_is_floating(struct sway_container *container) {
851 if (!container->parent && container->workspace && 918 if (!container->pending.parent && container->pending.workspace &&
852 list_find(container->workspace->floating, container) != -1) { 919 list_find(container->pending.workspace->floating, container) != -1) {
920 return true;
921 }
922 if (container->scratchpad) {
923 return true;
924 }
925 return false;
926}
927
928bool container_is_current_floating(struct sway_container *container) {
929 if (!container->current.parent && container->current.workspace &&
930 list_find(container->current.workspace->floating, container) != -1) {
853 return true; 931 return true;
854 } 932 }
855 if (container->scratchpad) { 933 if (container->scratchpad) {
@@ -859,10 +937,10 @@ bool container_is_floating(struct sway_container *container) {
859} 937}
860 938
861void container_get_box(struct sway_container *container, struct wlr_box *box) { 939void container_get_box(struct sway_container *container, struct wlr_box *box) {
862 box->x = container->x; 940 box->x = container->pending.x;
863 box->y = container->y; 941 box->y = container->pending.y;
864 box->width = container->width; 942 box->width = container->pending.width;
865 box->height = container->height; 943 box->height = container->pending.height;
866} 944}
867 945
868/** 946/**
@@ -870,14 +948,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
870 */ 948 */
871void container_floating_translate(struct sway_container *con, 949void container_floating_translate(struct sway_container *con,
872 double x_amount, double y_amount) { 950 double x_amount, double y_amount) {
873 con->x += x_amount; 951 con->pending.x += x_amount;
874 con->y += y_amount; 952 con->pending.y += y_amount;
875 con->content_x += x_amount; 953 con->pending.content_x += x_amount;
876 con->content_y += y_amount; 954 con->pending.content_y += y_amount;
877 955
878 if (con->children) { 956 if (con->pending.children) {
879 for (int i = 0; i < con->children->length; ++i) { 957 for (int i = 0; i < con->pending.children->length; ++i) {
880 struct sway_container *child = con->children->items[i]; 958 struct sway_container *child = con->pending.children->items[i];
881 container_floating_translate(child, x_amount, y_amount); 959 container_floating_translate(child, x_amount, y_amount);
882 } 960 }
883 } 961 }
@@ -893,8 +971,8 @@ void container_floating_translate(struct sway_container *con,
893 * center. 971 * center.
894 */ 972 */
895struct sway_output *container_floating_find_output(struct sway_container *con) { 973struct sway_output *container_floating_find_output(struct sway_container *con) {
896 double center_x = con->x + con->width / 2; 974 double center_x = con->pending.x + con->pending.width / 2;
897 double center_y = con->y + con->height / 2; 975 double center_y = con->pending.y + con->pending.height / 2;
898 struct sway_output *closest_output = NULL; 976 struct sway_output *closest_output = NULL;
899 double closest_distance = DBL_MAX; 977 double closest_distance = DBL_MAX;
900 for (int i = 0; i < root->outputs->length; ++i) { 978 for (int i = 0; i < root->outputs->length; ++i) {
@@ -925,11 +1003,11 @@ void container_floating_move_to(struct sway_container *con,
925 "Expected a floating container")) { 1003 "Expected a floating container")) {
926 return; 1004 return;
927 } 1005 }
928 container_floating_translate(con, lx - con->x, ly - con->y); 1006 container_floating_translate(con, lx - con->pending.x, ly - con->pending.y);
929 if (container_is_scratchpad_hidden(con)) { 1007 if (container_is_scratchpad_hidden(con)) {
930 return; 1008 return;
931 } 1009 }
932 struct sway_workspace *old_workspace = con->workspace; 1010 struct sway_workspace *old_workspace = con->pending.workspace;
933 struct sway_output *new_output = container_floating_find_output(con); 1011 struct sway_output *new_output = container_floating_find_output(con);
934 if (!sway_assert(new_output, "Unable to find any output")) { 1012 if (!sway_assert(new_output, "Unable to find any output")) {
935 return; 1013 return;
@@ -951,10 +1029,10 @@ void container_floating_move_to_center(struct sway_container *con) {
951 "Expected a floating container")) { 1029 "Expected a floating container")) {
952 return; 1030 return;
953 } 1031 }
954 struct sway_workspace *ws = con->workspace; 1032 struct sway_workspace *ws = con->pending.workspace;
955 double new_lx = ws->x + (ws->width - con->width) / 2; 1033 double new_lx = ws->x + (ws->width - con->pending.width) / 2;
956 double new_ly = ws->y + (ws->height - con->height) / 2; 1034 double new_ly = ws->y + (ws->height - con->pending.height) / 2;
957 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 1035 container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y);
958} 1036}
959 1037
960static bool find_urgent_iterator(struct sway_container *con, void *data) { 1038static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -972,42 +1050,39 @@ void container_end_mouse_operation(struct sway_container *container) {
972 } 1050 }
973} 1051}
974 1052
975static void set_fullscreen_iterator(struct sway_container *con, void *data) { 1053static void set_fullscreen(struct sway_container *con, bool enable) {
976 if (!con->view) { 1054 if (!con->view) {
977 return; 1055 return;
978 } 1056 }
979 if (con->view->impl->set_fullscreen) { 1057 if (con->view->impl->set_fullscreen) {
980 bool *enable = data; 1058 con->view->impl->set_fullscreen(con->view, enable);
981 con->view->impl->set_fullscreen(con->view, *enable);
982 if (con->view->foreign_toplevel) { 1059 if (con->view->foreign_toplevel) {
983 wlr_foreign_toplevel_handle_v1_set_fullscreen( 1060 wlr_foreign_toplevel_handle_v1_set_fullscreen(
984 con->view->foreign_toplevel, *enable); 1061 con->view->foreign_toplevel, enable);
985 } 1062 }
986 } 1063 }
987} 1064}
988 1065
989static void container_fullscreen_workspace(struct sway_container *con) { 1066static void container_fullscreen_workspace(struct sway_container *con) {
990 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1067 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
991 "Expected a non-fullscreen container")) { 1068 "Expected a non-fullscreen container")) {
992 return; 1069 return;
993 } 1070 }
994 bool enable = true; 1071 set_fullscreen(con, true);
995 set_fullscreen_iterator(con, &enable); 1072 con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;
996 container_for_each_child(con, set_fullscreen_iterator, &enable);
997 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
998 1073
999 con->saved_x = con->x; 1074 con->saved_x = con->pending.x;
1000 con->saved_y = con->y; 1075 con->saved_y = con->pending.y;
1001 con->saved_width = con->width; 1076 con->saved_width = con->pending.width;
1002 con->saved_height = con->height; 1077 con->saved_height = con->pending.height;
1003 1078
1004 if (con->workspace) { 1079 if (con->pending.workspace) {
1005 con->workspace->fullscreen = con; 1080 con->pending.workspace->fullscreen = con;
1006 struct sway_seat *seat; 1081 struct sway_seat *seat;
1007 struct sway_workspace *focus_ws; 1082 struct sway_workspace *focus_ws;
1008 wl_list_for_each(seat, &server.input->seats, link) { 1083 wl_list_for_each(seat, &server.input->seats, link) {
1009 focus_ws = seat_get_focused_workspace(seat); 1084 focus_ws = seat_get_focused_workspace(seat);
1010 if (focus_ws == con->workspace) { 1085 if (focus_ws == con->pending.workspace) {
1011 seat_set_focus_container(seat, con); 1086 seat_set_focus_container(seat, con);
1012 } else { 1087 } else {
1013 struct sway_node *focus = 1088 struct sway_node *focus =
@@ -1023,19 +1098,17 @@ static void container_fullscreen_workspace(struct sway_container *con) {
1023} 1098}
1024 1099
1025static void container_fullscreen_global(struct sway_container *con) { 1100static void container_fullscreen_global(struct sway_container *con) {
1026 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1101 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
1027 "Expected a non-fullscreen container")) { 1102 "Expected a non-fullscreen container")) {
1028 return; 1103 return;
1029 } 1104 }
1030 bool enable = true; 1105 set_fullscreen(con, true);
1031 set_fullscreen_iterator(con, &enable);
1032 container_for_each_child(con, set_fullscreen_iterator, &enable);
1033 1106
1034 root->fullscreen_global = con; 1107 root->fullscreen_global = con;
1035 con->saved_x = con->x; 1108 con->saved_x = con->pending.x;
1036 con->saved_y = con->y; 1109 con->saved_y = con->pending.y;
1037 con->saved_width = con->width; 1110 con->saved_width = con->pending.width;
1038 con->saved_height = con->height; 1111 con->saved_height = con->pending.height;
1039 1112
1040 struct sway_seat *seat; 1113 struct sway_seat *seat;
1041 wl_list_for_each(seat, &server.input->seats, link) { 1114 wl_list_for_each(seat, &server.input->seats, link) {
@@ -1045,34 +1118,32 @@ static void container_fullscreen_global(struct sway_container *con) {
1045 } 1118 }
1046 } 1119 }
1047 1120
1048 con->fullscreen_mode = FULLSCREEN_GLOBAL; 1121 con->pending.fullscreen_mode = FULLSCREEN_GLOBAL;
1049 container_end_mouse_operation(con); 1122 container_end_mouse_operation(con);
1050 ipc_event_window(con, "fullscreen_mode"); 1123 ipc_event_window(con, "fullscreen_mode");
1051} 1124}
1052 1125
1053void container_fullscreen_disable(struct sway_container *con) { 1126void container_fullscreen_disable(struct sway_container *con) {
1054 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, 1127 if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,
1055 "Expected a fullscreen container")) { 1128 "Expected a fullscreen container")) {
1056 return; 1129 return;
1057 } 1130 }
1058 bool enable = false; 1131 set_fullscreen(con, false);
1059 set_fullscreen_iterator(con, &enable);
1060 container_for_each_child(con, set_fullscreen_iterator, &enable);
1061 1132
1062 if (container_is_floating(con)) { 1133 if (container_is_floating(con)) {
1063 con->x = con->saved_x; 1134 con->pending.x = con->saved_x;
1064 con->y = con->saved_y; 1135 con->pending.y = con->saved_y;
1065 con->width = con->saved_width; 1136 con->pending.width = con->saved_width;
1066 con->height = con->saved_height; 1137 con->pending.height = con->saved_height;
1067 } 1138 }
1068 1139
1069 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1140 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1070 if (con->workspace) { 1141 if (con->pending.workspace) {
1071 con->workspace->fullscreen = NULL; 1142 con->pending.workspace->fullscreen = NULL;
1072 if (container_is_floating(con)) { 1143 if (container_is_floating(con)) {
1073 struct sway_output *output = 1144 struct sway_output *output =
1074 container_floating_find_output(con); 1145 container_floating_find_output(con);
1075 if (con->workspace->output != output) { 1146 if (con->pending.workspace->output != output) {
1076 container_floating_move_to_center(con); 1147 container_floating_move_to_center(con);
1077 } 1148 }
1078 } 1149 }
@@ -1084,11 +1155,11 @@ void container_fullscreen_disable(struct sway_container *con) {
1084 // If the container was mapped as fullscreen and set as floating by 1155 // 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 1156 // criteria, it needs to be reinitialized as floating to get the proper
1086 // size and location 1157 // size and location
1087 if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { 1158 if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {
1088 container_floating_resize_and_center(con); 1159 container_floating_resize_and_center(con);
1089 } 1160 }
1090 1161
1091 con->fullscreen_mode = FULLSCREEN_NONE; 1162 con->pending.fullscreen_mode = FULLSCREEN_NONE;
1092 container_end_mouse_operation(con); 1163 container_end_mouse_operation(con);
1093 ipc_event_window(con, "fullscreen_mode"); 1164 ipc_event_window(con, "fullscreen_mode");
1094 1165
@@ -1106,7 +1177,7 @@ void container_fullscreen_disable(struct sway_container *con) {
1106 1177
1107void container_set_fullscreen(struct sway_container *con, 1178void container_set_fullscreen(struct sway_container *con,
1108 enum sway_fullscreen_mode mode) { 1179 enum sway_fullscreen_mode mode) {
1109 if (con->fullscreen_mode == mode) { 1180 if (con->pending.fullscreen_mode == mode) {
1110 return; 1181 return;
1111 } 1182 }
1112 1183
@@ -1118,8 +1189,8 @@ void container_set_fullscreen(struct sway_container *con,
1118 if (root->fullscreen_global) { 1189 if (root->fullscreen_global) {
1119 container_fullscreen_disable(root->fullscreen_global); 1190 container_fullscreen_disable(root->fullscreen_global);
1120 } 1191 }
1121 if (con->workspace && con->workspace->fullscreen) { 1192 if (con->pending.workspace && con->pending.workspace->fullscreen) {
1122 container_fullscreen_disable(con->workspace->fullscreen); 1193 container_fullscreen_disable(con->pending.workspace->fullscreen);
1123 } 1194 }
1124 container_fullscreen_workspace(con); 1195 container_fullscreen_workspace(con);
1125 break; 1196 break;
@@ -1127,7 +1198,7 @@ void container_set_fullscreen(struct sway_container *con,
1127 if (root->fullscreen_global) { 1198 if (root->fullscreen_global) {
1128 container_fullscreen_disable(root->fullscreen_global); 1199 container_fullscreen_disable(root->fullscreen_global);
1129 } 1200 }
1130 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1201 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1131 container_fullscreen_disable(con); 1202 container_fullscreen_disable(con);
1132 } 1203 }
1133 container_fullscreen_global(con); 1204 container_fullscreen_global(con);
@@ -1137,8 +1208,8 @@ void container_set_fullscreen(struct sway_container *con,
1137 1208
1138struct sway_container *container_toplevel_ancestor( 1209struct sway_container *container_toplevel_ancestor(
1139 struct sway_container *container) { 1210 struct sway_container *container) {
1140 while (container->parent) { 1211 while (container->pending.parent) {
1141 container = container->parent; 1212 container = container->pending.parent;
1142 } 1213 }
1143 1214
1144 return container; 1215 return container;
@@ -1150,10 +1221,10 @@ bool container_is_floating_or_child(struct sway_container *container) {
1150 1221
1151bool container_is_fullscreen_or_child(struct sway_container *container) { 1222bool container_is_fullscreen_or_child(struct sway_container *container) {
1152 do { 1223 do {
1153 if (container->fullscreen_mode) { 1224 if (container->pending.fullscreen_mode) {
1154 return true; 1225 return true;
1155 } 1226 }
1156 container = container->parent; 1227 container = container->pending.parent;
1157 } while (container); 1228 } while (container);
1158 1229
1159 return false; 1230 return false;
@@ -1226,11 +1297,11 @@ void container_discover_outputs(struct sway_container *con) {
1226} 1297}
1227 1298
1228enum sway_container_layout container_parent_layout(struct sway_container *con) { 1299enum sway_container_layout container_parent_layout(struct sway_container *con) {
1229 if (con->parent) { 1300 if (con->pending.parent) {
1230 return con->parent->layout; 1301 return con->pending.parent->pending.layout;
1231 } 1302 }
1232 if (con->workspace) { 1303 if (con->pending.workspace) {
1233 return con->workspace->layout; 1304 return con->pending.workspace->layout;
1234 } 1305 }
1235 return L_NONE; 1306 return L_NONE;
1236} 1307}
@@ -1244,16 +1315,16 @@ enum sway_container_layout container_current_parent_layout(
1244} 1315}
1245 1316
1246list_t *container_get_siblings(struct sway_container *container) { 1317list_t *container_get_siblings(struct sway_container *container) {
1247 if (container->parent) { 1318 if (container->pending.parent) {
1248 return container->parent->children; 1319 return container->pending.parent->pending.children;
1249 } 1320 }
1250 if (container_is_scratchpad_hidden(container)) { 1321 if (container_is_scratchpad_hidden(container)) {
1251 return NULL; 1322 return NULL;
1252 } 1323 }
1253 if (list_find(container->workspace->tiling, container) != -1) { 1324 if (list_find(container->pending.workspace->tiling, container) != -1) {
1254 return container->workspace->tiling; 1325 return container->pending.workspace->tiling;
1255 } 1326 }
1256 return container->workspace->floating; 1327 return container->pending.workspace->floating;
1257} 1328}
1258 1329
1259int container_sibling_index(struct sway_container *child) { 1330int container_sibling_index(struct sway_container *child) {
@@ -1268,30 +1339,30 @@ list_t *container_get_current_siblings(struct sway_container *container) {
1268} 1339}
1269 1340
1270void container_handle_fullscreen_reparent(struct sway_container *con) { 1341void container_handle_fullscreen_reparent(struct sway_container *con) {
1271 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || 1342 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1272 con->workspace->fullscreen == con) { 1343 con->pending.workspace->fullscreen == con) {
1273 return; 1344 return;
1274 } 1345 }
1275 if (con->workspace->fullscreen) { 1346 if (con->pending.workspace->fullscreen) {
1276 container_fullscreen_disable(con->workspace->fullscreen); 1347 container_fullscreen_disable(con->pending.workspace->fullscreen);
1277 } 1348 }
1278 con->workspace->fullscreen = con; 1349 con->pending.workspace->fullscreen = con;
1279 1350
1280 arrange_workspace(con->workspace); 1351 arrange_workspace(con->pending.workspace);
1281} 1352}
1282 1353
1283static void set_workspace(struct sway_container *container, void *data) { 1354static void set_workspace(struct sway_container *container, void *data) {
1284 container->workspace = container->parent->workspace; 1355 container->pending.workspace = container->pending.parent->pending.workspace;
1285} 1356}
1286 1357
1287void container_insert_child(struct sway_container *parent, 1358void container_insert_child(struct sway_container *parent,
1288 struct sway_container *child, int i) { 1359 struct sway_container *child, int i) {
1289 if (child->workspace) { 1360 if (child->pending.workspace) {
1290 container_detach(child); 1361 container_detach(child);
1291 } 1362 }
1292 list_insert(parent->children, i, child); 1363 list_insert(parent->pending.children, i, child);
1293 child->parent = parent; 1364 child->pending.parent = parent;
1294 child->workspace = parent->workspace; 1365 child->pending.workspace = parent->pending.workspace;
1295 container_for_each_child(child, set_workspace, NULL); 1366 container_for_each_child(child, set_workspace, NULL);
1296 container_handle_fullscreen_reparent(child); 1367 container_handle_fullscreen_reparent(child);
1297 container_update_representation(parent); 1368 container_update_representation(parent);
@@ -1299,14 +1370,14 @@ void container_insert_child(struct sway_container *parent,
1299 1370
1300void container_add_sibling(struct sway_container *fixed, 1371void container_add_sibling(struct sway_container *fixed,
1301 struct sway_container *active, bool after) { 1372 struct sway_container *active, bool after) {
1302 if (active->workspace) { 1373 if (active->pending.workspace) {
1303 container_detach(active); 1374 container_detach(active);
1304 } 1375 }
1305 list_t *siblings = container_get_siblings(fixed); 1376 list_t *siblings = container_get_siblings(fixed);
1306 int index = list_find(siblings, fixed); 1377 int index = list_find(siblings, fixed);
1307 list_insert(siblings, index + after, active); 1378 list_insert(siblings, index + after, active);
1308 active->parent = fixed->parent; 1379 active->pending.parent = fixed->pending.parent;
1309 active->workspace = fixed->workspace; 1380 active->pending.workspace = fixed->pending.workspace;
1310 container_for_each_child(active, set_workspace, NULL); 1381 container_for_each_child(active, set_workspace, NULL);
1311 container_handle_fullscreen_reparent(active); 1382 container_handle_fullscreen_reparent(active);
1312 container_update_representation(active); 1383 container_update_representation(active);
@@ -1314,17 +1385,13 @@ void container_add_sibling(struct sway_container *fixed,
1314 1385
1315void container_add_child(struct sway_container *parent, 1386void container_add_child(struct sway_container *parent,
1316 struct sway_container *child) { 1387 struct sway_container *child) {
1317 if (child->workspace) { 1388 if (child->pending.workspace) {
1318 container_detach(child); 1389 container_detach(child);
1319 } 1390 }
1320 list_add(parent->children, child); 1391 list_add(parent->pending.children, child);
1321 child->parent = parent; 1392 child->pending.parent = parent;
1322 child->workspace = parent->workspace; 1393 child->pending.workspace = parent->pending.workspace;
1323 container_for_each_child(child, set_workspace, NULL); 1394 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); 1395 container_handle_fullscreen_reparent(child);
1329 container_update_representation(parent); 1396 container_update_representation(parent);
1330 node_set_dirty(&child->node); 1397 node_set_dirty(&child->node);
@@ -1332,15 +1399,15 @@ void container_add_child(struct sway_container *parent,
1332} 1399}
1333 1400
1334void container_detach(struct sway_container *child) { 1401void container_detach(struct sway_container *child) {
1335 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1402 if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1336 child->workspace->fullscreen = NULL; 1403 child->pending.workspace->fullscreen = NULL;
1337 } 1404 }
1338 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { 1405 if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1339 root->fullscreen_global = NULL; 1406 root->fullscreen_global = NULL;
1340 } 1407 }
1341 1408
1342 struct sway_container *old_parent = child->parent; 1409 struct sway_container *old_parent = child->pending.parent;
1343 struct sway_workspace *old_workspace = child->workspace; 1410 struct sway_workspace *old_workspace = child->pending.workspace;
1344 list_t *siblings = container_get_siblings(child); 1411 list_t *siblings = container_get_siblings(child);
1345 if (siblings) { 1412 if (siblings) {
1346 int index = list_find(siblings, child); 1413 int index = list_find(siblings, child);
@@ -1348,8 +1415,8 @@ void container_detach(struct sway_container *child) {
1348 list_del(siblings, index); 1415 list_del(siblings, index);
1349 } 1416 }
1350 } 1417 }
1351 child->parent = NULL; 1418 child->pending.parent = NULL;
1352 child->workspace = NULL; 1419 child->pending.workspace = NULL;
1353 container_for_each_child(child, set_workspace, NULL); 1420 container_for_each_child(child, set_workspace, NULL);
1354 1421
1355 if (old_parent) { 1422 if (old_parent) {
@@ -1364,18 +1431,18 @@ void container_detach(struct sway_container *child) {
1364 1431
1365void container_replace(struct sway_container *container, 1432void container_replace(struct sway_container *container,
1366 struct sway_container *replacement) { 1433 struct sway_container *replacement) {
1367 enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; 1434 enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode;
1368 bool scratchpad = container->scratchpad; 1435 bool scratchpad = container->scratchpad;
1369 struct sway_workspace *ws = NULL; 1436 struct sway_workspace *ws = NULL;
1370 if (fullscreen != FULLSCREEN_NONE) { 1437 if (fullscreen != FULLSCREEN_NONE) {
1371 container_fullscreen_disable(container); 1438 container_fullscreen_disable(container);
1372 } 1439 }
1373 if (scratchpad) { 1440 if (scratchpad) {
1374 ws = container->workspace; 1441 ws = container->pending.workspace;
1375 root_scratchpad_show(container); 1442 root_scratchpad_show(container);
1376 root_scratchpad_remove_container(container); 1443 root_scratchpad_remove_container(container);
1377 } 1444 }
1378 if (container->parent || container->workspace) { 1445 if (container->pending.parent || container->pending.workspace) {
1379 float width_fraction = container->width_fraction; 1446 float width_fraction = container->width_fraction;
1380 float height_fraction = container->height_fraction; 1447 float height_fraction = container->height_fraction;
1381 container_add_sibling(container, replacement, 1); 1448 container_add_sibling(container, replacement, 1);
@@ -1403,7 +1470,7 @@ struct sway_container *container_split(struct sway_container *child,
1403 enum sway_container_layout layout) { 1470 enum sway_container_layout layout) {
1404 // i3 doesn't split singleton H/V containers 1471 // i3 doesn't split singleton H/V containers
1405 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 1472 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354
1406 if (child->parent || child->workspace) { 1473 if (child->pending.parent || child->pending.workspace) {
1407 list_t *siblings = container_get_siblings(child); 1474 list_t *siblings = container_get_siblings(child);
1408 if (siblings->length == 1) { 1475 if (siblings->length == 1) {
1409 enum sway_container_layout current = container_parent_layout(child); 1476 enum sway_container_layout current = container_parent_layout(child);
@@ -1411,12 +1478,12 @@ struct sway_container *container_split(struct sway_container *child,
1411 current = L_NONE; 1478 current = L_NONE;
1412 } 1479 }
1413 if (current == L_HORIZ || current == L_VERT) { 1480 if (current == L_HORIZ || current == L_VERT) {
1414 if (child->parent) { 1481 if (child->pending.parent) {
1415 child->parent->layout = layout; 1482 child->pending.parent->pending.layout = layout;
1416 container_update_representation(child->parent); 1483 container_update_representation(child->pending.parent);
1417 } else { 1484 } else {
1418 child->workspace->layout = layout; 1485 child->pending.workspace->layout = layout;
1419 workspace_update_representation(child->workspace); 1486 workspace_update_representation(child->pending.workspace);
1420 } 1487 }
1421 return child; 1488 return child;
1422 } 1489 }
@@ -1429,25 +1496,25 @@ struct sway_container *container_split(struct sway_container *child,
1429 if (container_is_floating(child) && child->view) { 1496 if (container_is_floating(child) && child->view) {
1430 view_set_tiled(child->view, true); 1497 view_set_tiled(child->view, true);
1431 if (child->view->using_csd) { 1498 if (child->view->using_csd) {
1432 child->border = child->saved_border; 1499 child->pending.border = child->saved_border;
1433 } 1500 }
1434 } 1501 }
1435 1502
1436 struct sway_container *cont = container_create(NULL); 1503 struct sway_container *cont = container_create(NULL);
1437 cont->width = child->width; 1504 cont->pending.width = child->pending.width;
1438 cont->height = child->height; 1505 cont->pending.height = child->pending.height;
1439 cont->width_fraction = child->width_fraction; 1506 cont->width_fraction = child->width_fraction;
1440 cont->height_fraction = child->height_fraction; 1507 cont->height_fraction = child->height_fraction;
1441 cont->x = child->x; 1508 cont->pending.x = child->pending.x;
1442 cont->y = child->y; 1509 cont->pending.y = child->pending.y;
1443 cont->layout = layout; 1510 cont->pending.layout = layout;
1444 1511
1445 container_replace(child, cont); 1512 container_replace(child, cont);
1446 container_add_child(cont, child); 1513 container_add_child(cont, child);
1447 1514
1448 if (set_focus) { 1515 if (set_focus) {
1449 seat_set_raw_focus(seat, &cont->node); 1516 seat_set_raw_focus(seat, &cont->node);
1450 if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { 1517 if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1451 seat_set_focus(seat, &child->node); 1518 seat_set_focus(seat, &child->node);
1452 } else { 1519 } else {
1453 seat_set_raw_focus(seat, &child->node); 1520 seat_set_raw_focus(seat, &child->node);
@@ -1554,39 +1621,8 @@ static void update_marks_texture(struct sway_container *con,
1554 } 1621 }
1555 free(part); 1622 free(part);
1556 1623
1557 double scale = output->wlr_output->scale; 1624 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 1625
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); 1626 free(buffer);
1591} 1627}
1592 1628
@@ -1608,19 +1644,19 @@ void container_update_marks_textures(struct sway_container *con) {
1608void container_raise_floating(struct sway_container *con) { 1644void container_raise_floating(struct sway_container *con) {
1609 // Bring container to front by putting it at the end of the floating list. 1645 // Bring container to front by putting it at the end of the floating list.
1610 struct sway_container *floater = container_toplevel_ancestor(con); 1646 struct sway_container *floater = container_toplevel_ancestor(con);
1611 if (container_is_floating(floater) && floater->workspace) { 1647 if (container_is_floating(floater) && floater->pending.workspace) {
1612 list_move_to_end(floater->workspace->floating, floater); 1648 list_move_to_end(floater->pending.workspace->floating, floater);
1613 node_set_dirty(&floater->workspace->node); 1649 node_set_dirty(&floater->pending.workspace->node);
1614 } 1650 }
1615} 1651}
1616 1652
1617bool container_is_scratchpad_hidden(struct sway_container *con) { 1653bool container_is_scratchpad_hidden(struct sway_container *con) {
1618 return con->scratchpad && !con->workspace; 1654 return con->scratchpad && !con->pending.workspace;
1619} 1655}
1620 1656
1621bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { 1657bool container_is_scratchpad_hidden_or_child(struct sway_container *con) {
1622 con = container_toplevel_ancestor(con); 1658 con = container_toplevel_ancestor(con);
1623 return con->scratchpad && !con->workspace; 1659 return con->scratchpad && !con->pending.workspace;
1624} 1660}
1625 1661
1626bool container_is_sticky(struct sway_container *con) { 1662bool container_is_sticky(struct sway_container *con) {
@@ -1648,39 +1684,39 @@ static bool is_parallel(enum sway_container_layout first,
1648static bool container_is_squashable(struct sway_container *con, 1684static bool container_is_squashable(struct sway_container *con,
1649 struct sway_container *child) { 1685 struct sway_container *child) {
1650 enum sway_container_layout gp_layout = container_parent_layout(con); 1686 enum sway_container_layout gp_layout = container_parent_layout(con);
1651 return (con->layout == L_HORIZ || con->layout == L_VERT) && 1687 return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) &&
1652 (child->layout == L_HORIZ || child->layout == L_VERT) && 1688 (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) &&
1653 !is_parallel(con->layout, child->layout) && 1689 !is_parallel(con->pending.layout, child->pending.layout) &&
1654 is_parallel(gp_layout, child->layout); 1690 is_parallel(gp_layout, child->pending.layout);
1655} 1691}
1656 1692
1657static void container_squash_children(struct sway_container *con) { 1693static void container_squash_children(struct sway_container *con) {
1658 for (int i = 0; i < con->children->length; i++) { 1694 for (int i = 0; i < con->pending.children->length; i++) {
1659 struct sway_container *child = con->children->items[i]; 1695 struct sway_container *child = con->pending.children->items[i];
1660 i += container_squash(child); 1696 i += container_squash(child);
1661 } 1697 }
1662} 1698}
1663 1699
1664int container_squash(struct sway_container *con) { 1700int container_squash(struct sway_container *con) {
1665 if (!con->children) { 1701 if (!con->pending.children) {
1666 return 0; 1702 return 0;
1667 } 1703 }
1668 if (con->children->length != 1) { 1704 if (con->pending.children->length != 1) {
1669 container_squash_children(con); 1705 container_squash_children(con);
1670 return 0; 1706 return 0;
1671 } 1707 }
1672 struct sway_container *child = con->children->items[0]; 1708 struct sway_container *child = con->pending.children->items[0];
1673 int idx = container_sibling_index(con); 1709 int idx = container_sibling_index(con);
1674 int change = 0; 1710 int change = 0;
1675 if (container_is_squashable(con, child)) { 1711 if (container_is_squashable(con, child)) {
1676 // con and child are a redundant H/V pair. Destroy them. 1712 // con and child are a redundant H/V pair. Destroy them.
1677 while (child->children->length) { 1713 while (child->pending.children->length) {
1678 struct sway_container *current = child->children->items[0]; 1714 struct sway_container *current = child->pending.children->items[0];
1679 container_detach(current); 1715 container_detach(current);
1680 if (con->parent) { 1716 if (con->pending.parent) {
1681 container_insert_child(con->parent, current, idx); 1717 container_insert_child(con->pending.parent, current, idx);
1682 } else { 1718 } else {
1683 workspace_insert_tiling_direct(con->workspace, current, idx); 1719 workspace_insert_tiling_direct(con->pending.workspace, current, idx);
1684 } 1720 }
1685 change++; 1721 change++;
1686 } 1722 }
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..c095dce0 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -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 }
diff --git a/sway/tree/root.c b/sway/tree/root.c
index ebd185ec..dd4d8e33 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;
@@ -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);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7afcdf31..b2f70d70 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -56,6 +56,7 @@ void view_destroy(struct sway_view *view) {
56 "(might have a pending transaction?)")) { 56 "(might have a pending transaction?)")) {
57 return; 57 return;
58 } 58 }
59 wl_list_remove(&view->events.unmap.listener_list);
59 if (!wl_list_empty(&view->saved_buffers)) { 60 if (!wl_list_empty(&view->saved_buffers)) {
60 view_remove_saved_buffer(view); 61 view_remove_saved_buffer(view);
61 } 62 }
@@ -206,7 +207,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) {
206 } else { 207 } else {
207 only_visible = true; 208 only_visible = true;
208 } 209 }
209 con = con->parent; 210 con = con->pending.parent;
210 } 211 }
211 return only_visible; 212 return only_visible;
212} 213}
@@ -222,72 +223,73 @@ static bool view_is_only_visible(struct sway_view *view) {
222 } 223 }
223 } 224 }
224 225
225 con = con->parent; 226 con = con->pending.parent;
226 } 227 }
227 228
228 return true; 229 return true;
229} 230}
230 231
231static bool gaps_to_edge(struct sway_view *view) { 232static bool gaps_to_edge(struct sway_view *view) {
232 struct side_gaps gaps = view->container->workspace->current_gaps; 233 struct side_gaps gaps = view->container->pending.workspace->current_gaps;
233 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; 234 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0;
234} 235}
235 236
236void view_autoconfigure(struct sway_view *view) { 237void view_autoconfigure(struct sway_view *view) {
237 struct sway_container *con = view->container; 238 struct sway_container *con = view->container;
238 struct sway_workspace *ws = con->workspace; 239 struct sway_workspace *ws = con->pending.workspace;
239 240
240 if (container_is_scratchpad_hidden(con) && 241 if (container_is_scratchpad_hidden(con) &&
241 con->fullscreen_mode != FULLSCREEN_GLOBAL) { 242 con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
242 return; 243 return;
243 } 244 }
244 struct sway_output *output = ws ? ws->output : NULL; 245 struct sway_output *output = ws ? ws->output : NULL;
245 246
246 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 247 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
247 con->content_x = output->lx; 248 con->pending.content_x = output->lx;
248 con->content_y = output->ly; 249 con->pending.content_y = output->ly;
249 con->content_width = output->width; 250 con->pending.content_width = output->width;
250 con->content_height = output->height; 251 con->pending.content_height = output->height;
251 return; 252 return;
252 } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 253 } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
253 con->content_x = root->x; 254 con->pending.content_x = root->x;
254 con->content_y = root->y; 255 con->pending.content_y = root->y;
255 con->content_width = root->width; 256 con->pending.content_width = root->width;
256 con->content_height = root->height; 257 con->pending.content_height = root->height;
257 return; 258 return;
258 } 259 }
259 260
260 con->border_top = con->border_bottom = true; 261 con->pending.border_top = con->pending.border_bottom = true;
261 con->border_left = con->border_right = true; 262 con->pending.border_left = con->pending.border_right = true;
262 double y_offset = 0; 263 double y_offset = 0;
263 264
264 if (!container_is_floating(con) && ws) { 265 if (!container_is_floating_or_child(con) && ws) {
265 if (config->hide_edge_borders == E_BOTH 266 if (config->hide_edge_borders == E_BOTH
266 || config->hide_edge_borders == E_VERTICAL) { 267 || config->hide_edge_borders == E_VERTICAL) {
267 con->border_left = con->x != ws->x; 268 con->pending.border_left = con->pending.x != ws->x;
268 int right_x = con->x + con->width; 269 int right_x = con->pending.x + con->pending.width;
269 con->border_right = right_x != ws->x + ws->width; 270 con->pending.border_right = right_x != ws->x + ws->width;
270 } 271 }
271 272
272 if (config->hide_edge_borders == E_BOTH 273 if (config->hide_edge_borders == E_BOTH
273 || config->hide_edge_borders == E_HORIZONTAL) { 274 || config->hide_edge_borders == E_HORIZONTAL) {
274 con->border_top = con->y != ws->y; 275 con->pending.border_top = con->pending.y != ws->y;
275 int bottom_y = con->y + con->height; 276 int bottom_y = con->pending.y + con->pending.height;
276 con->border_bottom = bottom_y != ws->y + ws->height; 277 con->pending.border_bottom = bottom_y != ws->y + ws->height;
277 } 278 }
278 279
279 bool smart = config->hide_edge_borders_smart == ESMART_ON || 280 bool smart = config->hide_edge_borders_smart == ESMART_ON ||
280 (config->hide_edge_borders_smart == ESMART_NO_GAPS && 281 (config->hide_edge_borders_smart == ESMART_NO_GAPS &&
281 !gaps_to_edge(view)); 282 !gaps_to_edge(view));
282 if (smart) { 283 if (smart) {
283 bool show_border = container_is_floating_or_child(con) || 284 bool show_border = !view_is_only_visible(view);
284 !view_is_only_visible(view); 285 con->pending.border_left &= show_border;
285 con->border_left &= show_border; 286 con->pending.border_right &= show_border;
286 con->border_right &= show_border; 287 con->pending.border_top &= show_border;
287 con->border_top &= show_border; 288 con->pending.border_bottom &= show_border;
288 con->border_bottom &= show_border;
289 } 289 }
290 }
290 291
292 if (!container_is_floating(con)) {
291 // In a tabbed or stacked container, the container's y is the top of the 293 // 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, 294 // 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. 295 // bar, and disable any top border because we'll always have the title bar.
@@ -298,56 +300,56 @@ void view_autoconfigure(struct sway_view *view) {
298 enum sway_container_layout layout = container_parent_layout(con); 300 enum sway_container_layout layout = container_parent_layout(con);
299 if (layout == L_TABBED) { 301 if (layout == L_TABBED) {
300 y_offset = container_titlebar_height(); 302 y_offset = container_titlebar_height();
301 con->border_top = false; 303 con->pending.border_top = false;
302 } else if (layout == L_STACKED) { 304 } else if (layout == L_STACKED) {
303 y_offset = container_titlebar_height() * siblings->length; 305 y_offset = container_titlebar_height() * siblings->length;
304 con->border_top = false; 306 con->pending.border_top = false;
305 } 307 }
306 } 308 }
307 } 309 }
308 310
309 double x, y, width, height; 311 double x, y, width, height;
310 switch (con->border) { 312 switch (con->pending.border) {
311 default: 313 default:
312 case B_CSD: 314 case B_CSD:
313 case B_NONE: 315 case B_NONE:
314 x = con->x; 316 x = con->pending.x;
315 y = con->y + y_offset; 317 y = con->pending.y + y_offset;
316 width = con->width; 318 width = con->pending.width;
317 height = con->height - y_offset; 319 height = con->pending.height - y_offset;
318 break; 320 break;
319 case B_PIXEL: 321 case B_PIXEL:
320 x = con->x + con->border_thickness * con->border_left; 322 x = con->pending.x + con->pending.border_thickness * con->pending.border_left;
321 y = con->y + con->border_thickness * con->border_top + y_offset; 323 y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset;
322 width = con->width 324 width = con->pending.width
323 - con->border_thickness * con->border_left 325 - con->pending.border_thickness * con->pending.border_left
324 - con->border_thickness * con->border_right; 326 - con->pending.border_thickness * con->pending.border_right;
325 height = con->height - y_offset 327 height = con->pending.height - y_offset
326 - con->border_thickness * con->border_top 328 - con->pending.border_thickness * con->pending.border_top
327 - con->border_thickness * con->border_bottom; 329 - con->pending.border_thickness * con->pending.border_bottom;
328 break; 330 break;
329 case B_NORMAL: 331 case B_NORMAL:
330 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border 332 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
331 x = con->x + con->border_thickness * con->border_left; 333 x = con->pending.x + con->pending.border_thickness * con->pending.border_left;
332 width = con->width 334 width = con->pending.width
333 - con->border_thickness * con->border_left 335 - con->pending.border_thickness * con->pending.border_left
334 - con->border_thickness * con->border_right; 336 - con->pending.border_thickness * con->pending.border_right;
335 if (y_offset) { 337 if (y_offset) {
336 y = con->y + y_offset; 338 y = con->pending.y + y_offset;
337 height = con->height - y_offset 339 height = con->pending.height - y_offset
338 - con->border_thickness * con->border_bottom; 340 - con->pending.border_thickness * con->pending.border_bottom;
339 } else { 341 } else {
340 y = con->y + container_titlebar_height(); 342 y = con->pending.y + container_titlebar_height();
341 height = con->height - container_titlebar_height() 343 height = con->pending.height - container_titlebar_height()
342 - con->border_thickness * con->border_bottom; 344 - con->pending.border_thickness * con->pending.border_bottom;
343 } 345 }
344 break; 346 break;
345 } 347 }
346 348
347 con->content_x = x; 349 con->pending.content_x = x;
348 con->content_y = y; 350 con->pending.content_y = y;
349 con->content_width = width; 351 con->pending.content_width = width;
350 con->content_height = height; 352 con->pending.content_height = height;
351} 353}
352 354
353void view_set_activated(struct sway_view *view, bool activated) { 355void view_set_activated(struct sway_view *view, bool activated) {
@@ -361,7 +363,7 @@ void view_set_activated(struct sway_view *view, bool activated) {
361} 363}
362 364
363void view_request_activate(struct sway_view *view) { 365void view_request_activate(struct sway_view *view) {
364 struct sway_workspace *ws = view->container->workspace; 366 struct sway_workspace *ws = view->container->pending.workspace;
365 if (!ws) { // hidden scratchpad container 367 if (!ws) { // hidden scratchpad container
366 return; 368 return;
367 } 369 }
@@ -401,13 +403,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) { 403void view_update_csd_from_client(struct sway_view *view, bool enabled) {
402 sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); 404 sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled);
403 struct sway_container *con = view->container; 405 struct sway_container *con = view->container;
404 if (enabled && con && con->border != B_CSD) { 406 if (enabled && con && con->pending.border != B_CSD) {
405 con->saved_border = con->border; 407 con->saved_border = con->pending.border;
406 if (container_is_floating(con)) { 408 if (container_is_floating(con)) {
407 con->border = B_CSD; 409 con->pending.border = B_CSD;
408 } 410 }
409 } else if (!enabled && con && con->border == B_CSD) { 411 } else if (!enabled && con && con->pending.border == B_CSD) {
410 con->border = con->saved_border; 412 con->pending.border = con->saved_border;
411 } 413 }
412 view->using_csd = enabled; 414 view->using_csd = enabled;
413} 415}
@@ -465,6 +467,9 @@ static void view_subsurface_create(struct sway_view *view,
465static void view_init_subsurfaces(struct sway_view *view, 467static void view_init_subsurfaces(struct sway_view *view,
466 struct wlr_surface *surface); 468 struct wlr_surface *surface);
467 469
470static void view_child_init_subsurfaces(struct sway_view_child *view_child,
471 struct wlr_surface *surface);
472
468static void view_handle_surface_new_subsurface(struct wl_listener *listener, 473static void view_handle_surface_new_subsurface(struct wl_listener *listener,
469 void *data) { 474 void *data) {
470 struct sway_view *view = 475 struct sway_view *view =
@@ -577,7 +582,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
577 if (node && node->type == N_WORKSPACE) { 582 if (node && node->type == N_WORKSPACE) {
578 return node->sway_workspace; 583 return node->sway_workspace;
579 } else if (node && node->type == N_CONTAINER) { 584 } else if (node && node->type == N_CONTAINER) {
580 return node->sway_container->workspace; 585 return node->sway_container->pending.workspace;
581 } 586 }
582 587
583 // When there's no outputs connected, the above should match a workspace on 588 // When there's no outputs connected, the above should match a workspace on
@@ -590,12 +595,17 @@ static bool should_focus(struct sway_view *view) {
590 struct sway_seat *seat = input_manager_current_seat(); 595 struct sway_seat *seat = input_manager_current_seat();
591 struct sway_container *prev_con = seat_get_focused_container(seat); 596 struct sway_container *prev_con = seat_get_focused_container(seat);
592 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); 597 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
593 struct sway_workspace *map_ws = view->container->workspace; 598 struct sway_workspace *map_ws = view->container->pending.workspace;
594 599
595 if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { 600 if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
596 return true; 601 return true;
597 } 602 }
598 603
604 // View opened "under" fullscreen view should not be given focus.
605 if (root->fullscreen_global || !map_ws || map_ws->fullscreen) {
606 return false;
607 }
608
599 // Views can only take focus if they are mapped into the active workspace 609 // Views can only take focus if they are mapped into the active workspace
600 if (prev_ws != map_ws) { 610 if (prev_ws != map_ws) {
601 return false; 611 return false;
@@ -603,9 +613,9 @@ static bool should_focus(struct sway_view *view) {
603 613
604 // If the view is the only one in the focused workspace, it'll get focus 614 // If the view is the only one in the focused workspace, it'll get focus
605 // regardless of any no_focus criteria. 615 // regardless of any no_focus criteria.
606 if (!view->container->parent && !prev_con) { 616 if (!view->container->pending.parent && !prev_con) {
607 size_t num_children = view->container->workspace->tiling->length + 617 size_t num_children = view->container->pending.workspace->tiling->length +
608 view->container->workspace->floating->length; 618 view->container->pending.workspace->floating->length;
609 if (num_children == 1) { 619 if (num_children == 1) {
610 return true; 620 return true;
611 } 621 }
@@ -635,6 +645,7 @@ static void handle_foreign_activate_request(
635 break; 645 break;
636 } 646 }
637 } 647 }
648 transaction_commit_dirty();
638} 649}
639 650
640static void handle_foreign_fullscreen_request( 651static void handle_foreign_fullscreen_request(
@@ -645,9 +656,21 @@ static void handle_foreign_fullscreen_request(
645 656
646 // Match fullscreen command behavior for scratchpad hidden views 657 // Match fullscreen command behavior for scratchpad hidden views
647 struct sway_container *container = view->container; 658 struct sway_container *container = view->container;
648 if (!container->workspace) { 659 if (!container->pending.workspace) {
649 while (container->parent) { 660 while (container->pending.parent) {
650 container = container->parent; 661 container = container->pending.parent;
662 }
663 }
664
665 if (event->fullscreen && event->output && event->output->data) {
666 struct sway_output *output = event->output->data;
667 struct sway_workspace *ws = output_get_active_workspace(output);
668 if (ws && !container_is_scratchpad_hidden(view->container)) {
669 if (container_is_floating(view->container)) {
670 workspace_add_floating(ws, view->container);
671 } else {
672 workspace_add_tiling(ws, view->container);
673 }
651 } 674 }
652 } 675 }
653 676
@@ -656,12 +679,13 @@ static void handle_foreign_fullscreen_request(
656 if (event->fullscreen) { 679 if (event->fullscreen) {
657 arrange_root(); 680 arrange_root();
658 } else { 681 } else {
659 if (container->parent) { 682 if (container->pending.parent) {
660 arrange_container(container->parent); 683 arrange_container(container->pending.parent);
661 } else if (container->workspace) { 684 } else if (container->pending.workspace) {
662 arrange_workspace(container->workspace); 685 arrange_workspace(container->pending.workspace);
663 } 686 }
664 } 687 }
688 transaction_commit_dirty();
665} 689}
666 690
667static void handle_foreign_close_request( 691static void handle_foreign_close_request(
@@ -742,7 +766,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
742 766
743 view_init_subsurfaces(view, wlr_surface); 767 view_init_subsurfaces(view, wlr_surface);
744 wl_signal_add(&wlr_surface->events.new_subsurface, 768 wl_signal_add(&wlr_surface->events.new_subsurface,
745 &view->surface_new_subsurface); 769 &view->surface_new_subsurface);
746 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; 770 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
747 771
748 if (decoration) { 772 if (decoration) {
@@ -750,20 +774,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
750 } 774 }
751 775
752 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 776 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
753 view->container->border = config->floating_border; 777 view->container->pending.border = config->floating_border;
754 view->container->border_thickness = config->floating_border_thickness; 778 view->container->pending.border_thickness = config->floating_border_thickness;
755 container_set_floating(view->container, true); 779 container_set_floating(view->container, true);
756 } else { 780 } else {
757 view->container->border = config->border; 781 view->container->pending.border = config->border;
758 view->container->border_thickness = config->border_thickness; 782 view->container->pending.border_thickness = config->border_thickness;
759 view_set_tiled(view, true); 783 view_set_tiled(view, true);
760 } 784 }
761 785
762 if (config->popup_during_fullscreen == POPUP_LEAVE && 786 if (config->popup_during_fullscreen == POPUP_LEAVE &&
763 container->workspace && 787 container->pending.workspace &&
764 container->workspace->fullscreen && 788 container->pending.workspace->fullscreen &&
765 container->workspace->fullscreen->view) { 789 container->pending.workspace->fullscreen->view) {
766 struct sway_container *fs = container->workspace->fullscreen; 790 struct sway_container *fs = container->pending.workspace->fullscreen;
767 if (view_is_transient_for(view, fs->view)) { 791 if (view_is_transient_for(view, fs->view)) {
768 container_set_fullscreen(fs, false); 792 container_set_fullscreen(fs, false);
769 } 793 }
@@ -774,12 +798,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
774 798
775 if (fullscreen) { 799 if (fullscreen) {
776 container_set_fullscreen(view->container, true); 800 container_set_fullscreen(view->container, true);
777 arrange_workspace(view->container->workspace); 801 arrange_workspace(view->container->pending.workspace);
778 } else { 802 } else {
779 if (container->parent) { 803 if (container->pending.parent) {
780 arrange_container(container->parent); 804 arrange_container(container->pending.parent);
781 } else if (container->workspace) { 805 } else if (container->pending.workspace) {
782 arrange_workspace(container->workspace); 806 arrange_workspace(container->pending.workspace);
783 } 807 }
784 } 808 }
785 809
@@ -790,9 +814,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
790#if HAVE_XWAYLAND 814#if HAVE_XWAYLAND
791 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 815 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
792 struct wlr_xwayland_surface *xsurface = 816 struct wlr_xwayland_surface *xsurface =
793 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 817 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
794 set_focus = (wlr_xwayland_icccm_input_model(xsurface) != 818 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
795 WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; 819 WLR_ICCCM_INPUT_MODEL_NONE;
796 } 820 }
797#endif 821#endif
798 822
@@ -803,11 +827,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
803 const char *app_id; 827 const char *app_id;
804 const char *class; 828 const char *class;
805 if ((app_id = view_get_app_id(view)) != NULL) { 829 if ((app_id = view_get_app_id(view)) != NULL) {
806 wlr_foreign_toplevel_handle_v1_set_app_id( 830 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) { 831 } else if ((class = view_get_class(view)) != NULL) {
809 wlr_foreign_toplevel_handle_v1_set_app_id( 832 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class);
810 view->foreign_toplevel, class);
811 } 833 }
812} 834}
813 835
@@ -826,8 +848,8 @@ void view_unmap(struct sway_view *view) {
826 view->foreign_toplevel = NULL; 848 view->foreign_toplevel = NULL;
827 } 849 }
828 850
829 struct sway_container *parent = view->container->parent; 851 struct sway_container *parent = view->container->pending.parent;
830 struct sway_workspace *ws = view->container->workspace; 852 struct sway_workspace *ws = view->container->pending.workspace;
831 container_begin_destroy(view->container); 853 container_begin_destroy(view->container);
832 if (parent) { 854 if (parent) {
833 container_reap_empty(parent); 855 container_reap_empty(parent);
@@ -860,47 +882,38 @@ void view_unmap(struct sway_view *view) {
860 view->surface = NULL; 882 view->surface = NULL;
861} 883}
862 884
863void view_update_size(struct sway_view *view, int width, int height) { 885void view_update_size(struct sway_view *view) {
864 struct sway_container *con = view->container; 886 struct sway_container *con = view->container;
887 con->pending.content_width = view->geometry.width;
888 con->pending.content_height = view->geometry.height;
889 container_set_geometry_from_content(con);
890}
865 891
866 if (container_is_floating(con)) { 892void view_center_surface(struct sway_view *view) {
867 con->content_width = width; 893 struct sway_container *con = view->container;
868 con->content_height = height; 894 // We always center the current coordinates rather than the next, as the
869 container_set_geometry_from_content(con); 895 // geometry immediately affects the currently active rendering.
870 } else { 896 con->surface_x = fmax(con->current.content_x, con->current.content_x +
871 con->surface_x = con->content_x + (con->content_width - width) / 2; 897 (con->current.content_width - view->geometry.width) / 2);
872 con->surface_y = con->content_y + (con->content_height - height) / 2; 898 con->surface_y = fmax(con->current.content_y, con->current.content_y +
873 con->surface_x = fmax(con->surface_x, con->content_x); 899 (con->current.content_height - view->geometry.height) / 2);
874 con->surface_y = fmax(con->surface_y, con->content_y);
875 }
876} 900}
877 901
878static const struct sway_view_child_impl subsurface_impl; 902static const struct sway_view_child_impl subsurface_impl;
879 903
880static void subsurface_get_root_coords(struct sway_view_child *child, 904static void subsurface_get_view_coords(struct sway_view_child *child,
881 int *root_sx, int *root_sy) { 905 int *sx, int *sy) {
882 struct wlr_surface *surface = child->surface; 906 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 && 907 if (child->parent && child->parent->impl &&
887 child->parent->impl->get_root_coords) { 908 child->parent->impl->get_view_coords) {
888 int sx, sy; 909 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 { 910 } else {
893 while (surface && wlr_surface_is_subsurface(surface)) { 911 *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 } 912 }
913 struct wlr_subsurface *subsurface =
914 wlr_subsurface_from_wlr_surface(surface);
915 *sx += subsurface->current.x;
916 *sy += subsurface->current.y;
904} 917}
905 918
906static void subsurface_destroy(struct sway_view_child *child) { 919static void subsurface_destroy(struct sway_view_child *child) {
@@ -914,7 +927,7 @@ static void subsurface_destroy(struct sway_view_child *child) {
914} 927}
915 928
916static const struct sway_view_child_impl subsurface_impl = { 929static const struct sway_view_child_impl subsurface_impl = {
917 .get_root_coords = subsurface_get_root_coords, 930 .get_view_coords = subsurface_get_view_coords,
918 .destroy = subsurface_destroy, 931 .destroy = subsurface_destroy,
919}; 932};
920 933
@@ -968,15 +981,27 @@ static void view_child_subsurface_create(struct sway_view_child *child,
968 view_child_damage(&subsurface->child, true); 981 view_child_damage(&subsurface->child, true);
969} 982}
970 983
984static bool view_child_is_mapped(struct sway_view_child *child) {
985 while (child) {
986 if (!child->mapped) {
987 return false;
988 }
989 child = child->parent;
990 }
991 return true;
992}
993
971static void view_child_damage(struct sway_view_child *child, bool whole) { 994static void view_child_damage(struct sway_view_child *child, bool whole) {
972 if (!child || !child->mapped || !child->view || !child->view->container) { 995 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
973 return; 996 return;
974 } 997 }
975 int sx, sy; 998 int sx, sy;
976 child->impl->get_root_coords(child, &sx, &sy); 999 child->impl->get_view_coords(child, &sx, &sy);
977 desktop_damage_surface(child->surface, 1000 desktop_damage_surface(child->surface,
978 child->view->container->content_x + sx, 1001 child->view->container->pending.content_x -
979 child->view->container->content_y + sy, whole); 1002 child->view->geometry.x + sx,
1003 child->view->container->pending.content_y -
1004 child->view->geometry.y + sy, whole);
980} 1005}
981 1006
982static void view_child_handle_surface_commit(struct wl_listener *listener, 1007static void view_child_handle_surface_commit(struct wl_listener *listener,
@@ -1004,11 +1029,29 @@ static void view_child_handle_surface_destroy(struct wl_listener *listener,
1004static void view_init_subsurfaces(struct sway_view *view, 1029static void view_init_subsurfaces(struct sway_view *view,
1005 struct wlr_surface *surface) { 1030 struct wlr_surface *surface) {
1006 struct wlr_subsurface *subsurface; 1031 struct wlr_subsurface *subsurface;
1007 wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { 1032 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1033 current.link) {
1034 view_subsurface_create(view, subsurface);
1035 }
1036 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1037 current.link) {
1008 view_subsurface_create(view, subsurface); 1038 view_subsurface_create(view, subsurface);
1009 } 1039 }
1010} 1040}
1011 1041
1042static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1043 struct wlr_surface *surface) {
1044 struct wlr_subsurface *subsurface;
1045 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1046 current.link) {
1047 view_child_subsurface_create(view_child, subsurface);
1048 }
1049 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1050 current.link) {
1051 view_child_subsurface_create(view_child, subsurface);
1052 }
1053}
1054
1012static void view_child_handle_surface_map(struct wl_listener *listener, 1055static void view_child_handle_surface_map(struct wl_listener *listener,
1013 void *data) { 1056 void *data) {
1014 struct sway_view_child *child = 1057 struct sway_view_child *child =
@@ -1059,16 +1102,16 @@ void view_child_init(struct sway_view_child *child,
1059 wl_signal_add(&view->events.unmap, &child->view_unmap); 1102 wl_signal_add(&view->events.unmap, &child->view_unmap);
1060 child->view_unmap.notify = view_child_handle_view_unmap; 1103 child->view_unmap.notify = view_child_handle_view_unmap;
1061 1104
1062 struct sway_workspace *workspace = child->view->container->workspace; 1105 struct sway_workspace *workspace = child->view->container->pending.workspace;
1063 if (workspace) { 1106 if (workspace) {
1064 wlr_surface_send_enter(child->surface, workspace->output->wlr_output); 1107 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1065 } 1108 }
1066 1109
1067 view_init_subsurfaces(child->view, surface); 1110 view_child_init_subsurfaces(child, surface);
1068} 1111}
1069 1112
1070void view_child_destroy(struct sway_view_child *child) { 1113void view_child_destroy(struct sway_view_child *child) {
1071 if (child->mapped && child->view->container != NULL) { 1114 if (view_child_is_mapped(child) && child->view->container != NULL) {
1072 view_child_damage(child, true); 1115 view_child_damage(child, true);
1073 } 1116 }
1074 1117
@@ -1081,6 +1124,9 @@ void view_child_destroy(struct sway_view_child *child) {
1081 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { 1124 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1082 wl_list_remove(&subchild->link); 1125 wl_list_remove(&subchild->link);
1083 subchild->parent = NULL; 1126 subchild->parent = NULL;
1127 // The subchild lost its parent link, so it cannot see that the parent
1128 // is unmapped. Unmap it directly.
1129 subchild->mapped = false;
1084 } 1130 }
1085 1131
1086 wl_list_remove(&child->surface_commit.link); 1132 wl_list_remove(&child->surface_commit.link);
@@ -1101,18 +1147,27 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1101 if (wlr_surface_is_xdg_surface(wlr_surface)) { 1147 if (wlr_surface_is_xdg_surface(wlr_surface)) {
1102 struct wlr_xdg_surface *xdg_surface = 1148 struct wlr_xdg_surface *xdg_surface =
1103 wlr_xdg_surface_from_wlr_surface(wlr_surface); 1149 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1150 if (xdg_surface == NULL) {
1151 return NULL;
1152 }
1104 return view_from_wlr_xdg_surface(xdg_surface); 1153 return view_from_wlr_xdg_surface(xdg_surface);
1105 } 1154 }
1106#if HAVE_XWAYLAND 1155#if HAVE_XWAYLAND
1107 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 1156 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
1108 struct wlr_xwayland_surface *xsurface = 1157 struct wlr_xwayland_surface *xsurface =
1109 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 1158 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1159 if (xsurface == NULL) {
1160 return NULL;
1161 }
1110 return view_from_wlr_xwayland_surface(xsurface); 1162 return view_from_wlr_xwayland_surface(xsurface);
1111 } 1163 }
1112#endif 1164#endif
1113 if (wlr_surface_is_subsurface(wlr_surface)) { 1165 if (wlr_surface_is_subsurface(wlr_surface)) {
1114 struct wlr_subsurface *subsurface = 1166 struct wlr_subsurface *subsurface =
1115 wlr_subsurface_from_wlr_surface(wlr_surface); 1167 wlr_subsurface_from_wlr_surface(wlr_surface);
1168 if (subsurface == NULL) {
1169 return NULL;
1170 }
1116 return view_from_wlr_surface(subsurface->parent); 1171 return view_from_wlr_surface(subsurface->parent);
1117 } 1172 }
1118 if (wlr_surface_is_layer_surface(wlr_surface)) { 1173 if (wlr_surface_is_layer_surface(wlr_surface)) {
@@ -1225,8 +1280,6 @@ void view_update_title(struct sway_view *view, bool force) {
1225 view->container->title = NULL; 1280 view->container->title = NULL;
1226 view->container->formatted_title = NULL; 1281 view->container->formatted_title = NULL;
1227 } 1282 }
1228 container_calculate_title_height(view->container);
1229 config_update_font_height(false);
1230 1283
1231 // Update title after the global font height is updated 1284 // Update title after the global font height is updated
1232 container_update_title_textures(view->container); 1285 container_update_title_textures(view->container);
@@ -1242,15 +1295,15 @@ bool view_is_visible(struct sway_view *view) {
1242 if (view->container->node.destroying) { 1295 if (view->container->node.destroying) {
1243 return false; 1296 return false;
1244 } 1297 }
1245 struct sway_workspace *workspace = view->container->workspace; 1298 struct sway_workspace *workspace = view->container->pending.workspace;
1246 if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { 1299 if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
1247 bool fs_global_descendant = false; 1300 bool fs_global_descendant = false;
1248 struct sway_container *parent = view->container->parent; 1301 struct sway_container *parent = view->container->pending.parent;
1249 while (parent) { 1302 while (parent) {
1250 if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { 1303 if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1251 fs_global_descendant = true; 1304 fs_global_descendant = true;
1252 } 1305 }
1253 parent = parent->parent; 1306 parent = parent->pending.parent;
1254 } 1307 }
1255 if (!fs_global_descendant) { 1308 if (!fs_global_descendant) {
1256 return false; 1309 return false;
@@ -1268,13 +1321,13 @@ bool view_is_visible(struct sway_view *view) {
1268 enum sway_container_layout layout = container_parent_layout(con); 1321 enum sway_container_layout layout = container_parent_layout(con);
1269 if ((layout == L_TABBED || layout == L_STACKED) 1322 if ((layout == L_TABBED || layout == L_STACKED)
1270 && !container_is_floating(con)) { 1323 && !container_is_floating(con)) {
1271 struct sway_node *parent = con->parent ? 1324 struct sway_node *parent = con->pending.parent ?
1272 &con->parent->node : &con->workspace->node; 1325 &con->pending.parent->node : &con->pending.workspace->node;
1273 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1326 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
1274 return false; 1327 return false;
1275 } 1328 }
1276 } 1329 }
1277 con = con->parent; 1330 con = con->pending.parent;
1278 } 1331 }
1279 // Check view isn't hidden by another fullscreen view 1332 // Check view isn't hidden by another fullscreen view
1280 struct sway_container *fs = root->fullscreen_global ? 1333 struct sway_container *fs = root->fullscreen_global ?
@@ -1308,7 +1361,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1308 ipc_event_window(view->container, "urgent"); 1361 ipc_event_window(view->container, "urgent");
1309 1362
1310 if (!container_is_scratchpad_hidden(view->container)) { 1363 if (!container_is_scratchpad_hidden(view->container)) {
1311 workspace_detect_urgent(view->container->workspace); 1364 workspace_detect_urgent(view->container->pending.workspace);
1312 } 1365 }
1313} 1366}
1314 1367
@@ -1338,11 +1391,11 @@ static void view_save_buffer_iterator(struct wlr_surface *surface,
1338 saved_buffer->buffer = surface->buffer; 1391 saved_buffer->buffer = surface->buffer;
1339 saved_buffer->width = surface->current.width; 1392 saved_buffer->width = surface->current.width;
1340 saved_buffer->height = surface->current.height; 1393 saved_buffer->height = surface->current.height;
1341 saved_buffer->x = sx; 1394 saved_buffer->x = view->container->surface_x + sx;
1342 saved_buffer->y = sy; 1395 saved_buffer->y = view->container->surface_y + sy;
1343 saved_buffer->transform = surface->current.transform; 1396 saved_buffer->transform = surface->current.transform;
1344 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); 1397 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1345 wl_list_insert(&view->saved_buffers, &saved_buffer->link); 1398 wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
1346 } 1399 }
1347} 1400}
1348 1401
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 921b7d19..8dd7789d 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -48,7 +48,7 @@ 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 noop output for headless
54 return root->outputs->length ? root->outputs->items[0] : root->noop_output; 54 return root->outputs->length ? root->outputs->items[0] : root->noop_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) {
@@ -905,7 +904,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace,
905 enum sway_container_layout old_layout = workspace->layout; 904 enum sway_container_layout old_layout = workspace->layout;
906 struct sway_container *middle = workspace_wrap_children(workspace); 905 struct sway_container *middle = workspace_wrap_children(workspace);
907 workspace->layout = layout; 906 workspace->layout = layout;
908 middle->layout = old_layout; 907 middle->pending.layout = old_layout;
909 908
910 struct sway_seat *seat; 909 struct sway_seat *seat;
911 wl_list_for_each(seat, &server.input->seats, link) { 910 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..8a9a9a28 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->client_pending_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..15eab782 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -90,7 +90,7 @@ static void layer_surface_closed(void *_output,
90 swaybar_output_free(output); 90 swaybar_output_free(output);
91} 91}
92 92
93struct zwlr_layer_surface_v1_listener layer_surface_listener = { 93static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
94 .configure = layer_surface_configure, 94 .configure = layer_surface_configure,
95 .closed = layer_surface_closed, 95 .closed = layer_surface_closed,
96}; 96};
@@ -230,7 +230,7 @@ static void output_scale(void *data, struct wl_output *wl_output,
230 } 230 }
231} 231}
232 232
233struct wl_output_listener output_listener = { 233static const struct wl_output_listener output_listener = {
234 .geometry = output_geometry, 234 .geometry = output_geometry,
235 .mode = output_mode, 235 .mode = output_mode,
236 .done = output_done, 236 .done = output_done,
@@ -307,7 +307,7 @@ static void xdg_output_handle_description(void *data,
307 } 307 }
308} 308}
309 309
310struct zxdg_output_v1_listener xdg_output_listener = { 310static const struct zxdg_output_v1_listener xdg_output_listener = {
311 .logical_position = xdg_output_handle_logical_position, 311 .logical_position = xdg_output_handle_logical_position,
312 .logical_size = xdg_output_handle_logical_size, 312 .logical_size = xdg_output_handle_logical_size,
313 .done = xdg_output_handle_done, 313 .done = xdg_output_handle_done,
@@ -461,13 +461,28 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
461 461
462static void display_in(int fd, short mask, void *data) { 462static void display_in(int fd, short mask, void *data) {
463 struct swaybar *bar = data; 463 struct swaybar *bar = data;
464 if (mask & (POLLHUP | POLLERR)) {
465 if (mask & POLLERR) {
466 sway_log(SWAY_ERROR, "Wayland display poll error");
467 }
468 bar->running = false;
469 return;
470 }
464 if (wl_display_dispatch(bar->display) == -1) { 471 if (wl_display_dispatch(bar->display) == -1) {
472 sway_log(SWAY_ERROR, "wl_display_dispatch failed");
465 bar->running = false; 473 bar->running = false;
466 } 474 }
467} 475}
468 476
469static void ipc_in(int fd, short mask, void *data) { 477static void ipc_in(int fd, short mask, void *data) {
470 struct swaybar *bar = data; 478 struct swaybar *bar = data;
479 if (mask & (POLLHUP | POLLERR)) {
480 if (mask & POLLERR) {
481 sway_log(SWAY_ERROR, "IPC poll error");
482 }
483 bar->running = false;
484 return;
485 }
471 if (handle_ipc_readable(bar)) { 486 if (handle_ipc_readable(bar)) {
472 set_bar_dirty(bar); 487 set_bar_dirty(bar);
473 } 488 }
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..a64aa1ab 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(256);
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..92b73f4c 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;
@@ -779,6 +839,11 @@ void render_frame(struct swaybar_output *output) {
779 839
780 wl_surface_commit(output->surface); 840 wl_surface_commit(output->surface);
781 } 841 }
842
843 if (ctx.textaa_sharp != ctx.textaa_safe) {
844 cairo_font_options_destroy(ctx.textaa_sharp);
845 }
846 cairo_font_options_destroy(ctx.textaa_safe);
782 cairo_surface_destroy(recorder); 847 cairo_surface_destroy(recorder);
783 cairo_destroy(cairo); 848 cairo_destroy(cairo);
784} 849}
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index a5660f62..19f4beac 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 }
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 60536e48..574d3b75 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -343,7 +343,7 @@ int main(int argc, char **argv) {
343 343
344 sway_log_init(SWAY_INFO, NULL); 344 sway_log_init(SWAY_INFO, NULL);
345 345
346 static struct option long_options[] = { 346 static const struct option long_options[] = {
347 {"help", no_argument, NULL, 'h'}, 347 {"help", no_argument, NULL, 'h'},
348 {"monitor", no_argument, NULL, 'm'}, 348 {"monitor", no_argument, NULL, 'm'},
349 {"pretty", no_argument, NULL, 'p'}, 349 {"pretty", no_argument, NULL, 'p'},
diff --git a/swaynag/config.c b/swaynag/config.c
index ca7f4eb2..6db7cce5 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -51,7 +51,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
51 TO_PADDING_BTN, 51 TO_PADDING_BTN,
52 }; 52 };
53 53
54 static struct option opts[] = { 54 static const struct option opts[] = {
55 {"button", required_argument, NULL, 'b'}, 55 {"button", required_argument, NULL, 'b'},
56 {"button-no-terminal", required_argument, NULL, 'B'}, 56 {"button-no-terminal", required_argument, NULL, 'B'},
57 {"button-dismiss", required_argument, NULL, 'z'}, 57 {"button-dismiss", required_argument, NULL, 'z'},
@@ -59,6 +59,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
59 {"config", required_argument, NULL, 'c'}, 59 {"config", required_argument, NULL, 'c'},
60 {"debug", no_argument, NULL, 'd'}, 60 {"debug", no_argument, NULL, 'd'},
61 {"edge", required_argument, NULL, 'e'}, 61 {"edge", required_argument, NULL, 'e'},
62 {"layer", required_argument, NULL, 'y'},
62 {"font", required_argument, NULL, 'f'}, 63 {"font", required_argument, NULL, 'f'},
63 {"help", no_argument, NULL, 'h'}, 64 {"help", no_argument, NULL, 'h'},
64 {"detailed-message", no_argument, NULL, 'l'}, 65 {"detailed-message", no_argument, NULL, 'l'},
@@ -104,6 +105,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
104 " -c, --config <path> Path to config file.\n" 105 " -c, --config <path> Path to config file.\n"
105 " -d, --debug Enable debugging.\n" 106 " -d, --debug Enable debugging.\n"
106 " -e, --edge top|bottom Set the edge to use.\n" 107 " -e, --edge top|bottom Set the edge to use.\n"
108 " -y, --layer overlay|top|bottom|background\n"
109 " Set the layer to use.\n"
107 " -f, --font <font> Set the font to use.\n" 110 " -f, --font <font> Set the font to use.\n"
108 " -h, --help Show help message and quit.\n" 111 " -h, --help Show help message and quit.\n"
109 " -l, --detailed-message Read a detailed message from stdin.\n" 112 " -l, --detailed-message Read a detailed message from stdin.\n"
@@ -133,7 +136,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
133 136
134 optind = 1; 137 optind = 1;
135 while (1) { 138 while (1) {
136 int c = getopt_long(argc, argv, "b:B:z:Z:c:de:f:hlL:m:o:s:t:v", opts, NULL); 139 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) { 140 if (c == -1) {
138 break; 141 break;
139 } 142 }
@@ -184,6 +187,24 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
184 } 187 }
185 } 188 }
186 break; 189 break;
190 case 'y': // Layer
191 if (type) {
192 if (strcmp(optarg, "background") == 0) {
193 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
194 } else if (strcmp(optarg, "bottom") == 0) {
195 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
196 } else if (strcmp(optarg, "top") == 0) {
197 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
198 } else if (strcmp(optarg, "overlay") == 0) {
199 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
200 } else {
201 fprintf(stderr, "Invalid layer: %s\n"
202 "Usage: --layer overlay|top|bottom|background\n",
203 optarg);
204 return EXIT_FAILURE;
205 }
206 }
207 break;
187 case 'f': // Font 208 case 'f': // Font
188 if (type) { 209 if (type) {
189 free(type->font); 210 free(type->font);
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..6d4a7a58 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -30,8 +30,8 @@ static bool terminal_execute(char *terminal, char *command) {
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 char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1));
32 sprintf(cmd, "%s -e %s", terminal, fname); 32 sprintf(cmd, "%s -e %s", terminal, fname);
33 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 33 execlp("sh", "sh", "-c", cmd, NULL);
34 sway_log_errno(SWAY_ERROR, "Failed to run command, execl() returned."); 34 sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned.");
35 free(cmd); 35 free(cmd);
36 return false; 36 return false;
37} 37}
@@ -69,8 +69,8 @@ static void swaynag_button_execute(struct swaynag *swaynag,
69 sway_log(SWAY_DEBUG, 69 sway_log(SWAY_DEBUG,
70 "$TERMINAL not found. Running directly"); 70 "$TERMINAL not found. Running directly");
71 } 71 }
72 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); 72 execlp("sh", "sh", "-c", button->action, NULL);
73 sway_log_errno(SWAY_DEBUG, "execl failed"); 73 sway_log_errno(SWAY_DEBUG, "execlp failed");
74 _exit(EXIT_FAILURE); 74 _exit(EXIT_FAILURE);
75 } 75 }
76 } 76 }
@@ -103,7 +103,7 @@ static void layer_surface_closed(void *data,
103 swaynag_destroy(swaynag); 103 swaynag_destroy(swaynag);
104} 104}
105 105
106static struct zwlr_layer_surface_v1_listener layer_surface_listener = { 106static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
107 .configure = layer_surface_configure, 107 .configure = layer_surface_configure,
108 .closed = layer_surface_closed, 108 .closed = layer_surface_closed,
109}; 109};
@@ -124,7 +124,7 @@ static void surface_enter(void *data, struct wl_surface *surface,
124 }; 124 };
125} 125}
126 126
127static struct wl_surface_listener surface_listener = { 127static const struct wl_surface_listener surface_listener = {
128 .enter = surface_enter, 128 .enter = surface_enter,
129 .leave = nop, 129 .leave = nop,
130}; 130};
@@ -178,6 +178,8 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
178 wl_fixed_t surface_x, wl_fixed_t surface_y) { 178 wl_fixed_t surface_x, wl_fixed_t surface_y) {
179 struct swaynag_seat *seat = data; 179 struct swaynag_seat *seat = data;
180 struct swaynag_pointer *pointer = &seat->pointer; 180 struct swaynag_pointer *pointer = &seat->pointer;
181 pointer->x = wl_fixed_to_int(surface_x);
182 pointer->y = wl_fixed_to_int(surface_y);
181 pointer->serial = serial; 183 pointer->serial = serial;
182 update_cursor(seat); 184 update_cursor(seat);
183} 185}
@@ -198,8 +200,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
198 return; 200 return;
199 } 201 }
200 202
201 double x = seat->pointer.x * swaynag->scale; 203 double x = seat->pointer.x;
202 double y = seat->pointer.y * swaynag->scale; 204 double y = seat->pointer.y;
203 for (int i = 0; i < swaynag->buttons->length; i++) { 205 for (int i = 0; i < swaynag->buttons->length; i++) {
204 struct swaynag_button *nagbutton = swaynag->buttons->items[i]; 206 struct swaynag_button *nagbutton = swaynag->buttons->items[i];
205 if (x >= nagbutton->x 207 if (x >= nagbutton->x
@@ -263,7 +265,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
263 render_frame(swaynag); 265 render_frame(swaynag);
264} 266}
265 267
266static struct wl_pointer_listener pointer_listener = { 268static const struct wl_pointer_listener pointer_listener = {
267 .enter = wl_pointer_enter, 269 .enter = wl_pointer_enter,
268 .leave = nop, 270 .leave = nop,
269 .motion = wl_pointer_motion, 271 .motion = wl_pointer_motion,
@@ -289,7 +291,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
289 } 291 }
290} 292}
291 293
292const struct wl_seat_listener seat_listener = { 294static const struct wl_seat_listener seat_listener = {
293 .capabilities = seat_handle_capabilities, 295 .capabilities = seat_handle_capabilities,
294 .name = nop, 296 .name = nop,
295}; 297};
@@ -305,7 +307,7 @@ static void output_scale(void *data, struct wl_output *output,
305 } 307 }
306} 308}
307 309
308static struct wl_output_listener output_listener = { 310static const struct wl_output_listener output_listener = {
309 .geometry = nop, 311 .geometry = nop,
310 .mode = nop, 312 .mode = nop,
311 .done = nop, 313 .done = nop,
@@ -327,7 +329,7 @@ static void xdg_output_handle_name(void *data,
327 swaynag_output->swaynag->querying_outputs--; 329 swaynag_output->swaynag->querying_outputs--;
328} 330}
329 331
330static struct zxdg_output_v1_listener xdg_output_listener = { 332static const struct zxdg_output_v1_listener xdg_output_listener = {
331 .logical_position = nop, 333 .logical_position = nop,
332 .logical_size = nop, 334 .logical_size = nop,
333 .done = nop, 335 .done = nop,
@@ -474,7 +476,8 @@ void swaynag_setup(struct swaynag *swaynag) {
474 swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( 476 swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
475 swaynag->layer_shell, swaynag->surface, 477 swaynag->layer_shell, swaynag->surface,
476 swaynag->output ? swaynag->output->wl_output : NULL, 478 swaynag->output ? swaynag->output->wl_output : NULL,
477 ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); 479 swaynag->type->layer,
480 "swaynag");
478 assert(swaynag->layer_surface); 481 assert(swaynag->layer_surface);
479 zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, 482 zwlr_layer_surface_v1_add_listener(swaynag->layer_surface,
480 &layer_surface_listener, swaynag); 483 &layer_surface_listener, swaynag);
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;