aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/alpine.yml4
-rw-r--r--.builds/archlinux.yml2
-rw-r--r--.builds/freebsd.yml1
-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.md6
-rw-r--r--README.es.md6
-rw-r--r--README.fr.md64
-rw-r--r--README.hu.md77
-rw-r--r--README.ja.md4
-rw-r--r--README.ko.md4
-rw-r--r--README.md14
-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.c4
-rw-r--r--config.in2
-rwxr-xr-xcontrib/autoname-workspaces.py28
-rwxr-xr-xcontrib/grimshot4
-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.h2
-rw-r--r--include/pool-buffer.h2
-rw-r--r--include/sway/commands.h6
-rw-r--r--include/sway/config.h10
-rw-r--r--include/sway/desktop/idle_inhibit_v1.h1
-rw-r--r--include/sway/desktop/transaction.h12
-rw-r--r--include/sway/input/libinput.h2
-rw-r--r--include/sway/input/text_input.h3
-rw-r--r--include/sway/output.h4
-rw-r--r--include/sway/server.h23
-rw-r--r--include/sway/tree/container.h52
-rw-r--r--include/sway/tree/view.h3
-rw-r--r--include/sway/tree/workspace.h6
-rw-r--r--include/swaynag/types.h1
-rw-r--r--meson.build70
-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.c3
-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/fullscreen.c34
-rw-r--r--sway/commands/input.c4
-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.c2
-rw-r--r--sway/commands/output/dpms.c25
-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.c2
-rw-r--r--sway/commands/sticky.c6
-rw-r--r--sway/commands/swap.c56
-rw-r--r--sway/commands/unmark.c2
-rw-r--r--sway/commands/workspace.c12
-rw-r--r--sway/config.c71
-rw-r--r--sway/config/bar.c3
-rw-r--r--sway/config/output.c2
-rw-r--r--sway/criteria.c4
-rw-r--r--sway/desktop/idle_inhibit_v1.c8
-rw-r--r--sway/desktop/layer_shell.c28
-rw-r--r--sway/desktop/output.c158
-rw-r--r--sway/desktop/render.c124
-rw-r--r--sway/desktop/transaction.c253
-rw-r--r--sway/desktop/xdg_shell.c23
-rw-r--r--sway/desktop/xwayland.c63
-rw-r--r--sway/input/cursor.c29
-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.c43
-rw-r--r--sway/input/seatop_down.c2
-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.c132
-rw-r--r--sway/main.c42
-rw-r--r--sway/meson.build3
-rw-r--r--sway/server.c18
-rw-r--r--sway/sway-input.5.scd3
-rw-r--r--sway/sway-ipc.7.scd3
-rw-r--r--sway/sway-output.5.scd4
-rw-r--r--sway/swaynag.c4
-rw-r--r--sway/tree/arrange.c70
-rw-r--r--sway/tree/container.c518
-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.c320
-rw-r--r--sway/tree/workspace.c47
-rw-r--r--sway/xdg_activation_v1.c20
-rw-r--r--swaybar/bar.c6
-rw-r--r--swaybar/input.c4
-rw-r--r--swaybar/ipc.c20
-rw-r--r--swaybar/main.c2
-rw-r--r--swaybar/render.c134
-rw-r--r--swaybar/tray/item.c11
-rw-r--r--swaymsg/main.c2
-rw-r--r--swaynag/config.c25
-rw-r--r--swaynag/render.c2
-rw-r--r--swaynag/swaynag.1.scd3
-rw-r--r--swaynag/swaynag.5.scd5
-rw-r--r--swaynag/swaynag.c25
-rw-r--r--swaynag/types.c6
127 files changed, 2394 insertions, 1610 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index dc5e7c11..85538319 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
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index c0f70186..05ceef8d 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
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index 273badbc..4698dbc7 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
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..142c738c 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/?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..79a0df93 100644
--- a/README.dk.md
+++ b/README.dk.md
@@ -2,18 +2,18 @@
2 2
3Sway er en [i3](https://i3wm.org/)-kompatibel [Wayland](http://wayland.freedesktop.org/) compositor. 3Sway er en [i3](https://i3wm.org/)-kompatibel [Wayland](http://wayland.freedesktop.org/) compositor.
4Læs [Ofte stillede spørgsmål](https://github.com/swaywm/sway/wiki). 4Læs [Ofte stillede spørgsmål](https://github.com/swaywm/sway/wiki).
5Deltag på [IRC kanalen](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway på irc.freenode.net). 5Deltag på [IRC kanalen](https://web.libera.chat/?channels=#sway) (#sway på irc.libera.chat).
6 6
7## Udgivelses Signaturer 7## Udgivelses Signaturer
8 8
9Udgivelser er signeret med [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 9Udgivelser er signeret med [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
10og publiseret på [GitHub](https://github.com/swaywm/sway/releases). 10og publiseret på [GitHub](https://github.com/swaywm/sway/releases).
11 11
12## Installation 12## Installation
13 13
14### Fra Pakker 14### Fra Pakker
15 15
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) 16Sway er tilgængelig i mange distributioner. Prøv at installere pakken "sway". Hvis den ikke er tilgængelig, så tjek [denne wiki-side](https://github.com/swaywm/sway/wiki/Unsupported-packages)
17for information om installation til din(e) distribution(er). 17for information om installation til din(e) distribution(er).
18 18
19Hvis du er interesseret i at lave en Sway pakke til din distribution, burde du besøge IRC 19Hvis du er interesseret i at lave en Sway pakke til din distribution, burde du besøge IRC
diff --git a/README.es.md b/README.es.md
index 53f11f68..838778d4 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/?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..86d434e5 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/?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.hu.md b/README.hu.md
new file mode 100644
index 00000000..f673627a
--- /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/?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.ja.md b/README.ja.md
index fa28f3da..9e7daee1 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/?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..4d5619de 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/?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..e86dab14 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] - [Danish][dk] - [한국어][ko] - [Română][ro] - [Magyar][hu] - [Türkçe][tr]
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,9 +42,9 @@ _\*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, you need to suid the sway binary:
50 50
@@ -79,10 +79,12 @@ 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
82[i3]: https://i3wm.org/ 84[i3]: https://i3wm.org/
83[Wayland]: http://wayland.freedesktop.org/ 85[Wayland]: http://wayland.freedesktop.org/
84[FAQ]: https://github.com/swaywm/sway/wiki 86[FAQ]: https://github.com/swaywm/sway/wiki
85[IRC channel]: http://webchat.freenode.net/?channels=sway&uio=d4 87[IRC channel]: https://web.libera.chat/?channels=#sway
86[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 88[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
87[GitHub releases]: https://github.com/swaywm/sway/releases 89[GitHub releases]: https://github.com/swaywm/sway/releases
88[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 90[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
diff --git a/README.nl.md b/README.nl.md
index 86ebe398..a82a3d4c 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/?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..f981c741 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/?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..c33cdf04 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/?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..f3f4a32b 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/?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..257578a8 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/?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..ff603838
--- /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/?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..204fc40b 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/?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..3f25b6d9 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/?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..4834ded2 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/?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..dbc369dc 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
diff --git a/config.in b/config.in
index 08703bef..8031523e 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.
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..921837f9 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 ""
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..75dbba27 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/**
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..29a6bec3 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 */
diff --git a/include/sway/config.h b/include/sway/config.h
index 59f22ae2..254fbad0 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -292,6 +292,12 @@ struct workspace_config {
292 struct side_gaps gaps_outer; 292 struct side_gaps gaps_outer;
293}; 293};
294 294
295enum pango_markup_config {
296 PANGO_MARKUP_DISABLED = false,
297 PANGO_MARKUP_ENABLED = true,
298 PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix)
299};
300
295struct bar_config { 301struct bar_config {
296 char *swaybar_command; 302 char *swaybar_command;
297 struct wl_client *client; 303 struct wl_client *client;
@@ -323,7 +329,7 @@ struct bar_config {
323 char *position; 329 char *position;
324 list_t *bindings; 330 list_t *bindings;
325 char *status_command; 331 char *status_command;
326 bool pango_markup; 332 enum pango_markup_config pango_markup;
327 char *font; 333 char *font;
328 int height; // -1 not defined 334 int height; // -1 not defined
329 bool workspace_buttons; 335 bool workspace_buttons;
@@ -559,7 +565,7 @@ struct sway_config {
559 struct sway_node *node; 565 struct sway_node *node;
560 struct sway_container *container; 566 struct sway_container *container;
561 struct sway_workspace *workspace; 567 struct sway_workspace *workspace;
562 bool using_criteria; 568 bool node_overridden; // True if the node is selected by means other than focus
563 struct { 569 struct {
564 int argc; 570 int argc;
565 char **argv; 571 char **argv;
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/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/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/output.h b/include/sway/output.h
index 96986700..0ebcc77d 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -72,8 +72,8 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
72void output_add_workspace(struct sway_output *output, 72void output_add_workspace(struct sway_output *output,
73 struct sway_workspace *workspace); 73 struct sway_workspace *workspace);
74 74
75typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, 75typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
76 struct wlr_surface *surface, struct wlr_box *box, float rotation, 76 struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box,
77 void *user_data); 77 void *user_data);
78 78
79void output_damage_whole(struct sway_output *output); 79void output_damage_whole(struct sway_output *output);
diff --git a/include/sway/server.h b/include/sway/server.h
index 0f5e3ab2..3a5670d9 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -23,6 +23,8 @@
23#include "sway/xwayland.h" 23#include "sway/xwayland.h"
24#endif 24#endif
25 25
26struct sway_transaction;
27
26struct sway_server { 28struct sway_server {
27 struct wl_display *wl_display; 29 struct wl_display *wl_display;
28 struct wl_event_loop *wl_event_loop; 30 struct wl_event_loop *wl_event_loop;
@@ -85,8 +87,25 @@ struct sway_server {
85 struct wlr_text_input_manager_v3 *text_input; 87 struct wlr_text_input_manager_v3 *text_input;
86 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 88 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
87 89
90 struct wlr_xdg_activation_v1 *xdg_activation_v1;
91 struct wl_listener xdg_activation_v1_request_activate;
92
93 // The timeout for transactions, after which a transaction is applied
94 // regardless of readiness.
88 size_t txn_timeout_ms; 95 size_t txn_timeout_ms;
89 list_t *transactions; 96
97 // Stores a transaction after it has been committed, but is waiting for
98 // views to ack the new dimensions before being applied. A queued
99 // transaction is frozen and must not have new instructions added to it.
100 struct sway_transaction *queued_transaction;
101
102 // Stores a pending transaction that will be committed once the existing
103 // queued transaction is applied and freed. The pending transaction can be
104 // updated with new instructions as needed.
105 struct sway_transaction *pending_transaction;
106
107 // Stores the nodes that have been marked as "dirty" and will be put into
108 // the pending transaction.
90 list_t *dirty_nodes; 109 list_t *dirty_nodes;
91}; 110};
92 111
@@ -125,5 +144,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data);
125void handle_server_decoration(struct wl_listener *listener, void *data); 144void handle_server_decoration(struct wl_listener *listener, void *data);
126void handle_xdg_decoration(struct wl_listener *listener, void *data); 145void handle_xdg_decoration(struct wl_listener *listener, void *data);
127void handle_pointer_constraint(struct wl_listener *listener, void *data); 146void handle_pointer_constraint(struct wl_listener *listener, void *data);
147void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
148 void *data);
128 149
129#endif 150#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 7e9df59f..ddb2d683 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -46,9 +46,9 @@ struct sway_container_state {
46 46
47 enum sway_fullscreen_mode fullscreen_mode; 47 enum sway_fullscreen_mode fullscreen_mode;
48 48
49 struct sway_workspace *workspace; 49 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
50 struct sway_container *parent; 50 struct sway_container *parent; // NULL if container in root of workspace
51 list_t *children; 51 list_t *children; // struct sway_container
52 52
53 struct sway_container *focused_inactive_child; 53 struct sway_container *focused_inactive_child;
54 bool focused; 54 bool focused;
@@ -60,6 +60,7 @@ struct sway_container_state {
60 bool border_left; 60 bool border_left;
61 bool border_right; 61 bool border_right;
62 62
63 // These are in layout coordinates.
63 double content_x, content_y; 64 double content_x, content_y;
64 double content_width, content_height; 65 double content_width, content_height;
65}; 66};
@@ -68,14 +69,12 @@ struct sway_container {
68 struct sway_node node; 69 struct sway_node node;
69 struct sway_view *view; 70 struct sway_view *view;
70 71
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; 72 struct sway_container_state current;
73 struct sway_container_state pending;
74 74
75 char *title; // The view's title (unformatted) 75 char *title; // The view's title (unformatted)
76 char *formatted_title; // The title displayed in the title bar 76 char *formatted_title; // The title displayed in the title bar
77 77
78 enum sway_container_layout layout;
79 enum sway_container_layout prev_split_layout; 78 enum sway_container_layout prev_split_layout;
80 79
81 // Whether stickiness has been enabled on this container. Use 80 // Whether stickiness has been enabled on this container. Use
@@ -86,11 +85,13 @@ struct sway_container {
86 // For C_ROOT, this has no meaning 85 // For C_ROOT, this has no meaning
87 // For other types, this is the position in layout coordinates 86 // For other types, this is the position in layout coordinates
88 // Includes borders 87 // Includes borders
89 double x, y;
90 double width, height;
91 double saved_x, saved_y; 88 double saved_x, saved_y;
92 double saved_width, saved_height; 89 double saved_width, saved_height;
93 90
91 // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD
92 // border which we use to restore when the view returns to SSD.
93 enum sway_container_border saved_border;
94
94 // The share of the space of parent container this container occupies 95 // The share of the space of parent container this container occupies
95 double width_fraction; 96 double width_fraction;
96 double height_fraction; 97 double height_fraction;
@@ -100,33 +101,11 @@ struct sway_container {
100 double child_total_width; 101 double child_total_width;
101 double child_total_height; 102 double child_total_height;
102 103
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 104 // 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. 105 // refuses to resize to the content dimensions then it can be smaller.
109 // These are in layout coordinates. 106 // These are in layout coordinates.
110 double surface_x, surface_y; 107 double surface_x, surface_y;
111 108
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 109 // Outputs currently being intersected
131 list_t *outputs; // struct sway_output 110 list_t *outputs; // struct sway_output
132 111
@@ -185,6 +164,11 @@ void container_for_each_child(struct sway_container *container,
185 void (*f)(struct sway_container *container, void *data), void *data); 164 void (*f)(struct sway_container *container, void *data), void *data);
186 165
187/** 166/**
167 * Returns the fullscreen container obstructing this container if it exists.
168 */
169struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container);
170
171/**
188 * Returns true if the given container is an ancestor of this container. 172 * Returns true if the given container is an ancestor of this container.
189 */ 173 */
190bool container_has_ancestor(struct sway_container *container, 174bool container_has_ancestor(struct sway_container *container,
@@ -231,10 +215,17 @@ void container_set_geometry_from_content(struct sway_container *con);
231/** 215/**
232 * Determine if the given container is itself floating. 216 * Determine if the given container is itself floating.
233 * This will return false for any descendants of a floating container. 217 * This will return false for any descendants of a floating container.
218 *
219 * Uses pending container state.
234 */ 220 */
235bool container_is_floating(struct sway_container *container); 221bool container_is_floating(struct sway_container *container);
236 222
237/** 223/**
224 * Same as above, but for current container state.
225 */
226bool container_is_current_floating(struct sway_container *container);
227
228/**
238 * Get a container's box in layout coordinates. 229 * Get a container's box in layout coordinates.
239 */ 230 */
240void container_get_box(struct sway_container *container, struct wlr_box *box); 231void container_get_box(struct sway_container *container, struct wlr_box *box);
@@ -299,6 +290,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container);
299/** 290/**
300 * Return the output which will be used for scale purposes. 291 * Return the output which will be used for scale purposes.
301 * This is the most recently entered output. 292 * This is the most recently entered output.
293 * If the container is not on any output, return NULL.
302 */ 294 */
303struct sway_output *container_get_effective_output(struct sway_container *con); 295struct sway_output *container_get_effective_output(struct sway_container *con);
304 296
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index e071e6c9..86bd981c 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -316,7 +316,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
316 316
317void view_unmap(struct sway_view *view); 317void view_unmap(struct sway_view *view);
318 318
319void view_update_size(struct sway_view *view, int width, int height); 319void view_update_size(struct sway_view *view);
320void view_center_surface(struct sway_view *view);
320 321
321void view_child_init(struct sway_view_child *child, 322void view_child_init(struct sway_view_child *child,
322 const struct sway_view_child_impl *impl, struct sway_view *view, 323 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..65ba247f 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -60,8 +60,10 @@ 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
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/meson.build b/meson.build
index 38a55678..3681547d 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
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.53.0',
7 default_options: [ 7 default_options: [
@@ -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.14.0', '<0.15.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
@@ -313,12 +310,7 @@ endif
313summary({ 310summary({
314 'xwayland': have_xwayland, 311 'xwayland': have_xwayland,
315 'gdk-pixbuf': gdk_pixbuf.found(), 312 'gdk-pixbuf': gdk_pixbuf.found(),
316 'sd-bus': sdbus.found(),
317 'tray': have_tray, 313 'tray': have_tray,
318 'man-pages': scdoc.found(), 314 'man-pages': scdoc.found(),
319}, bool_yn: true) 315}, bool_yn: true)
320 316
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/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..4c67b3ce 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"
@@ -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/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/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/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..4418f23f 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 },
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/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..3e25c6f7 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 }
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/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..1e4edb38 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -182,9 +182,9 @@ 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 }
@@ -213,6 +213,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
213 ws = workspace_create(NULL, name); 213 ws = workspace_create(NULL, name);
214 free(name); 214 free(name);
215 } 215 }
216 if (ws && auto_back_and_forth) {
217 ws = workspace_auto_back_and_forth(ws);
218 }
216 } else if (strcasecmp(argv[0], "next") == 0 || 219 } else if (strcasecmp(argv[0], "next") == 0 ||
217 strcasecmp(argv[0], "prev") == 0 || 220 strcasecmp(argv[0], "prev") == 0 ||
218 strcasecmp(argv[0], "current") == 0) { 221 strcasecmp(argv[0], "current") == 0) {
@@ -235,11 +238,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
235 ws = workspace_create(NULL, name); 238 ws = workspace_create(NULL, name);
236 } 239 }
237 free(name); 240 free(name);
241 if (ws && auto_back_and_forth) {
242 ws = workspace_auto_back_and_forth(ws);
243 }
238 } 244 }
239 if (!ws) { 245 if (!ws) {
240 return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); 246 return cmd_results_new(CMD_FAILURE, "No workspace to switch to");
241 } 247 }
242 workspace_switch(ws, no_auto_back_and_forth); 248 workspace_switch(ws);
243 seat_consider_warp_to_focus(seat); 249 seat_consider_warp_to_focus(seat);
244 } 250 }
245 return cmd_results_new(CMD_SUCCESS, NULL); 251 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/config.c b/sway/config.c
index 6e665434..39013865 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"
@@ -338,35 +338,60 @@ static bool file_exists(const char *path) {
338 return path && access(path, R_OK) != -1; 338 return path && access(path, R_OK) != -1;
339} 339}
340 340
341static char *config_path(const char *prefix, const char *config_folder) {
342 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
343 return NULL;
344 }
345
346 const char *filename = "config";
347
348 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
349 char *path = calloc(size, sizeof(char));
350 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
351 return path;
352}
353
341static char *get_config_path(void) { 354static char *get_config_path(void) {
342 static const char *config_paths[] = { 355 char *path = NULL;
343 "$HOME/.sway/config", 356 const char *home = getenv("HOME");
344 "$XDG_CONFIG_HOME/sway/config", 357 size_t size_fallback = 1 + strlen(home) + strlen("/.config");
345 "$HOME/.i3/config", 358 char *config_home_fallback = calloc(size_fallback, sizeof(char));
346 "$XDG_CONFIG_HOME/i3/config", 359 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
347 SYSCONFDIR "/sway/config", 360
348 SYSCONFDIR "/i3/config", 361 const char *config_home = getenv("XDG_CONFIG_HOME");
362 if (config_home == NULL || config_home[0] == '\0') {
363 config_home = config_home_fallback;
364 }
365
366 struct config_path {
367 const char *prefix;
368 const char *config_folder;
349 }; 369 };
350 370
351 char *config_home = getenv("XDG_CONFIG_HOME"); 371 struct config_path config_paths[] = {
352 if (!config_home || !*config_home) { 372 { .prefix = home, .config_folder = ".sway"},
353 config_paths[1] = "$HOME/.config/sway/config"; 373 { .prefix = config_home, .config_folder = "sway"},
354 config_paths[3] = "$HOME/.config/i3/config"; 374 { .prefix = home, .config_folder = ".i3"},
355 } 375 { .prefix = config_home, .config_folder = "i3"},
376 { .prefix = SYSCONFDIR, .config_folder = "sway"},
377 { .prefix = SYSCONFDIR, .config_folder = "i3"}
378 };
356 379
357 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { 380 size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);
358 wordexp_t p; 381 for (size_t i = 0; i < num_config_paths; i++) {
359 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { 382 path = config_path(config_paths[i].prefix, config_paths[i].config_folder);
360 char *path = strdup(p.we_wordv[0]); 383 if (!path) {
361 wordfree(&p); 384 continue;
362 if (file_exists(path)) {
363 return path;
364 }
365 free(path);
366 } 385 }
386 if (file_exists(path)) {
387 break;
388 }
389 free(path);
390 path = NULL;
367 } 391 }
368 392
369 return NULL; 393 free(config_home_fallback);
394 return path;
370} 395}
371 396
372static bool load_config(const char *path, struct sway_config *config, 397static bool load_config(const char *path, struct sway_config *config,
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..7d0ed395 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -483,6 +483,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 483 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present. 484 // dependent on an output being present.
485 input_manager_configure_all_inputs(); 485 input_manager_configure_all_inputs();
486 // Reconfigure the cursor images, since the scale may have changed.
487 input_manager_configure_xcursor();
486 return true; 488 return true;
487} 489}
488 490
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..19718991 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -115,9 +115,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
115 // Horizontal axis 115 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT 116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; 117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if ((state->anchor & both_horiz) && box.width == 0) { 118 if (box.width == 0) {
119 box.x = bounds.x; 119 box.x = bounds.x;
120 box.width = bounds.width; 120 } else if ((state->anchor & both_horiz) == both_horiz) {
121 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
122 box.x = bounds.x; 123 box.x = bounds.x;
123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 124 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
@@ -128,9 +129,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
128 // Vertical axis 129 // Vertical axis
129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP 130 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; 131 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
131 if ((state->anchor & both_vert) && box.height == 0) { 132 if (box.height == 0) {
132 box.y = bounds.y; 133 box.y = bounds.y;
133 box.height = bounds.height; 134 } else if ((state->anchor & both_vert) == both_vert) {
135 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
134 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
135 box.y = bounds.y; 137 box.y = bounds.y;
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 138 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
@@ -139,17 +141,23 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
139 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); 141 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
140 } 142 }
141 // Margin 143 // Margin
142 if ((state->anchor & both_horiz) == both_horiz) { 144 if (box.width == 0) {
143 box.x += state->margin.left; 145 box.x += state->margin.left;
144 box.width -= state->margin.left + state->margin.right; 146 box.width = bounds.width -
147 (state->margin.left + state->margin.right);
148 } else if ((state->anchor & both_horiz) == both_horiz) {
149 // don't apply margins
145 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 150 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
146 box.x += state->margin.left; 151 box.x += state->margin.left;
147 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 152 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
148 box.x -= state->margin.right; 153 box.x -= state->margin.right;
149 } 154 }
150 if ((state->anchor & both_vert) == both_vert) { 155 if (box.height == 0) {
151 box.y += state->margin.top; 156 box.y += state->margin.top;
152 box.height -= state->margin.top + state->margin.bottom; 157 box.height = bounds.height -
158 (state->margin.top + state->margin.bottom);
159 } else if ((state->anchor & both_vert) == both_vert) {
160 // don't apply margins
153 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 161 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
154 box.y += state->margin.top; 162 box.y += state->margin.top;
155 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 163 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
@@ -191,7 +199,7 @@ void arrange_layers(struct sway_output *output) {
191 arrange_output(output); 199 arrange_output(output);
192 } 200 }
193 201
194 // Arrange non-exlusive surfaces from top->bottom 202 // Arrange non-exclusive surfaces from top->bottom
195 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 203 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
196 &usable_area, false); 204 &usable_area, false);
197 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 205 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
@@ -429,7 +437,7 @@ static struct sway_layer_subsurface *create_subsurface(
429 struct wlr_subsurface *wlr_subsurface, 437 struct wlr_subsurface *wlr_subsurface,
430 struct sway_layer_surface *layer_surface) { 438 struct sway_layer_surface *layer_surface) {
431 struct sway_layer_subsurface *subsurface = 439 struct sway_layer_subsurface *subsurface =
432 calloc(1, sizeof(struct sway_layer_surface)); 440 calloc(1, sizeof(struct sway_layer_subsurface));
433 if (subsurface == NULL) { 441 if (subsurface == NULL) {
434 return NULL; 442 return NULL;
435 } 443 }
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 5edc8f96..aa148290 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -56,26 +56,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
56 return NULL; 56 return NULL;
57} 57}
58 58
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 { 59struct surface_iterator_data {
80 sway_surface_iterator_func_t user_iterator; 60 sway_surface_iterator_func_t user_iterator;
81 void *user_data; 61 void *user_data;
@@ -84,7 +64,6 @@ struct surface_iterator_data {
84 struct sway_view *view; 64 struct sway_view *view;
85 double ox, oy; 65 double ox, oy;
86 int width, height; 66 int width, height;
87 float rotation;
88}; 67};
89 68
90static bool get_surface_box(struct surface_iterator_data *data, 69static bool get_surface_box(struct surface_iterator_data *data,
@@ -99,14 +78,9 @@ static bool get_surface_box(struct surface_iterator_data *data,
99 int sw = surface->current.width; 78 int sw = surface->current.width;
100 int sh = surface->current.height; 79 int sh = surface->current.height;
101 80
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 = { 81 struct wlr_box box = {
108 .x = data->ox + _sx, 82 .x = floor(data->ox + sx),
109 .y = data->oy + _sy, 83 .y = floor(data->oy + sy),
110 .width = sw, 84 .width = sw,
111 .height = sh, 85 .height = sh,
112 }; 86 };
@@ -114,16 +88,13 @@ static bool get_surface_box(struct surface_iterator_data *data,
114 memcpy(surface_box, &box, sizeof(struct wlr_box)); 88 memcpy(surface_box, &box, sizeof(struct wlr_box));
115 } 89 }
116 90
117 struct wlr_box rotated_box;
118 wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
119
120 struct wlr_box output_box = { 91 struct wlr_box output_box = {
121 .width = output->width, 92 .width = output->width,
122 .height = output->height, 93 .height = output->height,
123 }; 94 };
124 95
125 struct wlr_box intersection; 96 struct wlr_box intersection;
126 return wlr_box_intersection(&intersection, &output_box, &rotated_box); 97 return wlr_box_intersection(&intersection, &output_box, &box);
127} 98}
128 99
129static void output_for_each_surface_iterator(struct wlr_surface *surface, 100static void output_for_each_surface_iterator(struct wlr_surface *surface,
@@ -136,7 +107,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface,
136 return; 107 return;
137 } 108 }
138 109
139 data->user_iterator(data->output, data->view, surface, &box, data->rotation, 110 data->user_iterator(data->output, data->view, surface, &box,
140 data->user_data); 111 data->user_data);
141} 112}
142 113
@@ -152,7 +123,6 @@ void output_surface_for_each_surface(struct sway_output *output,
152 .oy = oy, 123 .oy = oy,
153 .width = surface->current.width, 124 .width = surface->current.width,
154 .height = surface->current.height, 125 .height = surface->current.height,
155 .rotation = 0,
156 }; 126 };
157 127
158 wlr_surface_for_each_surface(surface, 128 wlr_surface_for_each_surface(surface,
@@ -173,7 +143,6 @@ void output_view_for_each_surface(struct sway_output *output,
173 - view->geometry.y, 143 - view->geometry.y,
174 .width = view->container->current.content_width, 144 .width = view->container->current.content_width,
175 .height = view->container->current.content_height, 145 .height = view->container->current.content_height,
176 .rotation = 0, // TODO
177 }; 146 };
178 147
179 view_for_each_surface(view, output_for_each_surface_iterator, &data); 148 view_for_each_surface(view, output_for_each_surface_iterator, &data);
@@ -193,7 +162,6 @@ void output_view_for_each_popup_surface(struct sway_output *output,
193 - view->geometry.y, 162 - view->geometry.y,
194 .width = view->container->current.content_width, 163 .width = view->container->current.content_width,
195 .height = view->container->current.content_height, 164 .height = view->container->current.content_height,
196 .rotation = 0, // TODO
197 }; 165 };
198 166
199 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); 167 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
@@ -206,40 +174,19 @@ void output_layer_for_each_surface(struct sway_output *output,
206 wl_list_for_each(layer_surface, layer_surfaces, link) { 174 wl_list_for_each(layer_surface, layer_surfaces, link) {
207 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 175 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
208 layer_surface->layer_surface; 176 layer_surface->layer_surface;
209 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, 177 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
210 layer_surface->geo.x, layer_surface->geo.y, iterator, 178 struct surface_iterator_data data = {
211 user_data); 179 .user_iterator = iterator,
212 180 .user_data = user_data,
213 struct wlr_xdg_popup *state; 181 .output = output,
214 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 182 .view = NULL,
215 struct wlr_xdg_surface *popup = state->base; 183 .ox = layer_surface->geo.x,
216 if (!popup->configured) { 184 .oy = layer_surface->geo.y,
217 continue; 185 .width = surface->current.width,
218 } 186 .height = surface->current.height,
219 187 };
220 double popup_sx, popup_sy; 188 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
221 popup_sx = layer_surface->geo.x + 189 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 } 190 }
244} 191}
245 192
@@ -264,37 +211,19 @@ void output_layer_for_each_popup_surface(struct sway_output *output,
264 wl_list_for_each(layer_surface, layer_surfaces, link) { 211 wl_list_for_each(layer_surface, layer_surfaces, link) {
265 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 212 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
266 layer_surface->layer_surface; 213 layer_surface->layer_surface;
267 214 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
268 struct wlr_xdg_popup *state; 215 struct surface_iterator_data data = {
269 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 216 .user_iterator = iterator,
270 struct wlr_xdg_surface *popup = state->base; 217 .user_data = user_data,
271 if (!popup->configured) { 218 .output = output,
272 continue; 219 .view = NULL,
273 } 220 .ox = layer_surface->geo.x,
274 221 .oy = layer_surface->geo.y,
275 double popup_sx, popup_sy; 222 .width = surface->current.width,
276 popup_sx = layer_surface->geo.x + 223 .height = surface->current.height,
277 popup->popup->geometry.x - popup->geometry.x; 224 };
278 popup_sy = layer_surface->geo.y + 225 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
279 popup->popup->geometry.y - popup->geometry.y; 226 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 } 227 }
299} 228}
300 229
@@ -463,9 +392,9 @@ struct send_frame_done_data {
463 int msec_until_refresh; 392 int msec_until_refresh;
464}; 393};
465 394
466static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, 395static void send_frame_done_iterator(struct sway_output *output,
467 struct wlr_surface *surface, struct wlr_box *box, float rotation, 396 struct sway_view *view, struct wlr_surface *surface,
468 void *user_data) { 397 struct wlr_box *box, void *user_data) {
469 int view_max_render_time = 0; 398 int view_max_render_time = 0;
470 if (view != NULL) { 399 if (view != NULL) {
471 view_max_render_time = view->max_render_time; 400 view_max_render_time = view->max_render_time;
@@ -488,9 +417,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); 417 output_for_each_surface(output, send_frame_done_iterator, data);
489} 418}
490 419
491static void count_surface_iterator(struct sway_output *output, struct sway_view *view, 420static void count_surface_iterator(struct sway_output *output,
492 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 421 struct sway_view *view, struct wlr_surface *surface,
493 void *data) { 422 struct wlr_box *box, void *data) {
494 size_t *n = data; 423 size_t *n = data;
495 (*n)++; 424 (*n)++;
496} 425}
@@ -590,6 +519,7 @@ static int output_repaint_timer_handler(void *data) {
590 if (last_scanned_out && !scanned_out) { 519 if (last_scanned_out && !scanned_out) {
591 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 520 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s",
592 output->wlr_output->name); 521 output->wlr_output->name);
522 output_damage_whole(output);
593 } 523 }
594 last_scanned_out = scanned_out; 524 last_scanned_out = scanned_out;
595 525
@@ -693,18 +623,15 @@ void output_damage_whole(struct sway_output *output) {
693 } 623 }
694} 624}
695 625
696static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, 626static void damage_surface_iterator(struct sway_output *output,
697 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 627 struct sway_view *view, struct wlr_surface *surface,
698 void *_data) { 628 struct wlr_box *_box, void *_data) {
699 bool *data = _data; 629 bool *data = _data;
700 bool whole = *data; 630 bool whole = *data;
701 631
702 struct wlr_box box = *_box; 632 struct wlr_box box = *_box;
703 scale_box(&box, output->wlr_output->scale); 633 scale_box(&box, output->wlr_output->scale);
704 634
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)) { 635 if (pixman_region32_not_empty(&surface->buffer_damage)) {
709 pixman_region32_t damage; 636 pixman_region32_t damage;
710 pixman_region32_init(&damage); 637 pixman_region32_init(&damage);
@@ -717,14 +644,11 @@ static void damage_surface_iterator(struct sway_output *output, struct sway_view
717 ceil(output->wlr_output->scale) - surface->current.scale); 644 ceil(output->wlr_output->scale) - surface->current.scale);
718 } 645 }
719 pixman_region32_translate(&damage, box.x, box.y); 646 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); 647 wlr_output_damage_add(output->damage, &damage);
723 pixman_region32_fini(&damage); 648 pixman_region32_fini(&damage);
724 } 649 }
725 650
726 if (whole) { 651 if (whole) {
727 wlr_box_rotated_bounds(&box, &box, rotation);
728 wlr_output_damage_add_box(output->damage, &box); 652 wlr_output_damage_add_box(output->damage, &box);
729 } 653 }
730 654
@@ -816,7 +740,7 @@ static void update_output_manager_config(struct sway_server *server) {
816 struct wlr_box *output_box = wlr_output_layout_get_box( 740 struct wlr_box *output_box = wlr_output_layout_get_box(
817 root->output_layout, output->wlr_output); 741 root->output_layout, output->wlr_output);
818 // We mark the output enabled even if it is switched off by DPMS 742 // We mark the output enabled even if it is switched off by DPMS
819 config_head->state.enabled = output->enabled; 743 config_head->state.enabled = output->current_mode != NULL && output->enabled;
820 config_head->state.mode = output->current_mode; 744 config_head->state.mode = output->current_mode;
821 if (output_box) { 745 if (output_box) {
822 config_head->state.x = output_box->x; 746 config_head->state.x = output_box->x;
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index bd85282c..bf1b8666 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -32,6 +32,7 @@
32struct render_data { 32struct render_data {
33 pixman_region32_t *damage; 33 pixman_region32_t *damage;
34 float alpha; 34 float alpha;
35 struct wlr_box *clip_box;
35}; 36};
36 37
37/** 38/**
@@ -104,9 +105,6 @@ static void render_texture(struct wlr_output *wlr_output,
104 wlr_backend_get_renderer(wlr_output->backend); 105 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data; 106 struct sway_output *output = wlr_output->data;
106 107
107 struct wlr_gles2_texture_attribs attribs;
108 wlr_gles2_texture_get_attribs(texture, &attribs);
109
110 pixman_region32_t damage; 108 pixman_region32_t damage;
111 pixman_region32_init(&damage); 109 pixman_region32_init(&damage);
112 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, 110 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
@@ -133,9 +131,9 @@ damage_finish:
133 pixman_region32_fini(&damage); 131 pixman_region32_fini(&damage);
134} 132}
135 133
136static void render_surface_iterator(struct sway_output *output, struct sway_view *view, 134static void render_surface_iterator(struct sway_output *output,
137 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 135 struct sway_view *view, struct wlr_surface *surface,
138 void *_data) { 136 struct wlr_box *_box, void *_data) {
139 struct render_data *data = _data; 137 struct render_data *data = _data;
140 struct wlr_output *wlr_output = output->wlr_output; 138 struct wlr_output *wlr_output = output->wlr_output;
141 pixman_region32_t *output_damage = data->damage; 139 pixman_region32_t *output_damage = data->damage;
@@ -149,15 +147,23 @@ static void render_surface_iterator(struct sway_output *output, struct sway_view
149 struct wlr_fbox src_box; 147 struct wlr_fbox src_box;
150 wlr_surface_get_buffer_source_box(surface, &src_box); 148 wlr_surface_get_buffer_source_box(surface, &src_box);
151 149
152 struct wlr_box dst_box = *_box; 150 struct wlr_box proj_box = *_box;
153 scale_box(&dst_box, wlr_output->scale); 151 scale_box(&proj_box, wlr_output->scale);
154 152
155 float matrix[9]; 153 float matrix[9];
156 enum wl_output_transform transform = 154 enum wl_output_transform transform =
157 wlr_output_transform_invert(surface->current.transform); 155 wlr_output_transform_invert(surface->current.transform);
158 wlr_matrix_project_box(matrix, &dst_box, transform, rotation, 156 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
159 wlr_output->transform_matrix); 157 wlr_output->transform_matrix);
160 158
159 struct wlr_box dst_box = *_box;
160 struct wlr_box *clip_box = data->clip_box;
161 if (clip_box != NULL) {
162 dst_box.width = fmin(dst_box.width, clip_box->width);
163 dst_box.height = fmin(dst_box.height, clip_box->height);
164 }
165 scale_box(&dst_box, wlr_output->scale);
166
161 render_texture(wlr_output, output_damage, texture, 167 render_texture(wlr_output, output_damage, texture,
162 &src_box, &dst_box, matrix, alpha); 168 &src_box, &dst_box, matrix, alpha);
163 169
@@ -256,6 +262,14 @@ static void render_view_toplevels(struct sway_view *view,
256 .damage = damage, 262 .damage = damage,
257 .alpha = alpha, 263 .alpha = alpha,
258 }; 264 };
265 struct wlr_box clip_box;
266 if (!container_is_current_floating(view->container)) {
267 // As we pass the geometry offsets to the surface iterator, we will
268 // need to account for the offsets in the clip dimensions.
269 clip_box.width = view->container->current.content_width + view->geometry.x;
270 clip_box.height = view->container->current.content_height + view->geometry.y;
271 data.clip_box = &clip_box;
272 }
259 // Render all toplevels without descending into popups 273 // Render all toplevels without descending into popups
260 double ox = view->container->surface_x - 274 double ox = view->container->surface_x -
261 output->lx - view->geometry.x; 275 output->lx - view->geometry.x;
@@ -282,17 +296,18 @@ static void render_saved_view(struct sway_view *view,
282 if (wl_list_empty(&view->saved_buffers)) { 296 if (wl_list_empty(&view->saved_buffers)) {
283 return; 297 return;
284 } 298 }
299
300 bool floating = container_is_current_floating(view->container);
301
285 struct sway_saved_buffer *saved_buf; 302 struct sway_saved_buffer *saved_buf;
286 wl_list_for_each(saved_buf, &view->saved_buffers, link) { 303 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
287 if (!saved_buf->buffer->texture) { 304 if (!saved_buf->buffer->texture) {
288 continue; 305 continue;
289 } 306 }
290 307
291 struct wlr_box box = { 308 struct wlr_box proj_box = {
292 .x = view->container->surface_x - output->lx - 309 .x = saved_buf->x - view->saved_geometry.x - output->lx,
293 view->saved_geometry.x + saved_buf->x, 310 .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, 311 .width = saved_buf->width,
297 .height = saved_buf->height, 312 .height = saved_buf->height,
298 }; 313 };
@@ -303,20 +318,31 @@ static void render_saved_view(struct sway_view *view,
303 }; 318 };
304 319
305 struct wlr_box intersection; 320 struct wlr_box intersection;
306 bool intersects = wlr_box_intersection(&intersection, &output_box, &box); 321 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
307 if (!intersects) { 322 if (!intersects) {
308 continue; 323 continue;
309 } 324 }
310 325
311 scale_box(&box, wlr_output->scale); 326 struct wlr_box dst_box = proj_box;
327 scale_box(&proj_box, wlr_output->scale);
312 328
313 float matrix[9]; 329 float matrix[9];
314 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); 330 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
315 wlr_matrix_project_box(matrix, &box, transform, 0, 331 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
316 wlr_output->transform_matrix); 332 wlr_output->transform_matrix);
317 333
334 if (!floating) {
335 dst_box.width = fmin(dst_box.width,
336 view->container->current.content_width -
337 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
338 dst_box.height = fmin(dst_box.height,
339 view->container->current.content_height -
340 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
341 }
342 scale_box(&dst_box, wlr_output->scale);
343
318 render_texture(wlr_output, damage, saved_buf->buffer->texture, 344 render_texture(wlr_output, damage, saved_buf->buffer->texture,
319 &saved_buf->source_box, &box, matrix, alpha); 345 &saved_buf->source_box, &dst_box, matrix, alpha);
320 } 346 }
321 347
322 // FIXME: we should set the surface that this saved buffer originates from 348 // FIXME: we should set the surface that this saved buffer originates from
@@ -348,8 +374,8 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
348 if (state->border_left) { 374 if (state->border_left) {
349 memcpy(&color, colors->child_border, sizeof(float) * 4); 375 memcpy(&color, colors->child_border, sizeof(float) * 4);
350 premultiply_alpha(color, con->alpha); 376 premultiply_alpha(color, con->alpha);
351 box.x = state->x; 377 box.x = floor(state->x);
352 box.y = state->content_y; 378 box.y = floor(state->content_y);
353 box.width = state->border_thickness; 379 box.width = state->border_thickness;
354 box.height = state->content_height; 380 box.height = state->content_height;
355 scale_box(&box, output_scale); 381 scale_box(&box, output_scale);
@@ -361,14 +387,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
361 container_current_parent_layout(con); 387 container_current_parent_layout(con);
362 388
363 if (state->border_right) { 389 if (state->border_right) {
364 if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { 390 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
365 memcpy(&color, colors->indicator, sizeof(float) * 4); 391 memcpy(&color, colors->indicator, sizeof(float) * 4);
366 } else { 392 } else {
367 memcpy(&color, colors->child_border, sizeof(float) * 4); 393 memcpy(&color, colors->child_border, sizeof(float) * 4);
368 } 394 }
369 premultiply_alpha(color, con->alpha); 395 premultiply_alpha(color, con->alpha);
370 box.x = state->content_x + state->content_width; 396 box.x = floor(state->content_x + state->content_width);
371 box.y = state->content_y; 397 box.y = floor(state->content_y);
372 box.width = state->border_thickness; 398 box.width = state->border_thickness;
373 box.height = state->content_height; 399 box.height = state->content_height;
374 scale_box(&box, output_scale); 400 scale_box(&box, output_scale);
@@ -376,14 +402,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
376 } 402 }
377 403
378 if (state->border_bottom) { 404 if (state->border_bottom) {
379 if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { 405 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
380 memcpy(&color, colors->indicator, sizeof(float) * 4); 406 memcpy(&color, colors->indicator, sizeof(float) * 4);
381 } else { 407 } else {
382 memcpy(&color, colors->child_border, sizeof(float) * 4); 408 memcpy(&color, colors->child_border, sizeof(float) * 4);
383 } 409 }
384 premultiply_alpha(color, con->alpha); 410 premultiply_alpha(color, con->alpha);
385 box.x = state->x; 411 box.x = floor(state->x);
386 box.y = state->content_y + state->content_height; 412 box.y = floor(state->content_y + state->content_height);
387 box.width = state->width; 413 box.width = state->width;
388 box.height = state->border_thickness; 414 box.height = state->border_thickness;
389 scale_box(&box, output_scale); 415 scale_box(&box, output_scale);
@@ -464,9 +490,10 @@ static void render_titlebar(struct sway_output *output,
464 int ob_marks_x = 0; // output-buffer-local 490 int ob_marks_x = 0; // output-buffer-local
465 int ob_marks_width = 0; // output-buffer-local 491 int ob_marks_width = 0; // output-buffer-local
466 if (config->show_marks && marks_texture) { 492 if (config->show_marks && marks_texture) {
467 struct wlr_box texture_box; 493 struct wlr_box texture_box = {
468 wlr_texture_get_size(marks_texture, 494 .width = marks_texture->width,
469 &texture_box.width, &texture_box.height); 495 .height = marks_texture->height,
496 };
470 ob_marks_width = texture_box.width; 497 ob_marks_width = texture_box.width;
471 498
472 // The marks texture might be shorter than the config->font_height, in 499 // The marks texture might be shorter than the config->font_height, in
@@ -517,9 +544,18 @@ static void render_titlebar(struct sway_output *output,
517 int ob_title_x = 0; // output-buffer-local 544 int ob_title_x = 0; // output-buffer-local
518 int ob_title_width = 0; // output-buffer-local 545 int ob_title_width = 0; // output-buffer-local
519 if (title_texture) { 546 if (title_texture) {
520 struct wlr_box texture_box; 547 struct wlr_box texture_box = {
521 wlr_texture_get_size(title_texture, 548 .width = title_texture->width,
522 &texture_box.width, &texture_box.height); 549 .height = title_texture->height,
550 };
551
552 // The effective output may be NULL when con is not on any output.
553 // This can happen because we render all children of containers,
554 // even those that are out of the bounds of any output.
555 struct sway_output *effective = container_get_effective_output(con);
556 float title_scale = effective ? effective->wlr_output->scale : output_scale;
557 texture_box.width = texture_box.width * output_scale / title_scale;
558 texture_box.height = texture_box.height * output_scale / title_scale;
523 ob_title_width = texture_box.width; 559 ob_title_width = texture_box.width;
524 560
525 // The title texture might be shorter than the config->font_height, 561 // The title texture might be shorter than the config->font_height,
@@ -660,8 +696,8 @@ static void render_top_border(struct sway_output *output,
660 // Child border - top edge 696 // Child border - top edge
661 memcpy(&color, colors->child_border, sizeof(float) * 4); 697 memcpy(&color, colors->child_border, sizeof(float) * 4);
662 premultiply_alpha(color, con->alpha); 698 premultiply_alpha(color, con->alpha);
663 box.x = state->x; 699 box.x = floor(state->x);
664 box.y = state->y; 700 box.y = floor(state->y);
665 box.width = state->width; 701 box.width = state->width;
666 box.height = state->border_thickness; 702 box.height = state->border_thickness;
667 scale_box(&box, output_scale); 703 scale_box(&box, output_scale);
@@ -716,8 +752,8 @@ static void render_containers_linear(struct sway_output *output,
716 } 752 }
717 753
718 if (state->border == B_NORMAL) { 754 if (state->border == B_NORMAL) {
719 render_titlebar(output, damage, child, state->x, 755 render_titlebar(output, damage, child, floor(state->x),
720 state->y, state->width, colors, 756 floor(state->y), state->width, colors,
721 title_texture, marks_texture); 757 title_texture, marks_texture);
722 } else if (state->border == B_PIXEL) { 758 } else if (state->border == B_PIXEL) {
723 render_top_border(output, damage, child, colors); 759 render_top_border(output, damage, child, colors);
@@ -771,7 +807,7 @@ static void render_containers_tabbed(struct sway_output *output,
771 marks_texture = child->marks_unfocused; 807 marks_texture = child->marks_unfocused;
772 } 808 }
773 809
774 int x = cstate->x + tab_width * i; 810 int x = floor(cstate->x + tab_width * i);
775 811
776 // Make last tab use the remaining width of the parent 812 // Make last tab use the remaining width of the parent
777 if (i == parent->children->length - 1) { 813 if (i == parent->children->length - 1) {
@@ -884,8 +920,8 @@ static void render_container(struct sway_output *output,
884 struct parent_data data = { 920 struct parent_data data = {
885 .layout = con->current.layout, 921 .layout = con->current.layout,
886 .box = { 922 .box = {
887 .x = con->current.x, 923 .x = floor(con->current.x),
888 .y = con->current.y, 924 .y = floor(con->current.y),
889 .width = con->current.width, 925 .width = con->current.width,
890 .height = con->current.height, 926 .height = con->current.height,
891 }, 927 },
@@ -901,8 +937,8 @@ static void render_workspace(struct sway_output *output,
901 struct parent_data data = { 937 struct parent_data data = {
902 .layout = ws->current.layout, 938 .layout = ws->current.layout,
903 .box = { 939 .box = {
904 .x = ws->current.x, 940 .x = floor(ws->current.x),
905 .y = ws->current.y, 941 .y = floor(ws->current.y),
906 .width = ws->current.width, 942 .width = ws->current.width,
907 .height = ws->current.height, 943 .height = ws->current.height,
908 }, 944 },
@@ -936,8 +972,8 @@ static void render_floating_container(struct sway_output *soutput,
936 } 972 }
937 973
938 if (con->current.border == B_NORMAL) { 974 if (con->current.border == B_NORMAL) {
939 render_titlebar(soutput, damage, con, con->current.x, 975 render_titlebar(soutput, damage, con, floor(con->current.x),
940 con->current.y, con->current.width, colors, 976 floor(con->current.y), con->current.width, colors,
941 title_texture, marks_texture); 977 title_texture, marks_texture);
942 } else if (con->current.border == B_PIXEL) { 978 } else if (con->current.border == B_PIXEL) {
943 render_top_border(soutput, damage, con, colors); 979 render_top_border(soutput, damage, con, colors);
@@ -959,7 +995,7 @@ static void render_floating(struct sway_output *soutput,
959 } 995 }
960 for (int k = 0; k < ws->current.floating->length; ++k) { 996 for (int k = 0; k < ws->current.floating->length; ++k) {
961 struct sway_container *floater = ws->current.floating->items[k]; 997 struct sway_container *floater = ws->current.floating->items[k];
962 if (floater->fullscreen_mode != FULLSCREEN_NONE) { 998 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
963 continue; 999 continue;
964 } 1000 }
965 render_floating_container(soutput, damage, floater); 1001 render_floating_container(soutput, damage, floater);
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..d34654fd 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -70,13 +70,13 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
70 struct sway_view *view = popup->child.view; 70 struct sway_view *view = popup->child.view;
71 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 71 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
72 72
73 struct sway_output *output = view->container->workspace->output; 73 struct sway_output *output = view->container->pending.workspace->output;
74 74
75 // the output box expressed in the coordinate system of the toplevel parent 75 // the output box expressed in the coordinate system of the toplevel parent
76 // of the popup 76 // of the popup
77 struct wlr_box output_toplevel_sx_box = { 77 struct wlr_box output_toplevel_sx_box = {
78 .x = output->lx - view->container->content_x, 78 .x = output->lx - view->container->pending.content_x,
79 .y = output->ly - view->container->content_y, 79 .y = output->ly - view->container->pending.content_y,
80 .width = output->width, 80 .width = output->width,
81 .height = output->height, 81 .height = output->height,
82 }; 82 };
@@ -293,19 +293,23 @@ static void handle_commit(struct wl_listener *listener, void *data) {
293 new_geo.y != view->geometry.y; 293 new_geo.y != view->geometry.y;
294 294
295 if (new_size) { 295 if (new_size) {
296 // The view has unexpectedly sent a new size 296 // The client changed its surface size in this commit. For floating
297 // containers, we resize the container to match. For tiling containers,
298 // we only recenter the surface.
297 desktop_damage_view(view); 299 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)); 300 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
301 if (container_is_floating(view->container)) {
302 view_update_size(view);
303 transaction_commit_dirty_client();
304 } else {
305 view_center_surface(view);
306 }
300 desktop_damage_view(view); 307 desktop_damage_view(view);
301 transaction_commit_dirty();
302 } 308 }
303 309
304 if (view->container->node.instruction) { 310 if (view->container->node.instruction) {
305 transaction_notify_view_ready_by_serial(view, 311 transaction_notify_view_ready_by_serial(view,
306 xdg_surface->configure_serial); 312 xdg_surface->configure_serial);
307 } else if (new_size) {
308 transaction_notify_view_ready_immediately(view);
309 } 313 }
310 314
311 view_damage_from(view); 315 view_damage_from(view);
@@ -354,7 +358,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
354 if (e->fullscreen && e->output && e->output->data) { 358 if (e->fullscreen && e->output && e->output->data) {
355 struct sway_output *output = e->output->data; 359 struct sway_output *output = e->output->data;
356 struct sway_workspace *ws = output_get_active_workspace(output); 360 struct sway_workspace *ws = output_get_active_workspace(output);
357 if (ws && !container_is_scratchpad_hidden(container)) { 361 if (ws && !container_is_scratchpad_hidden(container) &&
362 container->pending.workspace != ws) {
358 if (container_is_floating(container)) { 363 if (container_is_floating(container)) {
359 workspace_add_floating(ws, container); 364 workspace_add_floating(ws, container);
360 } else { 365 } else {
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index e1a2e463..66cb3b02 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
@@ -399,30 +395,31 @@ static void handle_commit(struct wl_listener *listener, void *data) {
399 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 395 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
400 struct wlr_surface_state *state = &xsurface->surface->current; 396 struct wlr_surface_state *state = &xsurface->surface->current;
401 397
398 struct wlr_box new_geo;
399 get_geometry(view, &new_geo);
400 bool new_size = new_geo.width != view->geometry.width ||
401 new_geo.height != view->geometry.height ||
402 new_geo.x != view->geometry.x ||
403 new_geo.y != view->geometry.y;
404
405 if (new_size) {
406 // The client changed its surface size in this commit. For floating
407 // containers, we resize the container to match. For tiling containers,
408 // we only recenter the surface.
409 desktop_damage_view(view);
410 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
411 if (container_is_floating(view->container)) {
412 view_update_size(view);
413 transaction_commit_dirty_client();
414 } else {
415 view_center_surface(view);
416 }
417 desktop_damage_view(view);
418 }
419
402 if (view->container->node.instruction) { 420 if (view->container->node.instruction) {
403 get_geometry(view, &view->geometry);
404 transaction_notify_view_ready_by_geometry(view, 421 transaction_notify_view_ready_by_geometry(view,
405 xsurface->x, xsurface->y, state->width, state->height); 422 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 } 423 }
427 424
428 view_damage_from(view); 425 view_damage_from(view);
@@ -527,10 +524,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
527 view->natural_height = ev->height; 524 view->natural_height = ev->height;
528 container_floating_resize_and_center(view->container); 525 container_floating_resize_and_center(view->container);
529 526
530 configure(view, view->container->content_x, 527 configure(view, view->container->pending.content_x,
531 view->container->content_y, 528 view->container->pending.content_y,
532 view->container->content_width, 529 view->container->pending.content_width,
533 view->container->content_height); 530 view->container->pending.content_height);
534 node_set_dirty(&view->container->node); 531 node_set_dirty(&view->container->node);
535 } else { 532 } else {
536 configure(view, view->container->current.content_x, 533 configure(view, view->container->current.content_x,
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index fa604426..cbb5c6e9 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -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"
@@ -383,7 +382,6 @@ static void handle_pointer_motion_relative(
383 382
384 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, 383 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y,
385 e->unaccel_dx, e->unaccel_dy); 384 e->unaccel_dx, e->unaccel_dy);
386 transaction_commit_dirty();
387} 385}
388 386
389static void handle_pointer_motion_absolute( 387static void handle_pointer_motion_absolute(
@@ -401,7 +399,6 @@ static void handle_pointer_motion_absolute(
401 double dy = ly - cursor->cursor->y; 399 double dy = ly - cursor->cursor->y;
402 400
403 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 401 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
404 transaction_commit_dirty();
405} 402}
406 403
407void dispatch_cursor_button(struct sway_cursor *cursor, 404void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -431,7 +428,6 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
431 cursor_handle_activity_from_device(cursor, event->device); 428 cursor_handle_activity_from_device(cursor, event->device);
432 dispatch_cursor_button(cursor, event->device, 429 dispatch_cursor_button(cursor, event->device,
433 event->time_msec, event->button, event->state); 430 event->time_msec, event->button, event->state);
434 transaction_commit_dirty();
435} 431}
436 432
437void dispatch_cursor_axis(struct sway_cursor *cursor, 433void dispatch_cursor_axis(struct sway_cursor *cursor,
@@ -444,7 +440,6 @@ static void handle_pointer_axis(struct wl_listener *listener, void *data) {
444 struct wlr_event_pointer_axis *event = data; 440 struct wlr_event_pointer_axis *event = data;
445 cursor_handle_activity_from_device(cursor, event->device); 441 cursor_handle_activity_from_device(cursor, event->device);
446 dispatch_cursor_axis(cursor, event); 442 dispatch_cursor_axis(cursor, event);
447 transaction_commit_dirty();
448} 443}
449 444
450static void handle_pointer_frame(struct wl_listener *listener, void *data) { 445static void handle_pointer_frame(struct wl_listener *listener, void *data) {
@@ -495,7 +490,6 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
495 dispatch_cursor_button(cursor, event->device, event->time_msec, 490 dispatch_cursor_button(cursor, event->device, event->time_msec,
496 BTN_LEFT, WLR_BUTTON_PRESSED); 491 BTN_LEFT, WLR_BUTTON_PRESSED);
497 wlr_seat_pointer_notify_frame(wlr_seat); 492 wlr_seat_pointer_notify_frame(wlr_seat);
498 transaction_commit_dirty();
499 } 493 }
500} 494}
501 495
@@ -512,7 +506,6 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
512 dispatch_cursor_button(cursor, event->device, event->time_msec, 506 dispatch_cursor_button(cursor, event->device, event->time_msec,
513 BTN_LEFT, WLR_BUTTON_RELEASED); 507 BTN_LEFT, WLR_BUTTON_RELEASED);
514 wlr_seat_pointer_notify_frame(wlr_seat); 508 wlr_seat_pointer_notify_frame(wlr_seat);
515 transaction_commit_dirty();
516 } 509 }
517 } else { 510 } else {
518 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 511 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id);
@@ -553,7 +546,6 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
553 dx = lx - cursor->cursor->x; 546 dx = lx - cursor->cursor->x;
554 dy = ly - cursor->cursor->y; 547 dy = ly - cursor->cursor->y;
555 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 548 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
556 transaction_commit_dirty();
557 } 549 }
558 } else if (surface) { 550 } else if (surface) {
559 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 551 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec,
@@ -639,8 +631,6 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
639 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 631 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); 632 pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy);
641 } 633 }
642
643 transaction_commit_dirty();
644} 634}
645 635
646static void handle_tool_axis(struct wl_listener *listener, void *data) { 636static void handle_tool_axis(struct wl_listener *listener, void *data) {
@@ -720,7 +710,6 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
720 dispatch_cursor_button(cursor, event->device, event->time_msec, 710 dispatch_cursor_button(cursor, event->device, event->time_msec,
721 BTN_LEFT, WLR_BUTTON_RELEASED); 711 BTN_LEFT, WLR_BUTTON_RELEASED);
722 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 712 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)) { 713 } 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 714 // 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 715 // tablet v2, we should notify that surface if it gets released over a
@@ -733,7 +722,6 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
733 dispatch_cursor_button(cursor, event->device, event->time_msec, 722 dispatch_cursor_button(cursor, event->device, event->time_msec,
734 BTN_LEFT, WLR_BUTTON_PRESSED); 723 BTN_LEFT, WLR_BUTTON_PRESSED);
735 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 724 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
736 transaction_commit_dirty();
737 } 725 }
738 } else { 726 } else {
739 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); 727 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state);
@@ -820,7 +808,6 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
820 break; 808 break;
821 } 809 }
822 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 810 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
823 transaction_commit_dirty();
824 return; 811 return;
825 } 812 }
826 813
@@ -837,8 +824,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
837 824
838 struct sway_container *con = view->container; 825 struct sway_container *con = view->container;
839 826
840 double sx = cursor->cursor->x - con->content_x + view->geometry.x; 827 double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x;
841 double sy = cursor->cursor->y - con->content_y + view->geometry.y; 828 double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y;
842 829
843 if (!pixman_region32_contains_point(region, 830 if (!pixman_region32_contains_point(region,
844 floor(sx), floor(sy), NULL)) { 831 floor(sx), floor(sy), NULL)) {
@@ -849,8 +836,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
849 double sy = (boxes[0].y1 + boxes[0].y2) / 2.; 836 double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
850 837
851 wlr_cursor_warp_closest(cursor->cursor, NULL, 838 wlr_cursor_warp_closest(cursor->cursor, NULL,
852 sx + con->content_x - view->geometry.x, 839 sx + con->pending.content_x - view->geometry.x,
853 sy + con->content_y - view->geometry.y); 840 sy + con->pending.content_y - view->geometry.y);
854 841
855 cursor_rebase(cursor); 842 cursor_rebase(cursor);
856 } 843 }
@@ -1170,8 +1157,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
1170 return; 1157 return;
1171 } 1158 }
1172 1159
1173 double x = container->x + container->width / 2.0; 1160 double x = container->pending.x + container->pending.width / 2.0;
1174 double y = container->y + container->height / 2.0; 1161 double y = container->pending.y + container->pending.height / 2.0;
1175 1162
1176 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1163 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1177 cursor_unhide(cursor); 1164 cursor_unhide(cursor);
@@ -1284,8 +1271,8 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1284 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1271 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1285 struct sway_container *con = view->container; 1272 struct sway_container *con = view->container;
1286 1273
1287 double lx = sx + con->content_x - view->geometry.x; 1274 double lx = sx + con->pending.content_x - view->geometry.x;
1288 double ly = sy + con->content_y - view->geometry.y; 1275 double ly = sy + con->pending.content_y - view->geometry.y;
1289 1276
1290 wlr_cursor_warp(cursor->cursor, NULL, lx, ly); 1277 wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
1291 1278
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..f9eb8c8a 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,7 @@ 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();
370 } 375 }
371 seat_pointer_notify_button(seat, time_msec, button, state); 376 seat_pointer_notify_button(seat, time_msec, button, state);
372 return; 377 return;
@@ -381,7 +386,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
381 struct sway_container *cont_to_focus = cont; 386 struct sway_container *cont_to_focus = cont;
382 enum sway_container_layout layout = container_parent_layout(cont); 387 enum sway_container_layout layout = container_parent_layout(cont);
383 if (layout == L_TABBED || layout == L_STACKED) { 388 if (layout == L_TABBED || layout == L_STACKED) {
384 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); 389 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node);
385 } 390 }
386 391
387 seat_set_focus_container(seat, cont_to_focus); 392 seat_set_focus_container(seat, cont_to_focus);
@@ -397,9 +402,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
397 BTN_LEFT : BTN_RIGHT; 402 BTN_LEFT : BTN_RIGHT;
398 if (button == btn_resize) { 403 if (button == btn_resize) {
399 edge = 0; 404 edge = 0;
400 edge |= cursor->cursor->x > cont->x + cont->width / 2 ? 405 edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ?
401 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 406 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
402 edge |= cursor->cursor->y > cont->y + cont->height / 2 ? 407 edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ?
403 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 408 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
404 409
405 const char *image = NULL; 410 const char *image = NULL;
@@ -446,9 +451,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
446 if (mod_pressed && button == btn_resize) { 451 if (mod_pressed && button == btn_resize) {
447 struct sway_container *floater = container_toplevel_ancestor(cont); 452 struct sway_container *floater = container_toplevel_ancestor(cont);
448 edge = 0; 453 edge = 0;
449 edge |= cursor->cursor->x > floater->x + floater->width / 2 ? 454 edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ?
450 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 455 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
451 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 456 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
452 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 457 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
453 seatop_begin_resize_floating(seat, floater, edge); 458 seatop_begin_resize_floating(seat, floater, edge);
454 return; 459 return;
@@ -458,7 +463,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
458 // Handle moving a tiling container 463 // Handle moving a tiling container
459 if (config->tiling_drag && (mod_pressed || on_titlebar) && 464 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
460 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 465 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
461 cont && cont->fullscreen_mode == FULLSCREEN_NONE) { 466 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
462 struct sway_container *focus = seat_get_focused_container(seat); 467 struct sway_container *focus = seat_get_focused_container(seat);
463 bool focused = focus == cont || container_has_ancestor(focus, cont); 468 bool focused = focus == cont || container_has_ancestor(focus, cont);
464 if (on_titlebar && !focused) { 469 if (on_titlebar && !focused) {
@@ -487,6 +492,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
487 if (cont && state == WLR_BUTTON_PRESSED) { 492 if (cont && state == WLR_BUTTON_PRESSED) {
488 node = seat_get_focus_inactive(seat, &cont->node); 493 node = seat_get_focus_inactive(seat, &cont->node);
489 seat_set_focus(seat, node); 494 seat_set_focus(seat, node);
495 transaction_commit_dirty();
490 seat_pointer_notify_button(seat, time_msec, button, state); 496 seat_pointer_notify_button(seat, time_msec, button, state);
491 return; 497 return;
492 } 498 }
@@ -501,6 +507,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
501 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 507 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
502 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 508 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
503 seat_set_focus_surface(seat, xsurface->surface, false); 509 seat_set_focus_surface(seat, xsurface->surface, false);
510 transaction_commit_dirty();
504 seat_pointer_notify_button(seat, time_msec, button, state); 511 seat_pointer_notify_button(seat, time_msec, button, state);
505 return; 512 return;
506 } 513 }
@@ -530,6 +537,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
530 if (focus && hovered_output != node_get_output(focus)) { 537 if (focus && hovered_output != node_get_output(focus)) {
531 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 538 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
532 seat_set_focus(seat, &ws->node); 539 seat_set_focus(seat, &ws->node);
540 transaction_commit_dirty();
533 } 541 }
534 return; 542 return;
535 } 543 }
@@ -541,6 +549,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
541 struct sway_output *hovered_output = node_get_output(hovered_node); 549 struct sway_output *hovered_output = node_get_output(hovered_node);
542 if (hovered_output != focused_output) { 550 if (hovered_output != focused_output) {
543 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); 551 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node));
552 transaction_commit_dirty();
544 } 553 }
545 return; 554 return;
546 } 555 }
@@ -556,6 +565,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
556 if (hovered_node != e->previous_node || 565 if (hovered_node != e->previous_node ||
557 config->focus_follows_mouse == FOLLOWS_ALWAYS) { 566 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
558 seat_set_focus(seat, hovered_node); 567 seat_set_focus(seat, hovered_node);
568 transaction_commit_dirty();
559 } 569 }
560 } 570 }
561} 571}
@@ -664,7 +674,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
664 bool on_border = edge != WLR_EDGE_NONE; 674 bool on_border = edge != WLR_EDGE_NONE;
665 bool on_titlebar = cont && !on_border && !surface; 675 bool on_titlebar = cont && !on_border && !surface;
666 bool on_titlebar_border = cont && on_border && 676 bool on_titlebar_border = cont && on_border &&
667 cursor->cursor->y < cont->content_y; 677 cursor->cursor->y < cont->pending.content_y;
668 bool on_contents = cont && !on_border && surface; 678 bool on_contents = cont && !on_border && surface;
669 bool on_workspace = node && node->type == N_WORKSPACE; 679 bool on_workspace = node && node->type == N_WORKSPACE;
670 float scroll_factor = 680 float scroll_factor =
@@ -714,6 +724,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
714 // Use the focused child of the tabbed/stacked container, not the 724 // Use the focused child of the tabbed/stacked container, not the
715 // container the user scrolled on. 725 // container the user scrolled on.
716 seat_set_focus(seat, new_focus); 726 seat_set_focus(seat, new_focus);
727 transaction_commit_dirty();
717 handled = true; 728 handled = true;
718 } 729 }
719 } 730 }
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index 17f619e3..844cf5ab 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -5,6 +5,7 @@
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 {
@@ -107,4 +108,5 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
107 seat->seatop_data = e; 108 seat->seatop_data = e;
108 109
109 container_raise_floating(con); 110 container_raise_floating(con);
111 transaction_commit_dirty();
110} 112}
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..6c438424 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -1,3 +1,4 @@
1#include <float.h>
1#include <json.h> 2#include <json.h>
2#include <libevdev/libevdev.h> 3#include <libevdev/libevdev.h>
3#include <stdio.h> 4#include <stdio.h>
@@ -23,6 +24,20 @@
23static const int i3_output_id = INT32_MAX; 24static const int i3_output_id = INT32_MAX;
24static const int i3_scratch_id = INT32_MAX - 1; 25static const int i3_scratch_id = INT32_MAX - 1;
25 26
27static const char *ipc_json_node_type_description(enum sway_node_type node_type) {
28 switch (node_type) {
29 case N_ROOT:
30 return "root";
31 case N_OUTPUT:
32 return "output";
33 case N_WORKSPACE:
34 return "workspace";
35 case N_CONTAINER:
36 return "con";
37 }
38 return "none";
39}
40
26static const char *ipc_json_layout_description(enum sway_container_layout l) { 41static const char *ipc_json_layout_description(enum sway_container_layout l) {
27 switch (l) { 42 switch (l) {
28 case L_VERT: 43 case L_VERT:
@@ -189,16 +204,22 @@ static json_object *ipc_json_create_empty_rect(void) {
189 return ipc_json_create_rect(&empty); 204 return ipc_json_create_rect(&empty);
190} 205}
191 206
192static json_object *ipc_json_create_node(int id, char *name, 207static json_object *ipc_json_create_node(int id, const char* type, char *name,
193 bool focused, json_object *focus, struct wlr_box *box) { 208 bool focused, json_object *focus, struct wlr_box *box) {
194 json_object *object = json_object_new_object(); 209 json_object *object = json_object_new_object();
195 210
196 json_object_object_add(object, "id", json_object_new_int(id)); 211 json_object_object_add(object, "id", json_object_new_int(id));
197 json_object_object_add(object, "name", 212 json_object_object_add(object, "type", json_object_new_string(type));
198 name ? json_object_new_string(name) : NULL); 213 json_object_object_add(object, "orientation",
199 json_object_object_add(object, "rect", ipc_json_create_rect(box)); 214 json_object_new_string(
215 ipc_json_orientation_description(L_HORIZ)));
216 json_object_object_add(object, "percent", NULL);
217 json_object_object_add(object, "urgent", json_object_new_boolean(false));
218 json_object_object_add(object, "marks", json_object_new_array());
200 json_object_object_add(object, "focused", json_object_new_boolean(focused)); 219 json_object_object_add(object, "focused", json_object_new_boolean(focused));
201 json_object_object_add(object, "focus", focus); 220 json_object_object_add(object, "layout",
221 json_object_new_string(
222 ipc_json_layout_description(L_HORIZ)));
202 223
203 // set default values to be compatible with i3 224 // set default values to be compatible with i3
204 json_object_object_add(object, "border", 225 json_object_object_add(object, "border",
@@ -206,35 +227,25 @@ static json_object *ipc_json_create_node(int id, char *name,
206 ipc_json_border_description(B_NONE))); 227 ipc_json_border_description(B_NONE)));
207 json_object_object_add(object, "current_border_width", 228 json_object_object_add(object, "current_border_width",
208 json_object_new_int(0)); 229 json_object_new_int(0));
209 json_object_object_add(object, "layout", 230 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()); 231 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
232 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
218 json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); 233 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
234 json_object_object_add(object, "name",
235 name ? json_object_new_string(name) : NULL);
219 json_object_object_add(object, "window", NULL); 236 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()); 237 json_object_object_add(object, "nodes", json_object_new_array());
224 json_object_object_add(object, "floating_nodes", json_object_new_array()); 238 json_object_object_add(object, "floating_nodes", json_object_new_array());
239 json_object_object_add(object, "focus", focus);
240 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
225 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 241 json_object_object_add(object, "sticky", json_object_new_boolean(false));
226 242
227 return object; 243 return object;
228} 244}
229 245
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, 246static void ipc_json_describe_output(struct sway_output *output,
235 json_object *object) { 247 json_object *object) {
236 struct wlr_output *wlr_output = output->wlr_output; 248 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)); 249 json_object_object_add(object, "active", json_object_new_boolean(true));
239 json_object_object_add(object, "dpms", 250 json_object_object_add(object, "dpms",
240 json_object_new_boolean(wlr_output->enabled)); 251 json_object_new_boolean(wlr_output->enabled));
@@ -369,11 +380,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
369 json_object_new_int(container->node.id)); 380 json_object_new_int(container->node.id));
370 } 381 }
371 382
372 json_object *workspace = ipc_json_create_node(i3_scratch_id, 383 json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace",
373 "__i3_scratch", false, workspace_focus, &box); 384 "__i3_scratch", false, workspace_focus, &box);
374 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); 385 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 386
378 // List all hidden scratchpad containers as floating nodes 387 // List all hidden scratchpad containers as floating nodes
379 json_object *floating_array = json_object_new_array(); 388 json_object *floating_array = json_object_new_array();
@@ -390,10 +399,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
390 json_object *output_focus = json_object_new_array(); 399 json_object *output_focus = json_object_new_array();
391 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); 400 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
392 401
393 json_object *output = ipc_json_create_node(i3_output_id, 402 json_object *output = ipc_json_create_node(i3_output_id, "output",
394 "__i3", false, output_focus, &box); 403 "__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", 404 json_object_object_add(output, "layout",
398 json_object_new_string("output")); 405 json_object_new_string("output"));
399 406
@@ -423,7 +430,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
423 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); 430 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1));
424 json_object_object_add(object, "output", workspace->output ? 431 json_object_object_add(object, "output", workspace->output ?
425 json_object_new_string(workspace->output->wlr_output->name) : NULL); 432 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", 433 json_object_object_add(object, "urgent",
428 json_object_new_boolean(workspace->urgent)); 434 json_object_new_boolean(workspace->urgent));
429 json_object_object_add(object, "representation", workspace->representation ? 435 json_object_object_add(object, "representation", workspace->representation ?
@@ -451,27 +457,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; 457 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED;
452 if (((!tab_or_stack || container_is_floating(c)) && 458 if (((!tab_or_stack || container_is_floating(c)) &&
453 c->current.border != B_NORMAL) || 459 c->current.border != B_NORMAL) ||
454 c->fullscreen_mode != FULLSCREEN_NONE || 460 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
455 c->workspace == NULL) { 461 c->pending.workspace == NULL) {
456 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; 462 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0;
457 return; 463 return;
458 } 464 }
459 465
460 if (c->parent) { 466 if (c->pending.parent) {
461 deco_rect->x = c->x - c->parent->x; 467 deco_rect->x = c->pending.x - c->pending.parent->pending.x;
462 deco_rect->y = c->y - c->parent->y; 468 deco_rect->y = c->pending.y - c->pending.parent->pending.y;
463 } else { 469 } else {
464 deco_rect->x = c->x - c->workspace->x; 470 deco_rect->x = c->pending.x - c->pending.workspace->x;
465 deco_rect->y = c->y - c->workspace->y; 471 deco_rect->y = c->pending.y - c->pending.workspace->y;
466 } 472 }
467 deco_rect->width = c->width; 473 deco_rect->width = c->pending.width;
468 deco_rect->height = container_titlebar_height(); 474 deco_rect->height = container_titlebar_height();
469 475
470 if (!container_is_floating(c)) { 476 if (!container_is_floating(c)) {
471 if (parent_layout == L_TABBED) { 477 if (parent_layout == L_TABBED) {
472 deco_rect->width = c->parent 478 deco_rect->width = c->pending.parent
473 ? c->parent->width / c->parent->children->length 479 ? c->pending.parent->pending.width / c->pending.parent->pending.children->length
474 : c->workspace->width / c->workspace->tiling->length; 480 : c->pending.workspace->width / c->pending.workspace->tiling->length;
475 deco_rect->x += deco_rect->width * container_sibling_index(c); 481 deco_rect->x += deco_rect->width * container_sibling_index(c);
476 } else if (parent_layout == L_STACKED) { 482 } else if (parent_layout == L_STACKED) {
477 if (!c->view) { 483 if (!c->view) {
@@ -494,10 +500,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)); 500 json_object_object_add(object, "visible", json_object_new_boolean(visible));
495 501
496 struct wlr_box window_box = { 502 struct wlr_box window_box = {
497 c->content_x - c->x, 503 c->pending.content_x - c->pending.x,
498 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 504 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0,
499 c->content_width, 505 c->pending.content_width,
500 c->content_height 506 c->pending.content_height
501 }; 507 };
502 508
503 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); 509 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box));
@@ -583,16 +589,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) { 589static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
584 json_object_object_add(object, "name", 590 json_object_object_add(object, "name",
585 c->title ? json_object_new_string(c->title) : NULL); 591 c->title ? json_object_new_string(c->title) : NULL);
586 json_object_object_add(object, "type", 592 if (container_is_floating(c)) {
587 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 593 json_object_object_add(object, "type",
594 json_object_new_string("floating_con"));
595 }
588 596
589 json_object_object_add(object, "layout", 597 json_object_object_add(object, "layout",
590 json_object_new_string( 598 json_object_new_string(
591 ipc_json_layout_description(c->layout))); 599 ipc_json_layout_description(c->pending.layout)));
592 600
593 json_object_object_add(object, "orientation", 601 json_object_object_add(object, "orientation",
594 json_object_new_string( 602 json_object_new_string(
595 ipc_json_orientation_description(c->layout))); 603 ipc_json_orientation_description(c->pending.layout)));
596 604
597 bool urgent = c->view ? 605 bool urgent = c->view ?
598 view_is_urgent(c->view) : container_has_urgent_child(c); 606 view_is_urgent(c->view) : container_has_urgent_child(c);
@@ -600,7 +608,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)); 608 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
601 609
602 json_object_object_add(object, "fullscreen_mode", 610 json_object_object_add(object, "fullscreen_mode",
603 json_object_new_int(c->fullscreen_mode)); 611 json_object_new_int(c->pending.fullscreen_mode));
604 612
605 struct sway_node *parent = node_get_parent(&c->node); 613 struct sway_node *parent = node_get_parent(&c->node);
606 struct wlr_box parent_box = {0, 0, 0, 0}; 614 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -610,8 +618,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
610 } 618 }
611 619
612 if (parent_box.width != 0 && parent_box.height != 0) { 620 if (parent_box.width != 0 && parent_box.height != 0) {
613 double percent = ((double)c->width / parent_box.width) 621 double percent = ((double)c->pending.width / parent_box.width)
614 * ((double)c->height / parent_box.height); 622 * ((double)c->pending.height / parent_box.height);
615 json_object_object_add(object, "percent", json_object_new_double(percent)); 623 json_object_object_add(object, "percent", json_object_new_double(percent));
616 } 624 }
617 625
@@ -692,12 +700,11 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
692 }; 700 };
693 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 701 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
694 702
695 json_object *object = ipc_json_create_node( 703 json_object *object = ipc_json_create_node((int)node->id,
696 (int)node->id, name, focused, focus, &box); 704 ipc_json_node_type_description(node->type), name, focused, focus, &box);
697 705
698 switch (node->type) { 706 switch (node->type) {
699 case N_ROOT: 707 case N_ROOT:
700 ipc_json_describe_root(root, object);
701 break; 708 break;
702 case N_OUTPUT: 709 case N_OUTPUT:
703 ipc_json_describe_output(node->sway_output, object); 710 ipc_json_describe_output(node->sway_output, object);
@@ -743,10 +750,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
743 } 750 }
744 break; 751 break;
745 case N_CONTAINER: 752 case N_CONTAINER:
746 if (node->sway_container->children) { 753 if (node->sway_container->pending.children) {
747 for (i = 0; i < node->sway_container->children->length; ++i) { 754 for (i = 0; i < node->sway_container->pending.children->length; ++i) {
748 struct sway_container *child = 755 struct sway_container *child =
749 node->sway_container->children->items[i]; 756 node->sway_container->pending.children->items[i];
750 json_object_array_add(children, 757 json_object_array_add(children,
751 ipc_json_describe_node_recursive(&child->node)); 758 ipc_json_describe_node_recursive(&child->node));
752 } 759 }
@@ -996,6 +1003,17 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
996 } 1003 }
997 } 1004 }
998 1005
1006 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1007 struct input_config *ic = input_device_get_config(device);
1008 float scroll_factor = 1.0f;
1009 if (ic != NULL && !isnan(ic->scroll_factor) &&
1010 ic->scroll_factor != FLT_MIN) {
1011 scroll_factor = ic->scroll_factor;
1012 }
1013 json_object_object_add(object, "scroll_factor",
1014 json_object_new_double(scroll_factor));
1015 }
1016
999 if (wlr_input_device_is_libinput(device->wlr_device)) { 1017 if (wlr_input_device_is_libinput(device->wlr_device)) {
1000 struct libinput_device *libinput_dev; 1018 struct libinput_device *libinput_dev;
1001 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1019 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
@@ -1109,7 +1127,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
1109 json_object_object_add(json, "verbose", 1127 json_object_object_add(json, "verbose",
1110 json_object_new_boolean(bar->verbose)); 1128 json_object_new_boolean(bar->verbose));
1111 json_object_object_add(json, "pango_markup", 1129 json_object_object_add(json, "pango_markup",
1112 json_object_new_boolean(bar->pango_markup)); 1130 json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT
1131 ? config->pango_markup
1132 : bar->pango_markup));
1113 1133
1114 json_object *colors = json_object_new_object(); 1134 json_object *colors = json_object_new_object();
1115 json_object_object_add(colors, "background", 1135 json_object_object_add(colors, "background",
diff --git a/sway/main.c b/sway/main.c
index 0c219fb3..0611e80b 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) {
@@ -245,7 +209,7 @@ static void handle_wlr_log(enum wlr_log_importance importance,
245int main(int argc, char **argv) { 209int main(int argc, char **argv) {
246 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 210 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0;
247 211
248 static struct option long_options[] = { 212 static const struct option long_options[] = {
249 {"help", no_argument, NULL, 'h'}, 213 {"help", no_argument, NULL, 'h'},
250 {"config", required_argument, NULL, 'c'}, 214 {"config", required_argument, NULL, 'c'},
251 {"validate", no_argument, NULL, 'C'}, 215 {"validate", no_argument, NULL, 'C'},
@@ -344,11 +308,11 @@ int main(int argc, char **argv) {
344 } 308 }
345 309
346 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); 310 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION);
311 sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR);
347 log_kernel(); 312 log_kernel();
348 log_distro(); 313 log_distro();
349 log_env(); 314 log_env();
350 detect_proprietary(allow_unsupported_gpu); 315 detect_proprietary(allow_unsupported_gpu);
351 detect_raspi();
352 316
353 if (optind < argc) { // Behave as IPC client 317 if (optind < argc) { // Behave as IPC client
354 if (optind != 1) { 318 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..2e5ab104 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -9,6 +9,7 @@
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>
@@ -24,7 +25,11 @@
24#include <wlr/types/wlr_tablet_v2.h> 25#include <wlr/types/wlr_tablet_v2.h>
25#include <wlr/types/wlr_viewporter.h> 26#include <wlr/types/wlr_viewporter.h>
26#include <wlr/types/wlr_xcursor_manager.h> 27#include <wlr/types/wlr_xcursor_manager.h>
28#include <wlr/types/wlr_xdg_activation_v1.h>
27#include <wlr/types/wlr_xdg_decoration_v1.h> 29#include <wlr/types/wlr_xdg_decoration_v1.h>
30#include <wlr/types/wlr_xdg_foreign_registry.h>
31#include <wlr/types/wlr_xdg_foreign_v1.h>
32#include <wlr/types/wlr_xdg_foreign_v2.h>
28#include <wlr/types/wlr_xdg_output_v1.h> 33#include <wlr/types/wlr_xdg_output_v1.h>
29#include "config.h" 34#include "config.h"
30#include "list.h" 35#include "list.h"
@@ -150,6 +155,17 @@ bool server_init(struct sway_server *server) {
150 wlr_primary_selection_v1_device_manager_create(server->wl_display); 155 wlr_primary_selection_v1_device_manager_create(server->wl_display);
151 wlr_viewporter_create(server->wl_display); 156 wlr_viewporter_create(server->wl_display);
152 157
158 struct wlr_xdg_foreign_registry *foreign_registry =
159 wlr_xdg_foreign_registry_create(server->wl_display);
160 wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);
161 wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry);
162
163 server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display);
164 server->xdg_activation_v1_request_activate.notify =
165 xdg_activation_v1_handle_request_activate;
166 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
167 &server->xdg_activation_v1_request_activate);
168
153 // Avoid using "wayland-0" as display socket 169 // Avoid using "wayland-0" as display socket
154 char name_candidate[16]; 170 char name_candidate[16];
155 for (int i = 1; i <= 32; ++i) { 171 for (int i = 1; i <= 32; ++i) {
@@ -186,7 +202,6 @@ bool server_init(struct sway_server *server) {
186 } 202 }
187 203
188 server->dirty_nodes = create_list(); 204 server->dirty_nodes = create_list();
189 server->transactions = create_list();
190 205
191 server->input = input_manager_create(server); 206 server->input = input_manager_create(server);
192 input_manager_get_default_seat(); // create seat0 207 input_manager_get_default_seat(); // create seat0
@@ -202,7 +217,6 @@ void server_fini(struct sway_server *server) {
202 wl_display_destroy_clients(server->wl_display); 217 wl_display_destroy_clients(server->wl_display);
203 wl_display_destroy(server->wl_display); 218 wl_display_destroy(server->wl_display);
204 list_free(server->dirty_nodes); 219 list_free(server->dirty_nodes);
205 list_free(server->transactions);
206} 220}
207 221
208bool server_start(struct sway_server *server) { 222bool server_start(struct sway_server *server) {
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..373e9dce 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -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..2828c841 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -112,7 +112,7 @@ must be separated by one space. For example:
112*output* <name> toggle 112*output* <name> toggle
113 Toggle the specified output. 113 Toggle the specified output.
114 114
115*output* <name> dpms on|off 115*output* <name> dpms on|off|toggle
116 Enables or disables the specified output via DPMS. To turn an output off 116 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. 117 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
118 118
@@ -142,7 +142,7 @@ must be separated by one space. For example:
142 Enables or disables adaptive synchronization (often referred to as Variable 142 Enables or disables adaptive synchronization (often referred to as Variable
143 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). 143 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync).
144 144
145 Adaptive sync allows clients to submit frames a little to late without 145 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 146 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 147 adaptive sync can improve latency, but can cause flickering on some
148 hardware. 148 hardware.
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..b928d069 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"
@@ -30,12 +31,12 @@ struct sway_container *container_create(struct sway_view *view) {
30 return NULL; 31 return NULL;
31 } 32 }
32 node_init(&c->node, N_CONTAINER, c); 33 node_init(&c->node, N_CONTAINER, c);
33 c->layout = L_NONE; 34 c->pending.layout = L_NONE;
34 c->view = view; 35 c->view = view;
35 c->alpha = 1.0f; 36 c->alpha = 1.0f;
36 37
37 if (!view) { 38 if (!view) {
38 c->children = create_list(); 39 c->pending.children = create_list();
39 c->current.children = create_list(); 40 c->current.children = create_list();
40 } 41 }
41 c->marks = create_list(); 42 c->marks = create_list();
@@ -62,7 +63,7 @@ void container_destroy(struct sway_container *con) {
62 wlr_texture_destroy(con->title_focused_inactive); 63 wlr_texture_destroy(con->title_focused_inactive);
63 wlr_texture_destroy(con->title_unfocused); 64 wlr_texture_destroy(con->title_unfocused);
64 wlr_texture_destroy(con->title_urgent); 65 wlr_texture_destroy(con->title_urgent);
65 list_free(con->children); 66 list_free(con->pending.children);
66 list_free(con->current.children); 67 list_free(con->current.children);
67 list_free(con->outputs); 68 list_free(con->outputs);
68 69
@@ -90,10 +91,10 @@ void container_begin_destroy(struct sway_container *con) {
90 } 91 }
91 // The workspace must have the fullscreen pointer cleared so that the 92 // The workspace must have the fullscreen pointer cleared so that the
92 // seat code can find an appropriate new focus. 93 // seat code can find an appropriate new focus.
93 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { 94 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) {
94 con->workspace->fullscreen = NULL; 95 con->pending.workspace->fullscreen = NULL;
95 } 96 }
96 if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { 97 if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
97 container_fullscreen_disable(con); 98 container_fullscreen_disable(con);
98 } 99 }
99 100
@@ -108,11 +109,11 @@ void container_begin_destroy(struct sway_container *con) {
108 root_scratchpad_remove_container(con); 109 root_scratchpad_remove_container(con);
109 } 110 }
110 111
111 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 112 if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
112 container_fullscreen_disable(con); 113 container_fullscreen_disable(con);
113 } 114 }
114 115
115 if (con->parent || con->workspace) { 116 if (con->pending.parent || con->pending.workspace) {
116 container_detach(con); 117 container_detach(con);
117 } 118 }
118} 119}
@@ -121,12 +122,12 @@ void container_reap_empty(struct sway_container *con) {
121 if (con->view) { 122 if (con->view) {
122 return; 123 return;
123 } 124 }
124 struct sway_workspace *ws = con->workspace; 125 struct sway_workspace *ws = con->pending.workspace;
125 while (con) { 126 while (con) {
126 if (con->children->length) { 127 if (con->pending.children->length) {
127 return; 128 return;
128 } 129 }
129 struct sway_container *parent = con->parent; 130 struct sway_container *parent = con->pending.parent;
130 container_begin_destroy(con); 131 container_begin_destroy(con);
131 con = parent; 132 con = parent;
132 } 133 }
@@ -139,9 +140,9 @@ struct sway_container *container_flatten(struct sway_container *container) {
139 if (container->view) { 140 if (container->view) {
140 return NULL; 141 return NULL;
141 } 142 }
142 while (container && container->children->length == 1) { 143 while (container && container->pending.children->length == 1) {
143 struct sway_container *child = container->children->items[0]; 144 struct sway_container *child = container->pending.children->items[0];
144 struct sway_container *parent = container->parent; 145 struct sway_container *parent = container->pending.parent;
145 container_replace(container, child); 146 container_replace(container, child);
146 container_begin_destroy(container); 147 container_begin_destroy(container);
147 container = parent; 148 container = parent;
@@ -151,11 +152,11 @@ struct sway_container *container_flatten(struct sway_container *container) {
151 152
152struct sway_container *container_find_child(struct sway_container *container, 153struct sway_container *container_find_child(struct sway_container *container,
153 bool (*test)(struct sway_container *con, void *data), void *data) { 154 bool (*test)(struct sway_container *con, void *data), void *data) {
154 if (!container->children) { 155 if (!container->pending.children) {
155 return NULL; 156 return NULL;
156 } 157 }
157 for (int i = 0; i < container->children->length; ++i) { 158 for (int i = 0; i < container->pending.children->length; ++i) {
158 struct sway_container *child = container->children->items[i]; 159 struct sway_container *child = container->pending.children->items[i];
159 if (test(child, data)) { 160 if (test(child, data)) {
160 return child; 161 return child;
161 } 162 }
@@ -310,7 +311,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
310 return NULL; 311 return NULL;
311} 312}
312 313
313struct sway_container *view_container_at(struct sway_node *parent, 314static struct sway_container *view_container_content_at(struct sway_node *parent,
314 double lx, double ly, 315 double lx, double ly,
315 struct wlr_surface **surface, double *sx, double *sy) { 316 struct wlr_surface **surface, double *sx, double *sy) {
316 if (!sway_assert(node_is_view(parent), "Expected a view")) { 317 if (!sway_assert(node_is_view(parent), "Expected a view")) {
@@ -319,10 +320,33 @@ struct sway_container *view_container_at(struct sway_node *parent,
319 320
320 struct sway_container *container = parent->sway_container; 321 struct sway_container *container = parent->sway_container;
321 struct wlr_box box = { 322 struct wlr_box box = {
322 .x = container->x, 323 .x = container->pending.content_x,
323 .y = container->y, 324 .y = container->pending.content_y,
324 .width = container->width, 325 .width = container->pending.content_width,
325 .height = container->height, 326 .height = container->pending.content_height,
327 };
328
329 if (wlr_box_contains_point(&box, lx, ly)) {
330 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
331 return container;
332 }
333
334 return NULL;
335}
336
337static struct sway_container *view_container_at(struct sway_node *parent,
338 double lx, double ly,
339 struct wlr_surface **surface, double *sx, double *sy) {
340 if (!sway_assert(node_is_view(parent), "Expected a view")) {
341 return NULL;
342 }
343
344 struct sway_container *container = parent->sway_container;
345 struct wlr_box box = {
346 .x = container->pending.x,
347 .y = container->pending.y,
348 .width = container->pending.width,
349 .height = container->pending.height,
326 }; 350 };
327 351
328 if (wlr_box_contains_point(&box, lx, ly)) { 352 if (wlr_box_contains_point(&box, lx, ly)) {
@@ -394,7 +418,7 @@ struct sway_container *container_at(struct sway_workspace *workspace,
394 } 418 }
395 // Tiling (focused) 419 // Tiling (focused)
396 if (focus && focus->view && !is_floating) { 420 if (focus && focus->view && !is_floating) {
397 if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { 421 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
398 return c; 422 return c;
399 } 423 }
400 } 424 }
@@ -408,19 +432,41 @@ struct sway_container *container_at(struct sway_workspace *workspace,
408void container_for_each_child(struct sway_container *container, 432void container_for_each_child(struct sway_container *container,
409 void (*f)(struct sway_container *container, void *data), 433 void (*f)(struct sway_container *container, void *data),
410 void *data) { 434 void *data) {
411 if (container->children) { 435 if (container->pending.children) {
412 for (int i = 0; i < container->children->length; ++i) { 436 for (int i = 0; i < container->pending.children->length; ++i) {
413 struct sway_container *child = container->children->items[i]; 437 struct sway_container *child = container->pending.children->items[i];
414 f(child, data); 438 f(child, data);
415 container_for_each_child(child, f, data); 439 container_for_each_child(child, f, data);
416 } 440 }
417 } 441 }
418} 442}
419 443
444struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container)
445{
446 struct sway_workspace *workspace = container->pending.workspace;
447
448 if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) {
449 if (container_is_transient_for(container, workspace->fullscreen)) {
450 return NULL;
451 }
452 return workspace->fullscreen;
453 }
454
455 struct sway_container *fullscreen_global = root->fullscreen_global;
456 if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) {
457 if (container_is_transient_for(container, fullscreen_global)) {
458 return NULL;
459 }
460 return fullscreen_global;
461 }
462
463 return NULL;
464}
465
420bool container_has_ancestor(struct sway_container *descendant, 466bool container_has_ancestor(struct sway_container *descendant,
421 struct sway_container *ancestor) { 467 struct sway_container *ancestor) {
422 while (descendant) { 468 while (descendant) {
423 descendant = descendant->parent; 469 descendant = descendant->pending.parent;
424 if (descendant == ancestor) { 470 if (descendant == ancestor) {
425 return true; 471 return true;
426 } 472 }
@@ -485,6 +531,10 @@ static void update_title_texture(struct sway_container *con,
485 cairo_surface_destroy(dummy_surface); 531 cairo_surface_destroy(dummy_surface);
486 cairo_destroy(c); 532 cairo_destroy(c);
487 533
534 if (width == 0 || height == 0) {
535 return;
536 }
537
488 cairo_surface_t *surface = cairo_image_surface_create( 538 cairo_surface_t *surface = cairo_image_surface_create(
489 CAIRO_FORMAT_ARGB32, width, height); 539 CAIRO_FORMAT_ARGB32, width, height);
490 cairo_t *cairo = cairo_create(surface); 540 cairo_t *cairo = cairo_create(surface);
@@ -504,11 +554,11 @@ static void update_title_texture(struct sway_container *con,
504 554
505 cairo_surface_flush(surface); 555 cairo_surface_flush(surface);
506 unsigned char *data = cairo_image_surface_get_data(surface); 556 unsigned char *data = cairo_image_surface_get_data(surface);
507 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 557 int stride = cairo_image_surface_get_stride(surface);
508 struct wlr_renderer *renderer = wlr_backend_get_renderer( 558 struct wlr_renderer *renderer = wlr_backend_get_renderer(
509 output->wlr_output->backend); 559 output->wlr_output->backend);
510 *texture = wlr_texture_from_pixels( 560 *texture = wlr_texture_from_pixels(
511 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 561 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
512 cairo_surface_destroy(surface); 562 cairo_surface_destroy(surface);
513 g_object_unref(pango); 563 g_object_unref(pango);
514 cairo_destroy(cairo); 564 cairo_destroy(cairo);
@@ -596,23 +646,23 @@ size_t container_build_representation(enum sway_container_layout layout,
596 646
597void container_update_representation(struct sway_container *con) { 647void container_update_representation(struct sway_container *con) {
598 if (!con->view) { 648 if (!con->view) {
599 size_t len = container_build_representation(con->layout, 649 size_t len = container_build_representation(con->pending.layout,
600 con->children, NULL); 650 con->pending.children, NULL);
601 free(con->formatted_title); 651 free(con->formatted_title);
602 con->formatted_title = calloc(len + 1, sizeof(char)); 652 con->formatted_title = calloc(len + 1, sizeof(char));
603 if (!sway_assert(con->formatted_title, 653 if (!sway_assert(con->formatted_title,
604 "Unable to allocate title string")) { 654 "Unable to allocate title string")) {
605 return; 655 return;
606 } 656 }
607 container_build_representation(con->layout, con->children, 657 container_build_representation(con->pending.layout, con->pending.children,
608 con->formatted_title); 658 con->formatted_title);
609 container_calculate_title_height(con); 659 container_calculate_title_height(con);
610 container_update_title_textures(con); 660 container_update_title_textures(con);
611 } 661 }
612 if (con->parent) { 662 if (con->pending.parent) {
613 container_update_representation(con->parent); 663 container_update_representation(con->pending.parent);
614 } else if (con->workspace) { 664 } else if (con->pending.workspace) {
615 workspace_update_representation(con->workspace); 665 workspace_update_representation(con->pending.workspace);
616 } 666 }
617} 667}
618 668
@@ -663,20 +713,20 @@ static void floating_natural_resize(struct sway_container *con) {
663 floating_calculate_constraints(&min_width, &max_width, 713 floating_calculate_constraints(&min_width, &max_width,
664 &min_height, &max_height); 714 &min_height, &max_height);
665 if (!con->view) { 715 if (!con->view) {
666 con->width = fmax(min_width, fmin(con->width, max_width)); 716 con->pending.width = fmax(min_width, fmin(con->pending.width, max_width));
667 con->height = fmax(min_height, fmin(con->height, max_height)); 717 con->pending.height = fmax(min_height, fmin(con->pending.height, max_height));
668 } else { 718 } else {
669 struct sway_view *view = con->view; 719 struct sway_view *view = con->view;
670 con->content_width = 720 con->pending.content_width =
671 fmax(min_width, fmin(view->natural_width, max_width)); 721 fmax(min_width, fmin(view->natural_width, max_width));
672 con->content_height = 722 con->pending.content_height =
673 fmax(min_height, fmin(view->natural_height, max_height)); 723 fmax(min_height, fmin(view->natural_height, max_height));
674 container_set_geometry_from_content(con); 724 container_set_geometry_from_content(con);
675 } 725 }
676} 726}
677 727
678void container_floating_resize_and_center(struct sway_container *con) { 728void container_floating_resize_and_center(struct sway_container *con) {
679 struct sway_workspace *ws = con->workspace; 729 struct sway_workspace *ws = con->pending.workspace;
680 if (!ws) { 730 if (!ws) {
681 // On scratchpad, just resize 731 // On scratchpad, just resize
682 floating_natural_resize(con); 732 floating_natural_resize(con);
@@ -687,42 +737,42 @@ void container_floating_resize_and_center(struct sway_container *con) {
687 ws->output->wlr_output); 737 ws->output->wlr_output);
688 if (!ob) { 738 if (!ob) {
689 // On NOOP output. Will be called again when moved to an output 739 // On NOOP output. Will be called again when moved to an output
690 con->x = 0; 740 con->pending.x = 0;
691 con->y = 0; 741 con->pending.y = 0;
692 con->width = 0; 742 con->pending.width = 0;
693 con->height = 0; 743 con->pending.height = 0;
694 return; 744 return;
695 } 745 }
696 746
697 floating_natural_resize(con); 747 floating_natural_resize(con);
698 if (!con->view) { 748 if (!con->view) {
699 if (con->width > ws->width || con->height > ws->height) { 749 if (con->pending.width > ws->width || con->pending.height > ws->height) {
700 con->x = ob->x + (ob->width - con->width) / 2; 750 con->pending.x = ob->x + (ob->width - con->pending.width) / 2;
701 con->y = ob->y + (ob->height - con->height) / 2; 751 con->pending.y = ob->y + (ob->height - con->pending.height) / 2;
702 } else { 752 } else {
703 con->x = ws->x + (ws->width - con->width) / 2; 753 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
704 con->y = ws->y + (ws->height - con->height) / 2; 754 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
705 } 755 }
706 } else { 756 } else {
707 if (con->content_width > ws->width 757 if (con->pending.content_width > ws->width
708 || con->content_height > ws->height) { 758 || con->pending.content_height > ws->height) {
709 con->content_x = ob->x + (ob->width - con->content_width) / 2; 759 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; 760 con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2;
711 } else { 761 } else {
712 con->content_x = ws->x + (ws->width - con->content_width) / 2; 762 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; 763 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
714 } 764 }
715 765
716 // If the view's border is B_NONE then these properties are ignored. 766 // If the view's border is B_NONE then these properties are ignored.
717 con->border_top = con->border_bottom = true; 767 con->pending.border_top = con->pending.border_bottom = true;
718 con->border_left = con->border_right = true; 768 con->pending.border_left = con->pending.border_right = true;
719 769
720 container_set_geometry_from_content(con); 770 container_set_geometry_from_content(con);
721 } 771 }
722} 772}
723 773
724void container_floating_set_default_size(struct sway_container *con) { 774void container_floating_set_default_size(struct sway_container *con) {
725 if (!sway_assert(con->workspace, "Expected a container on a workspace")) { 775 if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) {
726 return; 776 return;
727 } 777 }
728 778
@@ -730,16 +780,16 @@ void container_floating_set_default_size(struct sway_container *con) {
730 floating_calculate_constraints(&min_width, &max_width, 780 floating_calculate_constraints(&min_width, &max_width,
731 &min_height, &max_height); 781 &min_height, &max_height);
732 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 782 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
733 workspace_get_box(con->workspace, box); 783 workspace_get_box(con->pending.workspace, box);
734 784
735 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 785 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)); 786 double height = fmax(min_height, fmin(box->height * 0.75, max_height));
737 if (!con->view) { 787 if (!con->view) {
738 con->width = width; 788 con->pending.width = width;
739 con->height = height; 789 con->pending.height = height;
740 } else { 790 } else {
741 con->content_width = width; 791 con->pending.content_width = width;
742 con->content_height = height; 792 con->pending.content_height = height;
743 container_set_geometry_from_content(con); 793 container_set_geometry_from_content(con);
744 } 794 }
745 795
@@ -761,8 +811,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) {
761 con->view->impl->set_resizing(con->view, resizing); 811 con->view->impl->set_resizing(con->view, resizing);
762 } 812 }
763 } else { 813 } else {
764 for (int i = 0; i < con->children->length; ++i ) { 814 for (int i = 0; i < con->pending.children->length; ++i ) {
765 struct sway_container *child = con->children->items[i]; 815 struct sway_container *child = con->pending.children->items[i];
766 container_set_resizing(child, resizing); 816 container_set_resizing(child, resizing);
767 } 817 }
768 } 818 }
@@ -774,21 +824,27 @@ void container_set_floating(struct sway_container *container, bool enable) {
774 } 824 }
775 825
776 struct sway_seat *seat = input_manager_current_seat(); 826 struct sway_seat *seat = input_manager_current_seat();
777 struct sway_workspace *workspace = container->workspace; 827 struct sway_workspace *workspace = container->pending.workspace;
828 struct sway_container *focus = seat_get_focused_container(seat);
829 bool set_focus = focus == container;
778 830
779 if (enable) { 831 if (enable) {
780 struct sway_container *old_parent = container->parent; 832 struct sway_container *old_parent = container->pending.parent;
781 container_detach(container); 833 container_detach(container);
782 workspace_add_floating(workspace, container); 834 workspace_add_floating(workspace, container);
783 if (container->view) { 835 if (container->view) {
784 view_set_tiled(container->view, false); 836 view_set_tiled(container->view, false);
785 if (container->view->using_csd) { 837 if (container->view->using_csd) {
786 container->border = B_CSD; 838 container->pending.border = B_CSD;
787 } 839 }
788 } 840 }
789 container_floating_set_default_size(container); 841 container_floating_set_default_size(container);
790 container_floating_resize_and_center(container); 842 container_floating_resize_and_center(container);
791 if (old_parent) { 843 if (old_parent) {
844 if (set_focus) {
845 seat_set_raw_focus(seat, &old_parent->node);
846 seat_set_raw_focus(seat, &container->node);
847 }
792 container_reap_empty(old_parent); 848 container_reap_empty(old_parent);
793 } 849 }
794 } else { 850 } else {
@@ -800,19 +856,23 @@ void container_set_floating(struct sway_container *container, bool enable) {
800 struct sway_container *reference = 856 struct sway_container *reference =
801 seat_get_focus_inactive_tiling(seat, workspace); 857 seat_get_focus_inactive_tiling(seat, workspace);
802 if (reference) { 858 if (reference) {
803 container_add_sibling(reference, container, 1); 859 if (reference->view) {
804 container->width = reference->width; 860 container_add_sibling(reference, container, 1);
805 container->height = reference->height; 861 } else {
862 container_add_child(reference, container);
863 }
864 container->pending.width = reference->pending.width;
865 container->pending.height = reference->pending.height;
806 } else { 866 } else {
807 struct sway_container *other = 867 struct sway_container *other =
808 workspace_add_tiling(workspace, container); 868 workspace_add_tiling(workspace, container);
809 other->width = workspace->width; 869 other->pending.width = workspace->width;
810 other->height = workspace->height; 870 other->pending.height = workspace->height;
811 } 871 }
812 if (container->view) { 872 if (container->view) {
813 view_set_tiled(container->view, true); 873 view_set_tiled(container->view, true);
814 if (container->view->using_csd) { 874 if (container->view->using_csd) {
815 container->border = container->saved_border; 875 container->pending.border = container->saved_border;
816 } 876 }
817 } 877 }
818 container->width_fraction = 0; 878 container->width_fraction = 0;
@@ -834,22 +894,33 @@ void container_set_geometry_from_content(struct sway_container *con) {
834 size_t border_width = 0; 894 size_t border_width = 0;
835 size_t top = 0; 895 size_t top = 0;
836 896
837 if (con->border != B_CSD) { 897 if (con->pending.border != B_CSD) {
838 border_width = con->border_thickness * (con->border != B_NONE); 898 border_width = con->pending.border_thickness * (con->pending.border != B_NONE);
839 top = con->border == B_NORMAL ? 899 top = con->pending.border == B_NORMAL ?
840 container_titlebar_height() : border_width; 900 container_titlebar_height() : border_width;
841 } 901 }
842 902
843 con->x = con->content_x - border_width; 903 con->pending.x = con->pending.content_x - border_width;
844 con->y = con->content_y - top; 904 con->pending.y = con->pending.content_y - top;
845 con->width = con->content_width + border_width * 2; 905 con->pending.width = con->pending.content_width + border_width * 2;
846 con->height = top + con->content_height + border_width; 906 con->pending.height = top + con->pending.content_height + border_width;
847 node_set_dirty(&con->node); 907 node_set_dirty(&con->node);
848} 908}
849 909
850bool container_is_floating(struct sway_container *container) { 910bool container_is_floating(struct sway_container *container) {
851 if (!container->parent && container->workspace && 911 if (!container->pending.parent && container->pending.workspace &&
852 list_find(container->workspace->floating, container) != -1) { 912 list_find(container->pending.workspace->floating, container) != -1) {
913 return true;
914 }
915 if (container->scratchpad) {
916 return true;
917 }
918 return false;
919}
920
921bool container_is_current_floating(struct sway_container *container) {
922 if (!container->current.parent && container->current.workspace &&
923 list_find(container->current.workspace->floating, container) != -1) {
853 return true; 924 return true;
854 } 925 }
855 if (container->scratchpad) { 926 if (container->scratchpad) {
@@ -859,10 +930,10 @@ bool container_is_floating(struct sway_container *container) {
859} 930}
860 931
861void container_get_box(struct sway_container *container, struct wlr_box *box) { 932void container_get_box(struct sway_container *container, struct wlr_box *box) {
862 box->x = container->x; 933 box->x = container->pending.x;
863 box->y = container->y; 934 box->y = container->pending.y;
864 box->width = container->width; 935 box->width = container->pending.width;
865 box->height = container->height; 936 box->height = container->pending.height;
866} 937}
867 938
868/** 939/**
@@ -870,14 +941,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
870 */ 941 */
871void container_floating_translate(struct sway_container *con, 942void container_floating_translate(struct sway_container *con,
872 double x_amount, double y_amount) { 943 double x_amount, double y_amount) {
873 con->x += x_amount; 944 con->pending.x += x_amount;
874 con->y += y_amount; 945 con->pending.y += y_amount;
875 con->content_x += x_amount; 946 con->pending.content_x += x_amount;
876 con->content_y += y_amount; 947 con->pending.content_y += y_amount;
877 948
878 if (con->children) { 949 if (con->pending.children) {
879 for (int i = 0; i < con->children->length; ++i) { 950 for (int i = 0; i < con->pending.children->length; ++i) {
880 struct sway_container *child = con->children->items[i]; 951 struct sway_container *child = con->pending.children->items[i];
881 container_floating_translate(child, x_amount, y_amount); 952 container_floating_translate(child, x_amount, y_amount);
882 } 953 }
883 } 954 }
@@ -893,8 +964,8 @@ void container_floating_translate(struct sway_container *con,
893 * center. 964 * center.
894 */ 965 */
895struct sway_output *container_floating_find_output(struct sway_container *con) { 966struct sway_output *container_floating_find_output(struct sway_container *con) {
896 double center_x = con->x + con->width / 2; 967 double center_x = con->pending.x + con->pending.width / 2;
897 double center_y = con->y + con->height / 2; 968 double center_y = con->pending.y + con->pending.height / 2;
898 struct sway_output *closest_output = NULL; 969 struct sway_output *closest_output = NULL;
899 double closest_distance = DBL_MAX; 970 double closest_distance = DBL_MAX;
900 for (int i = 0; i < root->outputs->length; ++i) { 971 for (int i = 0; i < root->outputs->length; ++i) {
@@ -925,11 +996,11 @@ void container_floating_move_to(struct sway_container *con,
925 "Expected a floating container")) { 996 "Expected a floating container")) {
926 return; 997 return;
927 } 998 }
928 container_floating_translate(con, lx - con->x, ly - con->y); 999 container_floating_translate(con, lx - con->pending.x, ly - con->pending.y);
929 if (container_is_scratchpad_hidden(con)) { 1000 if (container_is_scratchpad_hidden(con)) {
930 return; 1001 return;
931 } 1002 }
932 struct sway_workspace *old_workspace = con->workspace; 1003 struct sway_workspace *old_workspace = con->pending.workspace;
933 struct sway_output *new_output = container_floating_find_output(con); 1004 struct sway_output *new_output = container_floating_find_output(con);
934 if (!sway_assert(new_output, "Unable to find any output")) { 1005 if (!sway_assert(new_output, "Unable to find any output")) {
935 return; 1006 return;
@@ -951,10 +1022,10 @@ void container_floating_move_to_center(struct sway_container *con) {
951 "Expected a floating container")) { 1022 "Expected a floating container")) {
952 return; 1023 return;
953 } 1024 }
954 struct sway_workspace *ws = con->workspace; 1025 struct sway_workspace *ws = con->pending.workspace;
955 double new_lx = ws->x + (ws->width - con->width) / 2; 1026 double new_lx = ws->x + (ws->width - con->pending.width) / 2;
956 double new_ly = ws->y + (ws->height - con->height) / 2; 1027 double new_ly = ws->y + (ws->height - con->pending.height) / 2;
957 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 1028 container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y);
958} 1029}
959 1030
960static bool find_urgent_iterator(struct sway_container *con, void *data) { 1031static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -972,42 +1043,39 @@ void container_end_mouse_operation(struct sway_container *container) {
972 } 1043 }
973} 1044}
974 1045
975static void set_fullscreen_iterator(struct sway_container *con, void *data) { 1046static void set_fullscreen(struct sway_container *con, bool enable) {
976 if (!con->view) { 1047 if (!con->view) {
977 return; 1048 return;
978 } 1049 }
979 if (con->view->impl->set_fullscreen) { 1050 if (con->view->impl->set_fullscreen) {
980 bool *enable = data; 1051 con->view->impl->set_fullscreen(con->view, enable);
981 con->view->impl->set_fullscreen(con->view, *enable);
982 if (con->view->foreign_toplevel) { 1052 if (con->view->foreign_toplevel) {
983 wlr_foreign_toplevel_handle_v1_set_fullscreen( 1053 wlr_foreign_toplevel_handle_v1_set_fullscreen(
984 con->view->foreign_toplevel, *enable); 1054 con->view->foreign_toplevel, enable);
985 } 1055 }
986 } 1056 }
987} 1057}
988 1058
989static void container_fullscreen_workspace(struct sway_container *con) { 1059static void container_fullscreen_workspace(struct sway_container *con) {
990 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1060 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
991 "Expected a non-fullscreen container")) { 1061 "Expected a non-fullscreen container")) {
992 return; 1062 return;
993 } 1063 }
994 bool enable = true; 1064 set_fullscreen(con, true);
995 set_fullscreen_iterator(con, &enable); 1065 con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;
996 container_for_each_child(con, set_fullscreen_iterator, &enable);
997 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
998 1066
999 con->saved_x = con->x; 1067 con->saved_x = con->pending.x;
1000 con->saved_y = con->y; 1068 con->saved_y = con->pending.y;
1001 con->saved_width = con->width; 1069 con->saved_width = con->pending.width;
1002 con->saved_height = con->height; 1070 con->saved_height = con->pending.height;
1003 1071
1004 if (con->workspace) { 1072 if (con->pending.workspace) {
1005 con->workspace->fullscreen = con; 1073 con->pending.workspace->fullscreen = con;
1006 struct sway_seat *seat; 1074 struct sway_seat *seat;
1007 struct sway_workspace *focus_ws; 1075 struct sway_workspace *focus_ws;
1008 wl_list_for_each(seat, &server.input->seats, link) { 1076 wl_list_for_each(seat, &server.input->seats, link) {
1009 focus_ws = seat_get_focused_workspace(seat); 1077 focus_ws = seat_get_focused_workspace(seat);
1010 if (focus_ws == con->workspace) { 1078 if (focus_ws == con->pending.workspace) {
1011 seat_set_focus_container(seat, con); 1079 seat_set_focus_container(seat, con);
1012 } else { 1080 } else {
1013 struct sway_node *focus = 1081 struct sway_node *focus =
@@ -1023,19 +1091,17 @@ static void container_fullscreen_workspace(struct sway_container *con) {
1023} 1091}
1024 1092
1025static void container_fullscreen_global(struct sway_container *con) { 1093static void container_fullscreen_global(struct sway_container *con) {
1026 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1094 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
1027 "Expected a non-fullscreen container")) { 1095 "Expected a non-fullscreen container")) {
1028 return; 1096 return;
1029 } 1097 }
1030 bool enable = true; 1098 set_fullscreen(con, true);
1031 set_fullscreen_iterator(con, &enable);
1032 container_for_each_child(con, set_fullscreen_iterator, &enable);
1033 1099
1034 root->fullscreen_global = con; 1100 root->fullscreen_global = con;
1035 con->saved_x = con->x; 1101 con->saved_x = con->pending.x;
1036 con->saved_y = con->y; 1102 con->saved_y = con->pending.y;
1037 con->saved_width = con->width; 1103 con->saved_width = con->pending.width;
1038 con->saved_height = con->height; 1104 con->saved_height = con->pending.height;
1039 1105
1040 struct sway_seat *seat; 1106 struct sway_seat *seat;
1041 wl_list_for_each(seat, &server.input->seats, link) { 1107 wl_list_for_each(seat, &server.input->seats, link) {
@@ -1045,34 +1111,32 @@ static void container_fullscreen_global(struct sway_container *con) {
1045 } 1111 }
1046 } 1112 }
1047 1113
1048 con->fullscreen_mode = FULLSCREEN_GLOBAL; 1114 con->pending.fullscreen_mode = FULLSCREEN_GLOBAL;
1049 container_end_mouse_operation(con); 1115 container_end_mouse_operation(con);
1050 ipc_event_window(con, "fullscreen_mode"); 1116 ipc_event_window(con, "fullscreen_mode");
1051} 1117}
1052 1118
1053void container_fullscreen_disable(struct sway_container *con) { 1119void container_fullscreen_disable(struct sway_container *con) {
1054 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, 1120 if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,
1055 "Expected a fullscreen container")) { 1121 "Expected a fullscreen container")) {
1056 return; 1122 return;
1057 } 1123 }
1058 bool enable = false; 1124 set_fullscreen(con, false);
1059 set_fullscreen_iterator(con, &enable);
1060 container_for_each_child(con, set_fullscreen_iterator, &enable);
1061 1125
1062 if (container_is_floating(con)) { 1126 if (container_is_floating(con)) {
1063 con->x = con->saved_x; 1127 con->pending.x = con->saved_x;
1064 con->y = con->saved_y; 1128 con->pending.y = con->saved_y;
1065 con->width = con->saved_width; 1129 con->pending.width = con->saved_width;
1066 con->height = con->saved_height; 1130 con->pending.height = con->saved_height;
1067 } 1131 }
1068 1132
1069 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1133 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1070 if (con->workspace) { 1134 if (con->pending.workspace) {
1071 con->workspace->fullscreen = NULL; 1135 con->pending.workspace->fullscreen = NULL;
1072 if (container_is_floating(con)) { 1136 if (container_is_floating(con)) {
1073 struct sway_output *output = 1137 struct sway_output *output =
1074 container_floating_find_output(con); 1138 container_floating_find_output(con);
1075 if (con->workspace->output != output) { 1139 if (con->pending.workspace->output != output) {
1076 container_floating_move_to_center(con); 1140 container_floating_move_to_center(con);
1077 } 1141 }
1078 } 1142 }
@@ -1084,11 +1148,11 @@ void container_fullscreen_disable(struct sway_container *con) {
1084 // If the container was mapped as fullscreen and set as floating by 1148 // 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 1149 // criteria, it needs to be reinitialized as floating to get the proper
1086 // size and location 1150 // size and location
1087 if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { 1151 if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {
1088 container_floating_resize_and_center(con); 1152 container_floating_resize_and_center(con);
1089 } 1153 }
1090 1154
1091 con->fullscreen_mode = FULLSCREEN_NONE; 1155 con->pending.fullscreen_mode = FULLSCREEN_NONE;
1092 container_end_mouse_operation(con); 1156 container_end_mouse_operation(con);
1093 ipc_event_window(con, "fullscreen_mode"); 1157 ipc_event_window(con, "fullscreen_mode");
1094 1158
@@ -1106,7 +1170,7 @@ void container_fullscreen_disable(struct sway_container *con) {
1106 1170
1107void container_set_fullscreen(struct sway_container *con, 1171void container_set_fullscreen(struct sway_container *con,
1108 enum sway_fullscreen_mode mode) { 1172 enum sway_fullscreen_mode mode) {
1109 if (con->fullscreen_mode == mode) { 1173 if (con->pending.fullscreen_mode == mode) {
1110 return; 1174 return;
1111 } 1175 }
1112 1176
@@ -1118,8 +1182,8 @@ void container_set_fullscreen(struct sway_container *con,
1118 if (root->fullscreen_global) { 1182 if (root->fullscreen_global) {
1119 container_fullscreen_disable(root->fullscreen_global); 1183 container_fullscreen_disable(root->fullscreen_global);
1120 } 1184 }
1121 if (con->workspace && con->workspace->fullscreen) { 1185 if (con->pending.workspace && con->pending.workspace->fullscreen) {
1122 container_fullscreen_disable(con->workspace->fullscreen); 1186 container_fullscreen_disable(con->pending.workspace->fullscreen);
1123 } 1187 }
1124 container_fullscreen_workspace(con); 1188 container_fullscreen_workspace(con);
1125 break; 1189 break;
@@ -1127,7 +1191,7 @@ void container_set_fullscreen(struct sway_container *con,
1127 if (root->fullscreen_global) { 1191 if (root->fullscreen_global) {
1128 container_fullscreen_disable(root->fullscreen_global); 1192 container_fullscreen_disable(root->fullscreen_global);
1129 } 1193 }
1130 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1194 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1131 container_fullscreen_disable(con); 1195 container_fullscreen_disable(con);
1132 } 1196 }
1133 container_fullscreen_global(con); 1197 container_fullscreen_global(con);
@@ -1137,8 +1201,8 @@ void container_set_fullscreen(struct sway_container *con,
1137 1201
1138struct sway_container *container_toplevel_ancestor( 1202struct sway_container *container_toplevel_ancestor(
1139 struct sway_container *container) { 1203 struct sway_container *container) {
1140 while (container->parent) { 1204 while (container->pending.parent) {
1141 container = container->parent; 1205 container = container->pending.parent;
1142 } 1206 }
1143 1207
1144 return container; 1208 return container;
@@ -1150,10 +1214,10 @@ bool container_is_floating_or_child(struct sway_container *container) {
1150 1214
1151bool container_is_fullscreen_or_child(struct sway_container *container) { 1215bool container_is_fullscreen_or_child(struct sway_container *container) {
1152 do { 1216 do {
1153 if (container->fullscreen_mode) { 1217 if (container->pending.fullscreen_mode) {
1154 return true; 1218 return true;
1155 } 1219 }
1156 container = container->parent; 1220 container = container->pending.parent;
1157 } while (container); 1221 } while (container);
1158 1222
1159 return false; 1223 return false;
@@ -1226,11 +1290,11 @@ void container_discover_outputs(struct sway_container *con) {
1226} 1290}
1227 1291
1228enum sway_container_layout container_parent_layout(struct sway_container *con) { 1292enum sway_container_layout container_parent_layout(struct sway_container *con) {
1229 if (con->parent) { 1293 if (con->pending.parent) {
1230 return con->parent->layout; 1294 return con->pending.parent->pending.layout;
1231 } 1295 }
1232 if (con->workspace) { 1296 if (con->pending.workspace) {
1233 return con->workspace->layout; 1297 return con->pending.workspace->layout;
1234 } 1298 }
1235 return L_NONE; 1299 return L_NONE;
1236} 1300}
@@ -1244,16 +1308,16 @@ enum sway_container_layout container_current_parent_layout(
1244} 1308}
1245 1309
1246list_t *container_get_siblings(struct sway_container *container) { 1310list_t *container_get_siblings(struct sway_container *container) {
1247 if (container->parent) { 1311 if (container->pending.parent) {
1248 return container->parent->children; 1312 return container->pending.parent->pending.children;
1249 } 1313 }
1250 if (container_is_scratchpad_hidden(container)) { 1314 if (container_is_scratchpad_hidden(container)) {
1251 return NULL; 1315 return NULL;
1252 } 1316 }
1253 if (list_find(container->workspace->tiling, container) != -1) { 1317 if (list_find(container->pending.workspace->tiling, container) != -1) {
1254 return container->workspace->tiling; 1318 return container->pending.workspace->tiling;
1255 } 1319 }
1256 return container->workspace->floating; 1320 return container->pending.workspace->floating;
1257} 1321}
1258 1322
1259int container_sibling_index(struct sway_container *child) { 1323int container_sibling_index(struct sway_container *child) {
@@ -1268,30 +1332,30 @@ list_t *container_get_current_siblings(struct sway_container *container) {
1268} 1332}
1269 1333
1270void container_handle_fullscreen_reparent(struct sway_container *con) { 1334void container_handle_fullscreen_reparent(struct sway_container *con) {
1271 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || 1335 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1272 con->workspace->fullscreen == con) { 1336 con->pending.workspace->fullscreen == con) {
1273 return; 1337 return;
1274 } 1338 }
1275 if (con->workspace->fullscreen) { 1339 if (con->pending.workspace->fullscreen) {
1276 container_fullscreen_disable(con->workspace->fullscreen); 1340 container_fullscreen_disable(con->pending.workspace->fullscreen);
1277 } 1341 }
1278 con->workspace->fullscreen = con; 1342 con->pending.workspace->fullscreen = con;
1279 1343
1280 arrange_workspace(con->workspace); 1344 arrange_workspace(con->pending.workspace);
1281} 1345}
1282 1346
1283static void set_workspace(struct sway_container *container, void *data) { 1347static void set_workspace(struct sway_container *container, void *data) {
1284 container->workspace = container->parent->workspace; 1348 container->pending.workspace = container->pending.parent->pending.workspace;
1285} 1349}
1286 1350
1287void container_insert_child(struct sway_container *parent, 1351void container_insert_child(struct sway_container *parent,
1288 struct sway_container *child, int i) { 1352 struct sway_container *child, int i) {
1289 if (child->workspace) { 1353 if (child->pending.workspace) {
1290 container_detach(child); 1354 container_detach(child);
1291 } 1355 }
1292 list_insert(parent->children, i, child); 1356 list_insert(parent->pending.children, i, child);
1293 child->parent = parent; 1357 child->pending.parent = parent;
1294 child->workspace = parent->workspace; 1358 child->pending.workspace = parent->pending.workspace;
1295 container_for_each_child(child, set_workspace, NULL); 1359 container_for_each_child(child, set_workspace, NULL);
1296 container_handle_fullscreen_reparent(child); 1360 container_handle_fullscreen_reparent(child);
1297 container_update_representation(parent); 1361 container_update_representation(parent);
@@ -1299,14 +1363,14 @@ void container_insert_child(struct sway_container *parent,
1299 1363
1300void container_add_sibling(struct sway_container *fixed, 1364void container_add_sibling(struct sway_container *fixed,
1301 struct sway_container *active, bool after) { 1365 struct sway_container *active, bool after) {
1302 if (active->workspace) { 1366 if (active->pending.workspace) {
1303 container_detach(active); 1367 container_detach(active);
1304 } 1368 }
1305 list_t *siblings = container_get_siblings(fixed); 1369 list_t *siblings = container_get_siblings(fixed);
1306 int index = list_find(siblings, fixed); 1370 int index = list_find(siblings, fixed);
1307 list_insert(siblings, index + after, active); 1371 list_insert(siblings, index + after, active);
1308 active->parent = fixed->parent; 1372 active->pending.parent = fixed->pending.parent;
1309 active->workspace = fixed->workspace; 1373 active->pending.workspace = fixed->pending.workspace;
1310 container_for_each_child(active, set_workspace, NULL); 1374 container_for_each_child(active, set_workspace, NULL);
1311 container_handle_fullscreen_reparent(active); 1375 container_handle_fullscreen_reparent(active);
1312 container_update_representation(active); 1376 container_update_representation(active);
@@ -1314,17 +1378,13 @@ void container_add_sibling(struct sway_container *fixed,
1314 1378
1315void container_add_child(struct sway_container *parent, 1379void container_add_child(struct sway_container *parent,
1316 struct sway_container *child) { 1380 struct sway_container *child) {
1317 if (child->workspace) { 1381 if (child->pending.workspace) {
1318 container_detach(child); 1382 container_detach(child);
1319 } 1383 }
1320 list_add(parent->children, child); 1384 list_add(parent->pending.children, child);
1321 child->parent = parent; 1385 child->pending.parent = parent;
1322 child->workspace = parent->workspace; 1386 child->pending.workspace = parent->pending.workspace;
1323 container_for_each_child(child, set_workspace, NULL); 1387 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); 1388 container_handle_fullscreen_reparent(child);
1329 container_update_representation(parent); 1389 container_update_representation(parent);
1330 node_set_dirty(&child->node); 1390 node_set_dirty(&child->node);
@@ -1332,15 +1392,15 @@ void container_add_child(struct sway_container *parent,
1332} 1392}
1333 1393
1334void container_detach(struct sway_container *child) { 1394void container_detach(struct sway_container *child) {
1335 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1395 if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1336 child->workspace->fullscreen = NULL; 1396 child->pending.workspace->fullscreen = NULL;
1337 } 1397 }
1338 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { 1398 if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1339 root->fullscreen_global = NULL; 1399 root->fullscreen_global = NULL;
1340 } 1400 }
1341 1401
1342 struct sway_container *old_parent = child->parent; 1402 struct sway_container *old_parent = child->pending.parent;
1343 struct sway_workspace *old_workspace = child->workspace; 1403 struct sway_workspace *old_workspace = child->pending.workspace;
1344 list_t *siblings = container_get_siblings(child); 1404 list_t *siblings = container_get_siblings(child);
1345 if (siblings) { 1405 if (siblings) {
1346 int index = list_find(siblings, child); 1406 int index = list_find(siblings, child);
@@ -1348,8 +1408,8 @@ void container_detach(struct sway_container *child) {
1348 list_del(siblings, index); 1408 list_del(siblings, index);
1349 } 1409 }
1350 } 1410 }
1351 child->parent = NULL; 1411 child->pending.parent = NULL;
1352 child->workspace = NULL; 1412 child->pending.workspace = NULL;
1353 container_for_each_child(child, set_workspace, NULL); 1413 container_for_each_child(child, set_workspace, NULL);
1354 1414
1355 if (old_parent) { 1415 if (old_parent) {
@@ -1364,18 +1424,18 @@ void container_detach(struct sway_container *child) {
1364 1424
1365void container_replace(struct sway_container *container, 1425void container_replace(struct sway_container *container,
1366 struct sway_container *replacement) { 1426 struct sway_container *replacement) {
1367 enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; 1427 enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode;
1368 bool scratchpad = container->scratchpad; 1428 bool scratchpad = container->scratchpad;
1369 struct sway_workspace *ws = NULL; 1429 struct sway_workspace *ws = NULL;
1370 if (fullscreen != FULLSCREEN_NONE) { 1430 if (fullscreen != FULLSCREEN_NONE) {
1371 container_fullscreen_disable(container); 1431 container_fullscreen_disable(container);
1372 } 1432 }
1373 if (scratchpad) { 1433 if (scratchpad) {
1374 ws = container->workspace; 1434 ws = container->pending.workspace;
1375 root_scratchpad_show(container); 1435 root_scratchpad_show(container);
1376 root_scratchpad_remove_container(container); 1436 root_scratchpad_remove_container(container);
1377 } 1437 }
1378 if (container->parent || container->workspace) { 1438 if (container->pending.parent || container->pending.workspace) {
1379 float width_fraction = container->width_fraction; 1439 float width_fraction = container->width_fraction;
1380 float height_fraction = container->height_fraction; 1440 float height_fraction = container->height_fraction;
1381 container_add_sibling(container, replacement, 1); 1441 container_add_sibling(container, replacement, 1);
@@ -1403,7 +1463,7 @@ struct sway_container *container_split(struct sway_container *child,
1403 enum sway_container_layout layout) { 1463 enum sway_container_layout layout) {
1404 // i3 doesn't split singleton H/V containers 1464 // i3 doesn't split singleton H/V containers
1405 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 1465 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354
1406 if (child->parent || child->workspace) { 1466 if (child->pending.parent || child->pending.workspace) {
1407 list_t *siblings = container_get_siblings(child); 1467 list_t *siblings = container_get_siblings(child);
1408 if (siblings->length == 1) { 1468 if (siblings->length == 1) {
1409 enum sway_container_layout current = container_parent_layout(child); 1469 enum sway_container_layout current = container_parent_layout(child);
@@ -1411,12 +1471,12 @@ struct sway_container *container_split(struct sway_container *child,
1411 current = L_NONE; 1471 current = L_NONE;
1412 } 1472 }
1413 if (current == L_HORIZ || current == L_VERT) { 1473 if (current == L_HORIZ || current == L_VERT) {
1414 if (child->parent) { 1474 if (child->pending.parent) {
1415 child->parent->layout = layout; 1475 child->pending.parent->pending.layout = layout;
1416 container_update_representation(child->parent); 1476 container_update_representation(child->pending.parent);
1417 } else { 1477 } else {
1418 child->workspace->layout = layout; 1478 child->pending.workspace->layout = layout;
1419 workspace_update_representation(child->workspace); 1479 workspace_update_representation(child->pending.workspace);
1420 } 1480 }
1421 return child; 1481 return child;
1422 } 1482 }
@@ -1429,25 +1489,25 @@ struct sway_container *container_split(struct sway_container *child,
1429 if (container_is_floating(child) && child->view) { 1489 if (container_is_floating(child) && child->view) {
1430 view_set_tiled(child->view, true); 1490 view_set_tiled(child->view, true);
1431 if (child->view->using_csd) { 1491 if (child->view->using_csd) {
1432 child->border = child->saved_border; 1492 child->pending.border = child->saved_border;
1433 } 1493 }
1434 } 1494 }
1435 1495
1436 struct sway_container *cont = container_create(NULL); 1496 struct sway_container *cont = container_create(NULL);
1437 cont->width = child->width; 1497 cont->pending.width = child->pending.width;
1438 cont->height = child->height; 1498 cont->pending.height = child->pending.height;
1439 cont->width_fraction = child->width_fraction; 1499 cont->width_fraction = child->width_fraction;
1440 cont->height_fraction = child->height_fraction; 1500 cont->height_fraction = child->height_fraction;
1441 cont->x = child->x; 1501 cont->pending.x = child->pending.x;
1442 cont->y = child->y; 1502 cont->pending.y = child->pending.y;
1443 cont->layout = layout; 1503 cont->pending.layout = layout;
1444 1504
1445 container_replace(child, cont); 1505 container_replace(child, cont);
1446 container_add_child(cont, child); 1506 container_add_child(cont, child);
1447 1507
1448 if (set_focus) { 1508 if (set_focus) {
1449 seat_set_raw_focus(seat, &cont->node); 1509 seat_set_raw_focus(seat, &cont->node);
1450 if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { 1510 if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1451 seat_set_focus(seat, &child->node); 1511 seat_set_focus(seat, &child->node);
1452 } else { 1512 } else {
1453 seat_set_raw_focus(seat, &child->node); 1513 seat_set_raw_focus(seat, &child->node);
@@ -1563,6 +1623,10 @@ static void update_marks_texture(struct sway_container *con,
1563 "%s", buffer); 1623 "%s", buffer);
1564 cairo_destroy(c); 1624 cairo_destroy(c);
1565 1625
1626 if (width == 0 || height == 0) {
1627 return;
1628 }
1629
1566 cairo_surface_t *surface = cairo_image_surface_create( 1630 cairo_surface_t *surface = cairo_image_surface_create(
1567 CAIRO_FORMAT_ARGB32, width, height); 1631 CAIRO_FORMAT_ARGB32, width, height);
1568 cairo_t *cairo = cairo_create(surface); 1632 cairo_t *cairo = cairo_create(surface);
@@ -1579,11 +1643,11 @@ static void update_marks_texture(struct sway_container *con,
1579 1643
1580 cairo_surface_flush(surface); 1644 cairo_surface_flush(surface);
1581 unsigned char *data = cairo_image_surface_get_data(surface); 1645 unsigned char *data = cairo_image_surface_get_data(surface);
1582 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 1646 int stride = cairo_image_surface_get_stride(surface);
1583 struct wlr_renderer *renderer = wlr_backend_get_renderer( 1647 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1584 output->wlr_output->backend); 1648 output->wlr_output->backend);
1585 *texture = wlr_texture_from_pixels( 1649 *texture = wlr_texture_from_pixels(
1586 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 1650 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
1587 cairo_surface_destroy(surface); 1651 cairo_surface_destroy(surface);
1588 g_object_unref(pango); 1652 g_object_unref(pango);
1589 cairo_destroy(cairo); 1653 cairo_destroy(cairo);
@@ -1608,19 +1672,19 @@ void container_update_marks_textures(struct sway_container *con) {
1608void container_raise_floating(struct sway_container *con) { 1672void container_raise_floating(struct sway_container *con) {
1609 // Bring container to front by putting it at the end of the floating list. 1673 // Bring container to front by putting it at the end of the floating list.
1610 struct sway_container *floater = container_toplevel_ancestor(con); 1674 struct sway_container *floater = container_toplevel_ancestor(con);
1611 if (container_is_floating(floater) && floater->workspace) { 1675 if (container_is_floating(floater) && floater->pending.workspace) {
1612 list_move_to_end(floater->workspace->floating, floater); 1676 list_move_to_end(floater->pending.workspace->floating, floater);
1613 node_set_dirty(&floater->workspace->node); 1677 node_set_dirty(&floater->pending.workspace->node);
1614 } 1678 }
1615} 1679}
1616 1680
1617bool container_is_scratchpad_hidden(struct sway_container *con) { 1681bool container_is_scratchpad_hidden(struct sway_container *con) {
1618 return con->scratchpad && !con->workspace; 1682 return con->scratchpad && !con->pending.workspace;
1619} 1683}
1620 1684
1621bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { 1685bool container_is_scratchpad_hidden_or_child(struct sway_container *con) {
1622 con = container_toplevel_ancestor(con); 1686 con = container_toplevel_ancestor(con);
1623 return con->scratchpad && !con->workspace; 1687 return con->scratchpad && !con->pending.workspace;
1624} 1688}
1625 1689
1626bool container_is_sticky(struct sway_container *con) { 1690bool container_is_sticky(struct sway_container *con) {
@@ -1648,39 +1712,39 @@ static bool is_parallel(enum sway_container_layout first,
1648static bool container_is_squashable(struct sway_container *con, 1712static bool container_is_squashable(struct sway_container *con,
1649 struct sway_container *child) { 1713 struct sway_container *child) {
1650 enum sway_container_layout gp_layout = container_parent_layout(con); 1714 enum sway_container_layout gp_layout = container_parent_layout(con);
1651 return (con->layout == L_HORIZ || con->layout == L_VERT) && 1715 return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) &&
1652 (child->layout == L_HORIZ || child->layout == L_VERT) && 1716 (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) &&
1653 !is_parallel(con->layout, child->layout) && 1717 !is_parallel(con->pending.layout, child->pending.layout) &&
1654 is_parallel(gp_layout, child->layout); 1718 is_parallel(gp_layout, child->pending.layout);
1655} 1719}
1656 1720
1657static void container_squash_children(struct sway_container *con) { 1721static void container_squash_children(struct sway_container *con) {
1658 for (int i = 0; i < con->children->length; i++) { 1722 for (int i = 0; i < con->pending.children->length; i++) {
1659 struct sway_container *child = con->children->items[i]; 1723 struct sway_container *child = con->pending.children->items[i];
1660 i += container_squash(child); 1724 i += container_squash(child);
1661 } 1725 }
1662} 1726}
1663 1727
1664int container_squash(struct sway_container *con) { 1728int container_squash(struct sway_container *con) {
1665 if (!con->children) { 1729 if (!con->pending.children) {
1666 return 0; 1730 return 0;
1667 } 1731 }
1668 if (con->children->length != 1) { 1732 if (con->pending.children->length != 1) {
1669 container_squash_children(con); 1733 container_squash_children(con);
1670 return 0; 1734 return 0;
1671 } 1735 }
1672 struct sway_container *child = con->children->items[0]; 1736 struct sway_container *child = con->pending.children->items[0];
1673 int idx = container_sibling_index(con); 1737 int idx = container_sibling_index(con);
1674 int change = 0; 1738 int change = 0;
1675 if (container_is_squashable(con, child)) { 1739 if (container_is_squashable(con, child)) {
1676 // con and child are a redundant H/V pair. Destroy them. 1740 // con and child are a redundant H/V pair. Destroy them.
1677 while (child->children->length) { 1741 while (child->pending.children->length) {
1678 struct sway_container *current = child->children->items[0]; 1742 struct sway_container *current = child->pending.children->items[0];
1679 container_detach(current); 1743 container_detach(current);
1680 if (con->parent) { 1744 if (con->pending.parent) {
1681 container_insert_child(con->parent, current, idx); 1745 container_insert_child(con->pending.parent, current, idx);
1682 } else { 1746 } else {
1683 workspace_insert_tiling_direct(con->workspace, current, idx); 1747 workspace_insert_tiling_direct(con->pending.workspace, current, idx);
1684 } 1748 }
1685 change++; 1749 change++;
1686 } 1750 }
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..fcdd06f7 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,19 +882,21 @@ 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;
@@ -968,15 +992,25 @@ static void view_child_subsurface_create(struct sway_view_child *child,
968 view_child_damage(&subsurface->child, true); 992 view_child_damage(&subsurface->child, true);
969} 993}
970 994
995static bool view_child_is_mapped(struct sway_view_child *child) {
996 while (child) {
997 if (!child->mapped) {
998 return false;
999 }
1000 child = child->parent;
1001 }
1002 return true;
1003}
1004
971static void view_child_damage(struct sway_view_child *child, bool whole) { 1005static void view_child_damage(struct sway_view_child *child, bool whole) {
972 if (!child || !child->mapped || !child->view || !child->view->container) { 1006 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
973 return; 1007 return;
974 } 1008 }
975 int sx, sy; 1009 int sx, sy;
976 child->impl->get_root_coords(child, &sx, &sy); 1010 child->impl->get_root_coords(child, &sx, &sy);
977 desktop_damage_surface(child->surface, 1011 desktop_damage_surface(child->surface,
978 child->view->container->content_x + sx, 1012 child->view->container->pending.content_x + sx,
979 child->view->container->content_y + sy, whole); 1013 child->view->container->pending.content_y + sy, whole);
980} 1014}
981 1015
982static void view_child_handle_surface_commit(struct wl_listener *listener, 1016static void view_child_handle_surface_commit(struct wl_listener *listener,
@@ -1004,11 +1038,25 @@ static void view_child_handle_surface_destroy(struct wl_listener *listener,
1004static void view_init_subsurfaces(struct sway_view *view, 1038static void view_init_subsurfaces(struct sway_view *view,
1005 struct wlr_surface *surface) { 1039 struct wlr_surface *surface) {
1006 struct wlr_subsurface *subsurface; 1040 struct wlr_subsurface *subsurface;
1007 wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { 1041 wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) {
1042 view_subsurface_create(view, subsurface);
1043 }
1044 wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) {
1008 view_subsurface_create(view, subsurface); 1045 view_subsurface_create(view, subsurface);
1009 } 1046 }
1010} 1047}
1011 1048
1049static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1050 struct wlr_surface *surface) {
1051 struct wlr_subsurface *subsurface;
1052 wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) {
1053 view_child_subsurface_create(view_child, subsurface);
1054 }
1055 wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) {
1056 view_child_subsurface_create(view_child, subsurface);
1057 }
1058}
1059
1012static void view_child_handle_surface_map(struct wl_listener *listener, 1060static void view_child_handle_surface_map(struct wl_listener *listener,
1013 void *data) { 1061 void *data) {
1014 struct sway_view_child *child = 1062 struct sway_view_child *child =
@@ -1059,16 +1107,16 @@ void view_child_init(struct sway_view_child *child,
1059 wl_signal_add(&view->events.unmap, &child->view_unmap); 1107 wl_signal_add(&view->events.unmap, &child->view_unmap);
1060 child->view_unmap.notify = view_child_handle_view_unmap; 1108 child->view_unmap.notify = view_child_handle_view_unmap;
1061 1109
1062 struct sway_workspace *workspace = child->view->container->workspace; 1110 struct sway_workspace *workspace = child->view->container->pending.workspace;
1063 if (workspace) { 1111 if (workspace) {
1064 wlr_surface_send_enter(child->surface, workspace->output->wlr_output); 1112 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1065 } 1113 }
1066 1114
1067 view_init_subsurfaces(child->view, surface); 1115 view_child_init_subsurfaces(child, surface);
1068} 1116}
1069 1117
1070void view_child_destroy(struct sway_view_child *child) { 1118void view_child_destroy(struct sway_view_child *child) {
1071 if (child->mapped && child->view->container != NULL) { 1119 if (view_child_is_mapped(child) && child->view->container != NULL) {
1072 view_child_damage(child, true); 1120 view_child_damage(child, true);
1073 } 1121 }
1074 1122
@@ -1081,6 +1129,9 @@ void view_child_destroy(struct sway_view_child *child) {
1081 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { 1129 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1082 wl_list_remove(&subchild->link); 1130 wl_list_remove(&subchild->link);
1083 subchild->parent = NULL; 1131 subchild->parent = NULL;
1132 // The subchild lost its parent link, so it cannot see that the parent
1133 // is unmapped. Unmap it directly.
1134 subchild->mapped = false;
1084 } 1135 }
1085 1136
1086 wl_list_remove(&child->surface_commit.link); 1137 wl_list_remove(&child->surface_commit.link);
@@ -1101,18 +1152,27 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1101 if (wlr_surface_is_xdg_surface(wlr_surface)) { 1152 if (wlr_surface_is_xdg_surface(wlr_surface)) {
1102 struct wlr_xdg_surface *xdg_surface = 1153 struct wlr_xdg_surface *xdg_surface =
1103 wlr_xdg_surface_from_wlr_surface(wlr_surface); 1154 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1155 if (xdg_surface == NULL) {
1156 return NULL;
1157 }
1104 return view_from_wlr_xdg_surface(xdg_surface); 1158 return view_from_wlr_xdg_surface(xdg_surface);
1105 } 1159 }
1106#if HAVE_XWAYLAND 1160#if HAVE_XWAYLAND
1107 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 1161 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
1108 struct wlr_xwayland_surface *xsurface = 1162 struct wlr_xwayland_surface *xsurface =
1109 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 1163 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1164 if (xsurface == NULL) {
1165 return NULL;
1166 }
1110 return view_from_wlr_xwayland_surface(xsurface); 1167 return view_from_wlr_xwayland_surface(xsurface);
1111 } 1168 }
1112#endif 1169#endif
1113 if (wlr_surface_is_subsurface(wlr_surface)) { 1170 if (wlr_surface_is_subsurface(wlr_surface)) {
1114 struct wlr_subsurface *subsurface = 1171 struct wlr_subsurface *subsurface =
1115 wlr_subsurface_from_wlr_surface(wlr_surface); 1172 wlr_subsurface_from_wlr_surface(wlr_surface);
1173 if (subsurface == NULL) {
1174 return NULL;
1175 }
1116 return view_from_wlr_surface(subsurface->parent); 1176 return view_from_wlr_surface(subsurface->parent);
1117 } 1177 }
1118 if (wlr_surface_is_layer_surface(wlr_surface)) { 1178 if (wlr_surface_is_layer_surface(wlr_surface)) {
@@ -1242,15 +1302,15 @@ bool view_is_visible(struct sway_view *view) {
1242 if (view->container->node.destroying) { 1302 if (view->container->node.destroying) {
1243 return false; 1303 return false;
1244 } 1304 }
1245 struct sway_workspace *workspace = view->container->workspace; 1305 struct sway_workspace *workspace = view->container->pending.workspace;
1246 if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { 1306 if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
1247 bool fs_global_descendant = false; 1307 bool fs_global_descendant = false;
1248 struct sway_container *parent = view->container->parent; 1308 struct sway_container *parent = view->container->pending.parent;
1249 while (parent) { 1309 while (parent) {
1250 if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { 1310 if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1251 fs_global_descendant = true; 1311 fs_global_descendant = true;
1252 } 1312 }
1253 parent = parent->parent; 1313 parent = parent->pending.parent;
1254 } 1314 }
1255 if (!fs_global_descendant) { 1315 if (!fs_global_descendant) {
1256 return false; 1316 return false;
@@ -1268,13 +1328,13 @@ bool view_is_visible(struct sway_view *view) {
1268 enum sway_container_layout layout = container_parent_layout(con); 1328 enum sway_container_layout layout = container_parent_layout(con);
1269 if ((layout == L_TABBED || layout == L_STACKED) 1329 if ((layout == L_TABBED || layout == L_STACKED)
1270 && !container_is_floating(con)) { 1330 && !container_is_floating(con)) {
1271 struct sway_node *parent = con->parent ? 1331 struct sway_node *parent = con->pending.parent ?
1272 &con->parent->node : &con->workspace->node; 1332 &con->pending.parent->node : &con->pending.workspace->node;
1273 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1333 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
1274 return false; 1334 return false;
1275 } 1335 }
1276 } 1336 }
1277 con = con->parent; 1337 con = con->pending.parent;
1278 } 1338 }
1279 // Check view isn't hidden by another fullscreen view 1339 // Check view isn't hidden by another fullscreen view
1280 struct sway_container *fs = root->fullscreen_global ? 1340 struct sway_container *fs = root->fullscreen_global ?
@@ -1308,7 +1368,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1308 ipc_event_window(view->container, "urgent"); 1368 ipc_event_window(view->container, "urgent");
1309 1369
1310 if (!container_is_scratchpad_hidden(view->container)) { 1370 if (!container_is_scratchpad_hidden(view->container)) {
1311 workspace_detect_urgent(view->container->workspace); 1371 workspace_detect_urgent(view->container->pending.workspace);
1312 } 1372 }
1313} 1373}
1314 1374
@@ -1338,8 +1398,8 @@ static void view_save_buffer_iterator(struct wlr_surface *surface,
1338 saved_buffer->buffer = surface->buffer; 1398 saved_buffer->buffer = surface->buffer;
1339 saved_buffer->width = surface->current.width; 1399 saved_buffer->width = surface->current.width;
1340 saved_buffer->height = surface->current.height; 1400 saved_buffer->height = surface->current.height;
1341 saved_buffer->x = sx; 1401 saved_buffer->x = view->container->surface_x + sx;
1342 saved_buffer->y = sy; 1402 saved_buffer->y = view->container->surface_y + sy;
1343 saved_buffer->transform = surface->current.transform; 1403 saved_buffer->transform = surface->current.transform;
1344 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); 1404 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1345 wl_list_insert(&view->saved_buffers, &saved_buffer->link); 1405 wl_list_insert(&view->saved_buffers, &saved_buffer->link);
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 921b7d19..7e98dc92 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;
@@ -561,25 +561,30 @@ struct sway_workspace *workspace_output_prev(
561 return workspace_output_prev_next_impl(current->output, -1, create); 561 return workspace_output_prev_next_impl(current->output, -1, create);
562} 562}
563 563
564bool workspace_switch(struct sway_workspace *workspace, 564struct sway_workspace *workspace_auto_back_and_forth(
565 bool no_auto_back_and_forth) { 565 struct sway_workspace *workspace) {
566 struct sway_seat *seat = input_manager_current_seat(); 566 struct sway_seat *seat = input_manager_current_seat();
567 struct sway_workspace *active_ws = NULL; 567 struct sway_workspace *active_ws = NULL;
568 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); 568 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
569 if (focus && focus->type == N_WORKSPACE) { 569 if (focus && focus->type == N_WORKSPACE) {
570 active_ws = focus->sway_workspace; 570 active_ws = focus->sway_workspace;
571 } else if (focus && focus->type == N_CONTAINER) { 571 } else if (focus && focus->type == N_CONTAINER) {
572 active_ws = focus->sway_container->workspace; 572 active_ws = focus->sway_container->pending.workspace;
573 } 573 }
574 574
575 if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws 575 if (config->auto_back_and_forth && active_ws && active_ws == workspace &&
576 && active_ws == workspace && seat->prev_workspace_name) { 576 seat->prev_workspace_name) {
577 struct sway_workspace *new_ws = 577 struct sway_workspace *new_ws =
578 workspace_by_name(seat->prev_workspace_name); 578 workspace_by_name(seat->prev_workspace_name);
579 workspace = new_ws ? 579 workspace = new_ws ?
580 new_ws : 580 new_ws :
581 workspace_create(NULL, seat->prev_workspace_name); 581 workspace_create(NULL, seat->prev_workspace_name);
582 } 582 }
583 return workspace;
584}
585
586bool workspace_switch(struct sway_workspace *workspace) {
587 struct sway_seat *seat = input_manager_current_seat();
583 588
584 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", 589 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
585 workspace, workspace->name); 590 workspace, workspace->name);
@@ -736,13 +741,13 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws,
736} 741}
737 742
738static void set_workspace(struct sway_container *container, void *data) { 743static void set_workspace(struct sway_container *container, void *data) {
739 container->workspace = container->parent->workspace; 744 container->pending.workspace = container->pending.parent->pending.workspace;
740} 745}
741 746
742static void workspace_attach_tiling(struct sway_workspace *ws, 747static void workspace_attach_tiling(struct sway_workspace *ws,
743 struct sway_container *con) { 748 struct sway_container *con) {
744 list_add(ws->tiling, con); 749 list_add(ws->tiling, con);
745 con->workspace = ws; 750 con->pending.workspace = ws;
746 container_for_each_child(con, set_workspace, NULL); 751 container_for_each_child(con, set_workspace, NULL);
747 container_handle_fullscreen_reparent(con); 752 container_handle_fullscreen_reparent(con);
748 workspace_update_representation(ws); 753 workspace_update_representation(ws);
@@ -753,7 +758,7 @@ static void workspace_attach_tiling(struct sway_workspace *ws,
753struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { 758struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
754 struct sway_container *fs = ws->fullscreen; 759 struct sway_container *fs = ws->fullscreen;
755 struct sway_container *middle = container_create(NULL); 760 struct sway_container *middle = container_create(NULL);
756 middle->layout = ws->layout; 761 middle->pending.layout = ws->layout;
757 while (ws->tiling->length) { 762 while (ws->tiling->length) {
758 struct sway_container *child = ws->tiling->items[0]; 763 struct sway_container *child = ws->tiling->items[0];
759 container_detach(child); 764 container_detach(child);
@@ -771,9 +776,9 @@ void workspace_unwrap_children(struct sway_workspace *ws,
771 return; 776 return;
772 } 777 }
773 778
774 ws->layout = wrap->layout; 779 ws->layout = wrap->pending.layout;
775 while (wrap->children->length) { 780 while (wrap->pending.children->length) {
776 struct sway_container *child = wrap->children->items[0]; 781 struct sway_container *child = wrap->pending.children->items[0];
777 container_detach(child); 782 container_detach(child);
778 workspace_add_tiling(ws, child); 783 workspace_add_tiling(ws, child);
779 } 784 }
@@ -793,14 +798,18 @@ void workspace_detach(struct sway_workspace *workspace) {
793 798
794struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, 799struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
795 struct sway_container *con) { 800 struct sway_container *con) {
796 if (con->workspace) { 801 if (con->pending.workspace) {
802 struct sway_container *old_parent = con->pending.parent;
797 container_detach(con); 803 container_detach(con);
804 if (old_parent) {
805 container_reap_empty(old_parent);
806 }
798 } 807 }
799 if (config->default_layout != L_NONE) { 808 if (config->default_layout != L_NONE) {
800 con = container_split(con, config->default_layout); 809 con = container_split(con, config->default_layout);
801 } 810 }
802 list_add(workspace->tiling, con); 811 list_add(workspace->tiling, con);
803 con->workspace = workspace; 812 con->pending.workspace = workspace;
804 container_for_each_child(con, set_workspace, NULL); 813 container_for_each_child(con, set_workspace, NULL);
805 container_handle_fullscreen_reparent(con); 814 container_handle_fullscreen_reparent(con);
806 workspace_update_representation(workspace); 815 workspace_update_representation(workspace);
@@ -811,11 +820,11 @@ struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
811 820
812void workspace_add_floating(struct sway_workspace *workspace, 821void workspace_add_floating(struct sway_workspace *workspace,
813 struct sway_container *con) { 822 struct sway_container *con) {
814 if (con->workspace) { 823 if (con->pending.workspace) {
815 container_detach(con); 824 container_detach(con);
816 } 825 }
817 list_add(workspace->floating, con); 826 list_add(workspace->floating, con);
818 con->workspace = workspace; 827 con->pending.workspace = workspace;
819 container_for_each_child(con, set_workspace, NULL); 828 container_for_each_child(con, set_workspace, NULL);
820 container_handle_fullscreen_reparent(con); 829 container_handle_fullscreen_reparent(con);
821 node_set_dirty(&workspace->node); 830 node_set_dirty(&workspace->node);
@@ -825,7 +834,7 @@ void workspace_add_floating(struct sway_workspace *workspace,
825void workspace_insert_tiling_direct(struct sway_workspace *workspace, 834void workspace_insert_tiling_direct(struct sway_workspace *workspace,
826 struct sway_container *con, int index) { 835 struct sway_container *con, int index) {
827 list_insert(workspace->tiling, index, con); 836 list_insert(workspace->tiling, index, con);
828 con->workspace = workspace; 837 con->pending.workspace = workspace;
829 container_for_each_child(con, set_workspace, NULL); 838 container_for_each_child(con, set_workspace, NULL);
830 container_handle_fullscreen_reparent(con); 839 container_handle_fullscreen_reparent(con);
831 workspace_update_representation(workspace); 840 workspace_update_representation(workspace);
@@ -835,7 +844,7 @@ void workspace_insert_tiling_direct(struct sway_workspace *workspace,
835 844
836struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, 845struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
837 struct sway_container *con, int index) { 846 struct sway_container *con, int index) {
838 if (con->workspace) { 847 if (con->pending.workspace) {
839 container_detach(con); 848 container_detach(con);
840 } 849 }
841 if (config->default_layout != L_NONE) { 850 if (config->default_layout != L_NONE) {
@@ -905,7 +914,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace,
905 enum sway_container_layout old_layout = workspace->layout; 914 enum sway_container_layout old_layout = workspace->layout;
906 struct sway_container *middle = workspace_wrap_children(workspace); 915 struct sway_container *middle = workspace_wrap_children(workspace);
907 workspace->layout = layout; 916 workspace->layout = layout;
908 middle->layout = old_layout; 917 middle->pending.layout = old_layout;
909 918
910 struct sway_seat *seat; 919 struct sway_seat *seat;
911 wl_list_for_each(seat, &server.input->seats, link) { 920 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/swaybar/bar.c b/swaybar/bar.c
index 231c1ad7..74c1924f 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,
diff --git a/swaybar/input.c b/swaybar/input.c
index 4fe6dd93..6e13f177 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) {
@@ -339,7 +341,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
339 seat->axis[axis].discrete_steps += abs(discrete); 341 seat->axis[axis].discrete_steps += abs(discrete);
340} 342}
341 343
342static struct wl_pointer_listener pointer_listener = { 344static const struct wl_pointer_listener pointer_listener = {
343 .enter = wl_pointer_enter, 345 .enter = wl_pointer_enter,
344 .leave = wl_pointer_leave, 346 .leave = wl_pointer_leave,
345 .motion = wl_pointer_motion, 347 .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..fcc8be1d 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"
@@ -23,8 +23,31 @@ static const int WS_HORIZONTAL_PADDING = 5;
23static const double WS_VERTICAL_PADDING = 1.5; 23static const double WS_VERTICAL_PADDING = 1.5;
24static const double BORDER_WIDTH = 1; 24static const double BORDER_WIDTH = 1;
25 25
26static uint32_t render_status_line_error(cairo_t *cairo, 26struct render_context {
27 struct swaybar_output *output, double *x) { 27 cairo_t *cairo;
28 struct swaybar_output *output;
29 cairo_font_options_t *textaa_sharp;
30 cairo_font_options_t *textaa_safe;
31 uint32_t background_color;
32};
33
34static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {
35 uint32_t salpha = fontcolor & 0xFF;
36 uint32_t balpha = ctx->background_color & 0xFF;
37
38 // Subpixel antialiasing requires blend be done in cairo, not compositor
39 cairo_font_options_t *fo = salpha == balpha ?
40 ctx->textaa_sharp : ctx->textaa_safe;
41 cairo_set_font_options(ctx->cairo, fo);
42
43 // Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE'
44 cairo_operator_t op = salpha == 0xFF ?
45 CAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE;
46 cairo_set_operator(ctx->cairo, op);
47}
48
49static uint32_t render_status_line_error(struct render_context *ctx, double *x) {
50 struct swaybar_output *output = ctx->output;
28 const char *error = output->bar->status->text; 51 const char *error = output->bar->status->text;
29 if (!error) { 52 if (!error) {
30 return 0; 53 return 0;
@@ -32,6 +55,7 @@ static uint32_t render_status_line_error(cairo_t *cairo,
32 55
33 uint32_t height = output->height * output->scale; 56 uint32_t height = output->height * output->scale;
34 57
58 cairo_t *cairo = ctx->cairo;
35 cairo_set_source_u32(cairo, 0xFF0000FF); 59 cairo_set_source_u32(cairo, 0xFF0000FF);
36 60
37 int margin = 3 * output->scale; 61 int margin = 3 * output->scale;
@@ -53,21 +77,24 @@ 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));
80 choose_text_aa_mode(ctx, 0xFF0000FF);
56 pango_printf(cairo, font, output->scale, false, "%s", error); 81 pango_printf(cairo, font, output->scale, 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,
@@ -87,6 +114,7 @@ static uint32_t render_status_line_text(cairo_t *cairo,
87 uint32_t height = output->height * output->scale; 114 uint32_t height = output->height * output->scale;
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));
117 choose_text_aa_mode(ctx, fontcolor);
90 pango_printf(cairo, config->font, output->scale, 118 pango_printf(cairo, config->font, output->scale,
91 config->pango_markup, "%s", text); 119 config->pango_markup, "%s", text);
92 *x -= margin; 120 *x -= margin;
@@ -96,6 +124,7 @@ static uint32_t render_status_line_text(cairo_t *cairo,
96static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, 124static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
97 double x, double y, double width, double height) { 125 double x, double y, double width, double height) {
98 cairo_save(cairo); 126 cairo_save(cairo);
127 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
99 cairo_set_source_u32(cairo, color); 128 cairo_set_source_u32(cairo, color);
100 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 129 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
101 cairo_rectangle(cairo, x, y, width, height); 130 cairo_rectangle(cairo, x, y, width, height);
@@ -109,6 +138,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
109 render_sharp_rectangle(cairo, color, x, y, width, height); 138 render_sharp_rectangle(cairo, color, x, y, width, height);
110 } else { 139 } else {
111 cairo_save(cairo); 140 cairo_save(cairo);
141 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
112 cairo_set_source_u32(cairo, color); 142 cairo_set_source_u32(cairo, color);
113 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 143 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
114 if (width == 1) { 144 if (width == 1) {
@@ -146,9 +176,8 @@ static void i3bar_block_unref_callback(void *data) {
146 i3bar_block_unref(data); 176 i3bar_block_unref(data);
147} 177}
148 178
149static uint32_t render_status_block(cairo_t *cairo, 179static uint32_t render_status_block(struct render_context *ctx,
150 struct swaybar_output *output, struct i3bar_block *block, double *x, 180 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) { 181 if (!block->full_text || !*block->full_text) {
153 return 0; 182 return 0;
154 } 183 }
@@ -158,8 +187,9 @@ static uint32_t render_status_block(cairo_t *cairo,
158 text = block->short_text; 187 text = block->short_text;
159 } 188 }
160 189
190 cairo_t *cairo = ctx->cairo;
191 struct swaybar_output *output = ctx->output;
161 struct swaybar_config *config = output->bar->config; 192 struct swaybar_config *config = output->bar->config;
162
163 int text_width, text_height; 193 int text_width, text_height;
164 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 194 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
165 output->scale, block->markup, "%s", text); 195 output->scale, block->markup, "%s", text);
@@ -240,6 +270,7 @@ static uint32_t render_status_block(cairo_t *cairo,
240 if (bg_color) { 270 if (bg_color) {
241 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, 271 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
242 block_width, render_height); 272 block_width, render_height);
273 ctx->background_color = bg_color;
243 } 274 }
244 275
245 uint32_t border_color = block->urgent 276 uint32_t border_color = block->urgent
@@ -274,6 +305,7 @@ static uint32_t render_status_block(cairo_t *cairo,
274 color = block->color_set ? block->color : color; 305 color = block->color_set ? block->color : color;
275 color = block->urgent ? config->colors.urgent_workspace.text : color; 306 color = block->urgent ? config->colors.urgent_workspace.text : color;
276 cairo_set_source_u32(cairo, color); 307 cairo_set_source_u32(cairo, color);
308 choose_text_aa_mode(ctx, color);
277 pango_printf(cairo, config->font, output->scale, 309 pango_printf(cairo, config->font, output->scale,
278 block->markup, "%s", text); 310 block->markup, "%s", text);
279 x_pos += width; 311 x_pos += width;
@@ -287,17 +319,20 @@ static uint32_t render_status_block(cairo_t *cairo,
287 319
288 if (!edge && block->separator) { 320 if (!edge && block->separator) {
289 if (output->focused) { 321 if (output->focused) {
290 cairo_set_source_u32(cairo, config->colors.focused_separator); 322 color = config->colors.focused_separator;
291 } else { 323 } else {
292 cairo_set_source_u32(cairo, config->colors.separator); 324 color = config->colors.separator;
293 } 325 }
326 cairo_set_source_u32(cairo, color);
294 if (config->sep_symbol) { 327 if (config->sep_symbol) {
295 offset = x_pos + (sep_block_width - sep_width) / 2; 328 offset = x_pos + (sep_block_width - sep_width) / 2;
296 double sep_y = height / 2.0 - sep_height / 2.0; 329 double sep_y = height / 2.0 - sep_height / 2.0;
297 cairo_move_to(cairo, offset, (int)floor(sep_y)); 330 cairo_move_to(cairo, offset, (int)floor(sep_y));
331 choose_text_aa_mode(ctx, color);
298 pango_printf(cairo, config->font, output->scale, false, 332 pango_printf(cairo, config->font, output->scale, false,
299 "%s", config->sep_symbol); 333 "%s", config->sep_symbol);
300 } else { 334 } else {
335 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
301 cairo_set_line_width(cairo, 1); 336 cairo_set_line_width(cairo, 1);
302 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); 337 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
303 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); 338 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
@@ -459,13 +494,14 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
459 return width; 494 return width;
460} 495}
461 496
462static uint32_t render_status_line_i3bar(cairo_t *cairo, 497static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
463 struct swaybar_output *output, double *x) { 498 struct swaybar_output *output = ctx->output;
464 uint32_t max_height = 0; 499 uint32_t max_height = 0;
465 bool edge = *x == output->width * output->scale; 500 bool edge = *x == output->width * output->scale;
466 struct i3bar_block *block; 501 struct i3bar_block *block;
467 bool use_short_text = false; 502 bool use_short_text = false;
468 503
504 cairo_t *cairo = ctx->cairo;
469 double reserved_width = 505 double reserved_width =
470 predict_workspace_buttons_length(cairo, output) + 506 predict_workspace_buttons_length(cairo, output) +
471 predict_binding_mode_indicator_length(cairo, output) + 507 predict_binding_mode_indicator_length(cairo, output) +
@@ -479,7 +515,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
479 } 515 }
480 516
481 wl_list_for_each(block, &output->bar->status->blocks, link) { 517 wl_list_for_each(block, &output->bar->status->blocks, link) {
482 uint32_t h = render_status_block(cairo, output, block, x, edge, 518 uint32_t h = render_status_block(ctx, block, x, edge,
483 use_short_text); 519 use_short_text);
484 max_height = h > max_height ? h : max_height; 520 max_height = h > max_height ? h : max_height;
485 edge = false; 521 edge = false;
@@ -487,29 +523,30 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
487 return max_height; 523 return max_height;
488} 524}
489 525
490static uint32_t render_status_line(cairo_t *cairo, 526static uint32_t render_status_line(struct render_context *ctx, double *x) {
491 struct swaybar_output *output, double *x) { 527 struct status_line *status = ctx->output->bar->status;
492 struct status_line *status = output->bar->status;
493 switch (status->protocol) { 528 switch (status->protocol) {
494 case PROTOCOL_ERROR: 529 case PROTOCOL_ERROR:
495 return render_status_line_error(cairo, output, x); 530 return render_status_line_error(ctx, x);
496 case PROTOCOL_TEXT: 531 case PROTOCOL_TEXT:
497 return render_status_line_text(cairo, output, x); 532 return render_status_line_text(ctx, x);
498 case PROTOCOL_I3BAR: 533 case PROTOCOL_I3BAR:
499 return render_status_line_i3bar(cairo, output, x); 534 return render_status_line_i3bar(ctx, x);
500 case PROTOCOL_UNDEF: 535 case PROTOCOL_UNDEF:
501 return 0; 536 return 0;
502 } 537 }
503 return 0; 538 return 0;
504} 539}
505 540
506static uint32_t render_binding_mode_indicator(cairo_t *cairo, 541static uint32_t render_binding_mode_indicator(struct render_context *ctx,
507 struct swaybar_output *output, double x) { 542 double x) {
543 struct swaybar_output *output = ctx->output;
508 const char *mode = output->bar->mode; 544 const char *mode = output->bar->mode;
509 if (!mode) { 545 if (!mode) {
510 return 0; 546 return 0;
511 } 547 }
512 548
549 cairo_t *cairo = ctx->cairo;
513 struct swaybar_config *config = output->bar->config; 550 struct swaybar_config *config = output->bar->config;
514 int text_width, text_height; 551 int text_width, text_height;
515 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 552 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
@@ -533,7 +570,9 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
533 } 570 }
534 571
535 uint32_t height = output->height * output->scale; 572 uint32_t height = output->height * output->scale;
573 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
536 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 574 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
575 ctx->background_color = config->colors.binding_mode.background;
537 cairo_rectangle(cairo, x, 0, width, height); 576 cairo_rectangle(cairo, x, 0, width, height);
538 cairo_fill(cairo); 577 cairo_fill(cairo);
539 578
@@ -550,6 +589,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
550 double text_y = height / 2.0 - text_height / 2.0; 589 double text_y = height / 2.0 - text_height / 2.0;
551 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 590 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)); 591 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
592 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
553 pango_printf(cairo, config->font, output->scale, 593 pango_printf(cairo, config->font, output->scale,
554 output->bar->mode_pango_markup, "%s", mode); 594 output->bar->mode_pango_markup, "%s", mode);
555 return output->height; 595 return output->height;
@@ -565,9 +605,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(
565 return HOTSPOT_IGNORE; 605 return HOTSPOT_IGNORE;
566} 606}
567 607
568static uint32_t render_workspace_button(cairo_t *cairo, 608static uint32_t render_workspace_button(struct render_context *ctx,
569 struct swaybar_output *output,
570 struct swaybar_workspace *ws, double *x) { 609 struct swaybar_workspace *ws, double *x) {
610 struct swaybar_output *output = ctx->output;
571 struct swaybar_config *config = output->bar->config; 611 struct swaybar_config *config = output->bar->config;
572 struct box_colors box_colors; 612 struct box_colors box_colors;
573 if (ws->urgent) { 613 if (ws->urgent) {
@@ -582,6 +622,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
582 622
583 uint32_t height = output->height * output->scale; 623 uint32_t height = output->height * output->scale;
584 624
625 cairo_t *cairo = ctx->cairo;
585 int text_width, text_height; 626 int text_width, text_height;
586 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 627 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
587 output->scale, config->pango_markup, "%s", ws->label); 628 output->scale, config->pango_markup, "%s", ws->label);
@@ -603,7 +644,9 @@ static uint32_t render_workspace_button(cairo_t *cairo,
603 width = config->workspace_min_width * output->scale; 644 width = config->workspace_min_width * output->scale;
604 } 645 }
605 646
647 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
606 cairo_set_source_u32(cairo, box_colors.background); 648 cairo_set_source_u32(cairo, box_colors.background);
649 ctx->background_color = box_colors.background;
607 cairo_rectangle(cairo, *x, 0, width, height); 650 cairo_rectangle(cairo, *x, 0, width, height);
608 cairo_fill(cairo); 651 cairo_fill(cairo);
609 652
@@ -620,6 +663,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
620 double text_y = height / 2.0 - text_height / 2.0; 663 double text_y = height / 2.0 - text_height / 2.0;
621 cairo_set_source_u32(cairo, box_colors.text); 664 cairo_set_source_u32(cairo, box_colors.text);
622 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 665 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
666 choose_text_aa_mode(ctx, box_colors.text);
623 pango_printf(cairo, config->font, output->scale, config->pango_markup, 667 pango_printf(cairo, config->font, output->scale, config->pango_markup,
624 "%s", ws->label); 668 "%s", ws->label);
625 669
@@ -637,15 +681,19 @@ static uint32_t render_workspace_button(cairo_t *cairo,
637 return output->height; 681 return output->height;
638} 682}
639 683
640static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { 684static uint32_t render_to_cairo(struct render_context *ctx) {
685 cairo_t *cairo = ctx->cairo;
686 struct swaybar_output *output = ctx->output;
641 struct swaybar *bar = output->bar; 687 struct swaybar *bar = output->bar;
642 struct swaybar_config *config = bar->config; 688 struct swaybar_config *config = bar->config;
643 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 689 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
644 if (output->focused) { 690 if (output->focused) {
645 cairo_set_source_u32(cairo, config->colors.focused_background); 691 ctx->background_color = config->colors.focused_background;
646 } else { 692 } else {
647 cairo_set_source_u32(cairo, config->colors.background); 693 ctx->background_color = config->colors.background;
648 } 694 }
695
696 cairo_set_source_u32(cairo, ctx->background_color);
649 cairo_paint(cairo); 697 cairo_paint(cairo);
650 698
651 int th; 699 int th;
@@ -666,19 +714,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
666 } 714 }
667#endif 715#endif
668 if (bar->status) { 716 if (bar->status) {
669 uint32_t h = render_status_line(cairo, output, &x); 717 uint32_t h = render_status_line(ctx, &x);
670 max_height = h > max_height ? h : max_height; 718 max_height = h > max_height ? h : max_height;
671 } 719 }
672 x = 0; 720 x = 0;
673 if (config->workspace_buttons) { 721 if (config->workspace_buttons) {
674 struct swaybar_workspace *ws; 722 struct swaybar_workspace *ws;
675 wl_list_for_each(ws, &output->workspaces, link) { 723 wl_list_for_each(ws, &output->workspaces, link) {
676 uint32_t h = render_workspace_button(cairo, output, ws, &x); 724 uint32_t h = render_workspace_button(ctx, ws, &x);
677 max_height = h > max_height ? h : max_height; 725 max_height = h > max_height ? h : max_height;
678 } 726 }
679 } 727 }
680 if (config->binding_mode_indicator) { 728 if (config->binding_mode_indicator) {
681 uint32_t h = render_binding_mode_indicator(cairo, output, x); 729 uint32_t h = render_binding_mode_indicator(ctx, x);
682 max_height = h > max_height ? h : max_height; 730 max_height = h > max_height ? h : max_height;
683 } 731 }
684 732
@@ -708,26 +756,35 @@ void render_frame(struct swaybar_output *output) {
708 756
709 free_hotspots(&output->hotspots); 757 free_hotspots(&output->hotspots);
710 758
759 struct render_context ctx = { 0 };
760 ctx.output = output;
761
711 cairo_surface_t *recorder = cairo_recording_surface_create( 762 cairo_surface_t *recorder = cairo_recording_surface_create(
712 CAIRO_CONTENT_COLOR_ALPHA, NULL); 763 CAIRO_CONTENT_COLOR_ALPHA, NULL);
713 cairo_t *cairo = cairo_create(recorder); 764 cairo_t *cairo = cairo_create(recorder);
714 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 765 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
766 ctx.cairo = cairo;
767
715 cairo_font_options_t *fo = cairo_font_options_create(); 768 cairo_font_options_t *fo = cairo_font_options_create();
716 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); 769 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
770 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
771 ctx.textaa_safe = fo;
717 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 772 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
718 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 773 ctx.textaa_sharp = ctx.textaa_safe;
719 } else { 774 } else {
775 fo = cairo_font_options_create();
776 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
720 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 777 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
721 cairo_font_options_set_subpixel_order(fo, 778 cairo_font_options_set_subpixel_order(fo,
722 to_cairo_subpixel_order(output->subpixel)); 779 to_cairo_subpixel_order(output->subpixel));
780 ctx.textaa_sharp = fo;
723 } 781 }
724 cairo_set_font_options(cairo, fo); 782
725 cairo_font_options_destroy(fo);
726 cairo_save(cairo); 783 cairo_save(cairo);
727 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 784 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
728 cairo_paint(cairo); 785 cairo_paint(cairo);
729 cairo_restore(cairo); 786 cairo_restore(cairo);
730 uint32_t height = render_to_cairo(cairo, output); 787 uint32_t height = render_to_cairo(&ctx);
731 int config_height = output->bar->config->height; 788 int config_height = output->bar->config->height;
732 if (config_height > 0) { 789 if (config_height > 0) {
733 height = config_height; 790 height = config_height;
@@ -779,6 +836,11 @@ void render_frame(struct swaybar_output *output) {
779 836
780 wl_surface_commit(output->surface); 837 wl_surface_commit(output->surface);
781 } 838 }
839
840 if (ctx.textaa_sharp != ctx.textaa_safe) {
841 cairo_font_options_destroy(ctx.textaa_sharp);
842 }
843 cairo_font_options_destroy(ctx.textaa_safe);
782 cairo_surface_destroy(recorder); 844 cairo_surface_destroy(recorder);
783 cairo_destroy(cairo); 845 cairo_destroy(cairo);
784} 846}
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..2a7f869a 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"
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..dd17c0b0 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}
@@ -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;