aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/alpine.yml15
-rw-r--r--.builds/archlinux.yml6
-rw-r--r--.builds/freebsd.yml5
-rw-r--r--.clang-format16
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md6
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml4
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--README.de.md6
-rw-r--r--README.dk.md65
-rw-r--r--README.es.md8
-rw-r--r--README.fr.md64
-rw-r--r--README.gr.md73
-rw-r--r--README.hu.md77
-rw-r--r--README.ir.md60
-rw-r--r--README.ja.md6
-rw-r--r--README.ko.md6
-rw-r--r--README.md25
-rw-r--r--README.nl.md8
-rw-r--r--README.pl.md8
-rw-r--r--README.pt.md8
-rw-r--r--README.ro.md6
-rw-r--r--README.ru.md41
-rw-r--r--README.tr.md68
-rw-r--r--README.uk.md8
-rw-r--r--README.zh-CN.md8
-rw-r--r--README.zh-TW.md8
-rw-r--r--client/pool-buffer.c2
-rw-r--r--common/background-image.c2
-rw-r--r--common/cairo.c4
-rw-r--r--common/pango.c22
-rw-r--r--common/util.c12
-rw-r--r--config.in6
-rwxr-xr-xcontrib/_incr_version21
-rwxr-xr-xcontrib/autoname-workspaces.py28
-rwxr-xr-xcontrib/grimshot8
-rw-r--r--contrib/grimshot.13
-rw-r--r--contrib/grimshot.1.scd1
-rwxr-xr-xcontrib/inactive-windows-transparency.py7
-rw-r--r--include/background-image.h2
-rw-r--r--include/cairo_util.h (renamed from include/cairo.h)6
-rw-r--r--include/ipc-client.h3
-rw-r--r--include/pango.h5
-rw-r--r--include/pool-buffer.h2
-rw-r--r--include/sway/commands.h9
-rw-r--r--include/sway/config.h53
-rw-r--r--include/sway/desktop.h2
-rw-r--r--include/sway/desktop/idle_inhibit_v1.h1
-rw-r--r--include/sway/desktop/transaction.h12
-rw-r--r--include/sway/input/cursor.h4
-rw-r--r--include/sway/input/libinput.h2
-rw-r--r--include/sway/input/seat.h5
-rw-r--r--include/sway/input/text_input.h5
-rw-r--r--include/sway/layers.h8
-rw-r--r--include/sway/output.h7
-rw-r--r--include/sway/server.h36
-rw-r--r--include/sway/surface.h2
-rw-r--r--include/sway/tree/container.h64
-rw-r--r--include/sway/tree/root.h2
-rw-r--r--include/sway/tree/view.h18
-rw-r--r--include/sway/tree/workspace.h12
-rw-r--r--include/swaybar/i3bar.h1
-rw-r--r--include/swaynag/swaynag.h3
-rw-r--r--include/swaynag/types.h1
-rw-r--r--include/util.h6
-rw-r--r--meson.build157
-rw-r--r--meson_options.txt2
-rw-r--r--protocols/meson.build5
-rw-r--r--sway/commands.c88
-rw-r--r--sway/commands/bar.c5
-rw-r--r--sway/commands/bar/colors.c2
-rw-r--r--sway/commands/bar/font.c15
-rw-r--r--sway/commands/bar/hidden_state.c2
-rw-r--r--sway/commands/bar/mode.c2
-rw-r--r--sway/commands/bind.c7
-rw-r--r--sway/commands/border.c8
-rw-r--r--sway/commands/client.c16
-rw-r--r--sway/commands/exec_always.c23
-rw-r--r--sway/commands/floating.c8
-rw-r--r--sway/commands/focus.c43
-rw-r--r--sway/commands/font.c2
-rw-r--r--sway/commands/fullscreen.c34
-rw-r--r--sway/commands/gaps.c14
-rw-r--r--sway/commands/input.c4
-rw-r--r--sway/commands/input/map_to_region.c1
-rw-r--r--sway/commands/layout.c12
-rw-r--r--sway/commands/mode.c2
-rw-r--r--sway/commands/move.c152
-rw-r--r--sway/commands/output.c10
-rw-r--r--sway/commands/output/dpms.c25
-rw-r--r--sway/commands/output/mode.c58
-rw-r--r--sway/commands/output/render_bit_depth.c29
-rw-r--r--sway/commands/reload.c1
-rw-r--r--sway/commands/resize.c112
-rw-r--r--sway/commands/scratchpad.c21
-rw-r--r--sway/commands/seat.c4
-rw-r--r--sway/commands/seat/attach.c2
-rw-r--r--sway/commands/smart_gaps.c7
-rw-r--r--sway/commands/split.c23
-rw-r--r--sway/commands/sticky.c6
-rw-r--r--sway/commands/swap.c56
-rw-r--r--sway/commands/title_format.c1
-rw-r--r--sway/commands/unmark.c2
-rw-r--r--sway/commands/workspace.c23
-rw-r--r--sway/config.c107
-rw-r--r--sway/config/bar.c5
-rw-r--r--sway/config/output.c104
-rw-r--r--sway/criteria.c4
-rw-r--r--sway/desktop/idle_inhibit_v1.c16
-rw-r--r--sway/desktop/layer_shell.c131
-rw-r--r--sway/desktop/output.c237
-rw-r--r--sway/desktop/render.c176
-rw-r--r--sway/desktop/surface.c2
-rw-r--r--sway/desktop/transaction.c253
-rw-r--r--sway/desktop/xdg_shell.c65
-rw-r--r--sway/desktop/xwayland.c66
-rw-r--r--sway/input/cursor.c112
-rw-r--r--sway/input/keyboard.c77
-rw-r--r--sway/input/libinput.c30
-rw-r--r--sway/input/seat.c109
-rw-r--r--sway/input/seatop_default.c46
-rw-r--r--sway/input/seatop_down.c44
-rw-r--r--sway/input/seatop_move_floating.c10
-rw-r--r--sway/input/seatop_move_tiling.c170
-rw-r--r--sway/input/seatop_resize_floating.c54
-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.c146
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/main.c84
-rw-r--r--sway/meson.build4
-rw-r--r--sway/server.c92
-rw-r--r--sway/sway-bar.5.scd2
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway-ipc.7.scd5
-rw-r--r--sway/sway-output.5.scd29
-rw-r--r--sway/sway.5.scd35
-rw-r--r--sway/swaynag.c6
-rw-r--r--sway/tree/arrange.c70
-rw-r--r--sway/tree/container.c748
-rw-r--r--sway/tree/node.c18
-rw-r--r--sway/tree/output.c29
-rw-r--r--sway/tree/root.c41
-rw-r--r--sway/tree/view.c405
-rw-r--r--sway/tree/workspace.c123
-rw-r--r--sway/xdg_activation_v1.c20
-rw-r--r--sway/xdg_decoration.c28
-rw-r--r--swaybar/bar.c24
-rw-r--r--swaybar/i3bar.c31
-rw-r--r--swaybar/input.c12
-rw-r--r--swaybar/ipc.c20
-rw-r--r--swaybar/main.c2
-rw-r--r--swaybar/render.c358
-rw-r--r--swaybar/status_line.c13
-rw-r--r--swaybar/tray/item.c37
-rw-r--r--swaybar/tray/tray.c4
-rw-r--r--swaymsg/main.c115
-rw-r--r--swaymsg/swaymsg.1.scd20
-rw-r--r--swaynag/config.c25
-rw-r--r--swaynag/render.c60
-rw-r--r--swaynag/swaynag.1.scd3
-rw-r--r--swaynag/swaynag.5.scd5
-rw-r--r--swaynag/swaynag.c84
-rw-r--r--swaynag/types.c6
164 files changed, 4092 insertions, 2505 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index dc5e7c11..abf636ab 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,10 +17,11 @@ 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://gitlab.freedesktop.org/wlroots/wlroots.git
23tasks: 25tasks:
24 - wlroots: | 26 - wlroots: |
25 cd wlroots 27 cd wlroots
@@ -28,7 +30,7 @@ tasks:
28 sudo ninja -C build install 30 sudo ninja -C build install
29 - setup: | 31 - setup: |
30 cd sway 32 cd sway
31 meson build -Dauto_features=enabled -Dtray=disabled 33 meson build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled
32 - build: | 34 - build: |
33 cd sway 35 cd sway
34 ninja -C build 36 ninja -C build
@@ -36,3 +38,10 @@ tasks:
36 cd sway 38 cd sway
37 meson configure build -Dxwayland=disabled 39 meson configure build -Dxwayland=disabled
38 ninja -C build 40 ninja -C build
41 - build-static: |
42 cd sway
43 mkdir subprojects
44 ln -s ../../wlroots subprojects/wlroots
45 rm -rf build
46 meson build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots
47 ninja -C build
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index c0f70186..ac4cdb4d 100644
--- a/.builds/archlinux.yml
+++ b/.builds/archlinux.yml
@@ -13,10 +13,12 @@ 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://gitlab.freedesktop.org/wlroots/wlroots.git
20tasks: 22tasks:
21 - wlroots: | 23 - wlroots: |
22 cd wlroots 24 cd wlroots
@@ -25,7 +27,7 @@ tasks:
25 sudo ninja -C build install 27 sudo ninja -C build install
26 - setup: | 28 - setup: |
27 cd sway 29 cd sway
28 meson build -Dauto_features=enabled -Dsd-bus-provider=libsystemd 30 meson build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd
29 - build: | 31 - build: |
30 cd sway 32 cd sway
31 ninja -C build 33 ninja -C build
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index 273badbc..97e7eccc 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -19,13 +19,14 @@ 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
25- x11/xcb-util-wm 26- x11/xcb-util-wm
26sources: 27sources:
27- https://github.com/swaywm/sway 28- https://github.com/swaywm/sway
28- https://github.com/swaywm/wlroots 29- https://gitlab.freedesktop.org/wlroots/wlroots.git
29tasks: 30tasks:
30- setup: | 31- setup: |
31 cd sway 32 cd sway
@@ -33,7 +34,7 @@ tasks:
33 cd subprojects 34 cd subprojects
34 ln -s ../../wlroots wlroots 35 ln -s ../../wlroots wlroots
35 cd .. 36 cd ..
36 meson build -Dtray=enabled -Dsd-bus-provider=basu 37 meson build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu
37- build: | 38- build: |
38 cd sway 39 cd sway
39 ninja -C build 40 ninja -C build
diff --git a/.clang-format b/.clang-format
deleted file mode 100644
index 5818da3c..00000000
--- a/.clang-format
+++ /dev/null
@@ -1,16 +0,0 @@
1BasedOnStyle: LLVM
2IndentWidth: 4
3TabWidth: 4
4UseTab: Always
5BreakBeforeBraces: Attach
6AllowShortIfStatementsOnASingleLine: false
7IndentCaseLabels: false
8SortIncludes: false
9ColumnLimit: 80
10AlignAfterOpenBracket: DontAlign
11BinPackParameters: false
12BinPackArguments: false
13ContinuationIndentWidth: 8
14AllowAllParametersOfDeclarationOnNextLine: false
15AllowShortLoopsOnASingleLine: true
16ReflowComments: false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 156accde..eba606e6 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -6,10 +6,9 @@ labels: 'bug'
6--- 6---
7 7
8### Please read the following before submitting: 8### Please read the following before submitting:
9- Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on irc.freenode.net. 9- Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on Libera Chat.
10- Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway. 10- Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway.
11- Problems with the Wayland version of Firefox are likely to be Firefox bugs. Start by submitting your issue to the Firefox Bugzilla and come back here only after they confirm otherwise. 11- Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or obsolete workarounds.
12- Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or osbolete workarounds.
13 If you fix a script or find outdated information, don't hesitate to adjust the wiki page. 12 If you fix a script or find outdated information, don't hesitate to adjust the wiki page.
14 13
15### Please fill out the following: 14### Please fill out the following:
@@ -19,6 +18,7 @@ labels: 'bug'
19- **Debug Log:** 18- **Debug Log:**
20 - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. 19 - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com.
21 - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. 20 - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway.
21 - Attach the **full** file, do not truncate it.
22 22
23- **Configuration File:** 23- **Configuration File:**
24 - Please try to produce with the default configuration. 24 - Please try to produce with the default configuration.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index f09cdf5b..0092609b 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
1blank_issues_enabled: false 1blank_issues_enabled: false
2contact_links: 2contact_links:
3 - name: Questions 3 - name: Questions
4 url: "http://webchat.freenode.net/?channels=sway&uio=d4" 4 url: "https://libera.chat"
5 about: "Please ask questions on IRC in #sway on irc.freenode.net" 5 about: "Please ask questions on IRC in #sway on Libera Chat"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c17afdda..4f043f7a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,9 +1,8 @@
1# Contributing to sway 1# Contributing to sway
2 2
3Contributing just involves sending a pull request. You will probably be more 3Contributing just involves sending a pull request. You will probably be more
4successful with your contribution if you visit 4successful with your contribution if you visit #sway-devel on Libera Chat
5[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on 5upfront and discuss your plans.
6irc.freenode.net upfront and discuss your plans.
7 6
8Note: rules are made to be broken. Adjust or ignore any/all of these as you see 7Note: rules are made to be broken. Adjust or ignore any/all of these as you see
9fit, but be prepared to justify it to your peers. 8fit, but be prepared to justify it to your peers.
diff --git a/README.de.md b/README.de.md
index 24e66a60..01b5e9ad 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,8 +1,8 @@
1# Sway 1# Sway
2Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway on irc.freenode.net; Englisch). 2Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](https://web.libera.chat/gamja/?channels=#sway) bei (#sway on irc.libera.chat; Englisch).
3 3
4## Signaturen 4## Signaturen
5Jedes Release wird mit dem PGP-Schlüssel [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) signiert und auf GitHub veröffentlicht. 5Jedes Release wird mit dem PGP-Schlüssel [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) signiert und auf GitHub veröffentlicht.
6 6
7## Installation 7## Installation
8### Mit der Paketverwaltung 8### Mit der Paketverwaltung
@@ -15,7 +15,7 @@ Falls du sway für deine eigene Distribution als Paket bereitstellen möchtest,
15sway benötigt die folgenden Pakete: 15sway benötigt die folgenden Pakete:
16 16
17* meson\* 17* meson\*
18* [wlroots](https://github.com/swaywm/wlroots) 18* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
19* wayland 19* wayland
20* wayland-protocols\* 20* wayland-protocols\*
21* pcre 21* pcre
diff --git a/README.dk.md b/README.dk.md
index 535000c3..f712e96b 100644
--- a/README.dk.md
+++ b/README.dk.md
@@ -1,41 +1,43 @@
1# Sway 1# Sway
2 2
3Sway er en [i3](https://i3wm.org/)-kompatibel [Wayland](http://wayland.freedesktop.org/) compositor. 3Sway er en [i3]-kompatibel [Wayland] compositor. Læs [Ofte stillede spørgsmål].
4Læs [Ofte stillede spørgsmål](https://github.com/swaywm/sway/wiki). 4Deltag på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat).
5Deltag på [IRC kanalen](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway på irc.freenode.net).
6 5
7## Udgivelses Signaturer 6## Udgivelses Signaturer
8 7
9Udgivelser er signeret med [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 8Udgivelser er signeret med [E88F5E48] og publiceret [på GitHub][GitHub
10og publiseret på [GitHub](https://github.com/swaywm/sway/releases). 9releases].
11 10
12## Installation 11## Installation
13 12
14### Fra Pakker 13### Fra pakker
15 14
16Sway er tilgængelig i mange distributioner. Prøv at installere pakken "svay". Hvis den ikke er tilgængelig, så tjek [denne wiki-side](https://github.com/swaywm/sway/wiki/Unsupported-packages) 15Sway er tilgængelig i mange distributioner. Prøv at installere "sway" pakken
17for information om installation til din(e) distribution(er). 16fra din.
18 17
19Hvis du er interesseret i at lave en Sway pakke til din distribution, burde du besøge IRC 18Hvis du er interesseret i at pakke Sway til din distribution, kan du tage forbi
20kanalen eller sende en e-mail til sir@cmpwn.com for rådgivning. 19IRC kanalen eller sende en email til sir@cmpwn.com for rådgivning.
21 20
22### Kompilering fra kildekode 21### Kompilering fra kildekode
23 22
24Installation afhænger af følgende programmer: 23Se [denne wiki-side][Opsætning til udvikling] hvis du vil bygge HEAD af sway og
24wlroots til test eller udvikling.
25
26Installationsafhængigheder:
25 27
26* meson \* 28* meson \*
27* [wlroots](https://github.com/swaywm/wlroots) 29* [wlroots]
28* wayland 30* wayland
29* wayland-protocols \* 31* wayland-protocols \*
30* pcre 32* pcre
31* json-c 33* json-c
32* pango 34* pango
33* cairo 35* cairo
34* gdk-pixbuf2 (valgfrit tillæg: system tray) 36* gdk-pixbuf2 (valgfrit: system tray)
35* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (valgfrit tillæg: man pages) \* 37* [scdoc] (valgfrit: man pages) \*
36* git \* 38* git \*
37 39
38_\*Kompiler krav_ 40_\*Kompileringsafhængighed_
39 41
40Kør følgende kommandoer: 42Kør følgende kommandoer:
41 43
@@ -43,21 +45,30 @@ Kør følgende kommandoer:
43 ninja -C build 45 ninja -C build
44 sudo ninja -C build install 46 sudo ninja -C build install
45 47
46På systemer uden 'logind', behøver du at sætte ejerens bruger-id for Sways eksekverbare filer - såkaldt SUID (Set owner User ID): 48På systemer uden logind eller seatd skal du sætte SUID bit på sway filen:
47 49
48 sudo chmod a+s /usr/local/bin/sway 50 sudo chmod a+s /usr/local/bin/sway
49 51
50Sway vil frasige sig 'root' tilladelser kort efter opstart 52Sway dropper 'root' tilladelser kort efter opstart.
51 53
52## Konfiguration 54## Konfiguration
53 55
54Hvis du allerede bruger i3, bør du kopiere din i3-konfiguration til `~/.config/sway/config` og 56Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til
55det vil bare fungerer. Ellers skal du kopiere eksempel konfigurations filen til 57`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til
56`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. 58`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. Kør
57Kør `man 5 sway` for at få oplysninger om konfigurationen. 59`man 5 sway` for at få oplysninger om konfigurationen.
58 60
59## Kører 61## Eksekvering
60 62
61Kør `sway` fra en TTY. Nogle display managers fungerer muligvis, men understøttes ikke af 63Kør `sway` fra en TTY. Nogle display managers kan fungere, men Sway yder ikke
62Sway (gdm er kendt for at fungere temmelig godt). 64support til dem (gdm er kendt for at fungere temmelig godt).
63 65
66[i3]: https://i3wm.org/
67[Wayland]: http://wayland.freedesktop.org/
68[Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki
69[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway
70[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
71[GitHub releases]: https://github.com/swaywm/sway/releases
72[Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup
73[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
74[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.es.md b/README.es.md
index 53f11f68..7af7d90b 100644
--- a/README.es.md
+++ b/README.es.md
@@ -1,12 +1,12 @@
1# sway 1# sway
2 2
3sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). 3sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/).
4Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 4Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway on
5irc.freenode.net). 5irc.libera.chat).
6 6
7## Firmas de las versiones 7## Firmas de las versiones
8 8
9Las distintas versiones están firmadas con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 9Las distintas versiones están firmadas con [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
10y publicadas [en GitHub](https://github.com/swaywm/sway/releases). 10y publicadas [en GitHub](https://github.com/swaywm/sway/releases).
11 11
12## Instalación 12## Instalación
@@ -25,7 +25,7 @@ escriba un email a sir@cmpwn.com
25Instale las dependencias: 25Instale las dependencias:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre
diff --git a/README.fr.md b/README.fr.md
index a72696d6..359a30f9 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -1,48 +1,49 @@
1# sway 1# sway
2 2
3Sway est un compositeur [Wayland](http://wayland.freedesktop.org/) compatible 3Sway est un compositeur [Wayland] compatible avec [i3]. Lisez la
4avec [i3](https://i3wm.org/), **en cours de développement**. Lisez la 4[FAQ]. Rejoignez le [canal IRC] (#sway sur irc.libera.chat).
5[FAQ](https://github.com/swaywm/sway/wiki). Rejoignez le [canal
6IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur
7irc.freenode.net).
8 5
9## Aide en français 6## Aide en français
10 7
11[abdelq](//github.com/abdelq) fournit du support en français sur IRC et Github, dans le fuseau horaire UTC-4 (EST). 8[abdelq] fournit du support en français sur IRC et Github, dans le fuseau
9horaire UTC-4 (EST).
12 10
13## Signatures de nouvelles versions 11## Signatures de nouvelles versions
14 12
15Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 13Les nouvelles versions sont signées avec [E88F5E48] et publiées
16et publiées [sur GitHub](https://github.com/swaywm/sway/releases). 14[sur GitHub][versions GitHub].
17 15
18## Installation 16## Installation
19 17
20### À partir de paquets 18### À partir de paquets
21 19
22Sway est disponible sur plusieurs distributions. Essayez d'installer le paquet "sway" pour 20Sway est disponible sur beaucoup de distributions. Essayez d'installer le
23la vôtre. Si ce n'est pas disponible, consultez [cette page wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) 21paquet "sway" pour la vôtre.
24pour de l'information sur l'installation pour vos distributions.
25 22
26Si vous êtes intéressé à maintenir Sway pour votre distribution, passez par le canal 23Si vous êtes intéressé à maintenir Sway pour votre distribution, passez sur le
27IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des conseils. 24canal IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des
25conseils.
28 26
29### Compilation depuis la source 27### Compilation depuis les sources
28
29Consultez [cette page wiki][Configuration de développement] si vous souhaitez
30compiler la révision HEAD de sway et wlroots pour tester ou développer.
30 31
31Installez les dépendances : 32Installez les dépendances :
32 33
33* meson \* 34* meson \*
34* [wlroots](https://github.com/swaywm/wlroots) 35* [wlroots]
35* wayland 36* wayland
36* wayland-protocols \* 37* wayland-protocols \*
37* pcre 38* pcre
38* json-c 39* json-c
39* pango 40* pango
40* cairo 41* cairo
41* gdk-pixbuf2 (optionnel: system tray) 42* gdk-pixbuf2 (optionnel : system tray)
42* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optionnel: requis pour les pages man) \* 43* [scdoc] (optionnel : requis pour les pages man) \*
43* git \* 44* git (optionnel : information de version) \*
44 45
45_\*Requis uniquement pour la compilation_ 46_\* Requis uniquement pour la compilation_
46 47
47Exécutez ces commandes : 48Exécutez ces commandes :
48 49
@@ -58,12 +59,25 @@ Sway se débarassera des permissions *root* peu de temps après le démarrage.
58 59
59## Configuration 60## Configuration
60 61
61Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et 62Si vous utilisez déjà i3, copiez votre configuration i3 vers
62cela va fonctionner. Sinon, copiez l'exemple de fichier de configuration à 63`~/.config/sway/config` et sway fonctionnera directement. Sinon, copiez
63`~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. 64l'exemple de fichier de configuration vers `~/.config/sway/config`. Il se
64Exécutez `man 5 sway` pour l'information sur la configuration. 65trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour lire la
66documentation pour la configuration de sway.
65 67
66## Exécution 68## Exécution
67 69
68Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, 70Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent
69mais ne sont pas supportés par Sway (gdm est réputé pour assez bien fonctionner). 71fonctionner, mais ne sont pas supportés par Sway (gdm est réputé pour assez
72bien fonctionner).
73
74[Wayland]: http://wayland.freedesktop.org/
75[i3]: https://i3wm.org/
76[FAQ]: https://github.com/swaywm/sway/wiki
77[canal IRC]: https://web.libera.chat/gamja/?channels=#sway
78[abdelq]: https://github.com/abdelq
79[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
80[versions GitHub]: https://github.com/swaywm/sway/releases
81[Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup
82[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
83[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.gr.md b/README.gr.md
new file mode 100644
index 00000000..4c30e29d
--- /dev/null
+++ b/README.gr.md
@@ -0,0 +1,73 @@
1# Sway
2
3Το sway ένα [i3]-συμβατό [Wayland] compositor. Διαβάστε το [FAQ]. Μπείτε στο
4[IRC channel] \(#sway on irc.libera.chat).
5
6## ΥπογÏαφές δημοσιεÏσεων
7
8Οι εκδόσεις είναι υπογεÏαμμένες με [E88F5E48] και δημοσιευμένες [στο GitHub][GitHub releases].
9
10## Εγκατάσταση
11
12### Από πακέτα
13
14Το Sway είναι διαθέσιμο σε πολλά distributions. Δοκιμάστε εγκαταστώντας το "sway" package για
15το δικό σας.
16
17Εάν ενδιαφέÏεστε για packaging του sway για το distribution σας, να πάτε στο IRC
18channel ή στείλτε ένα email στο sir@cmpwn.com για συμβουλές.
19
20### Compiling από πηγή
21
22ΤσεκάÏετε [αυτό το wiki page][Development setup] εάμα θέλετε να κάνετε build το HEAD του
23sway και wlroots γιά τεστάÏισμα ή development.
24
25Εγκατάσταση των dependencies:
26
27* meson \*
28* [wlroots]
29* wayland
30* wayland-protocols \*
31* pcre
32* json-c
33* pango
34* cairo
35* gdk-pixbuf2 (Ï€ÏοαιÏετικό: system tray)
36* [scdoc] (Ï€ÏοαιÏετικό: man pages) \*
37* git (Ï€ÏοαιÏετικό: πληÏοφοÏίες εκδώσεων) \*
38
39_\*Compile-time dep_
40
41ΤÏέξτε αυτά τα commands:
42
43 meson build/
44 ninja -C build/
45 sudo ninja -C build/ install
46
47Σε συστήματα χωÏίς logind ή seatd, Ï€Ïέπει να κάνετε suid το sway binary:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Το Sway θα κάνει drop root δικαιώματα λίγο μετά την εκκίνηση.
52
53## Configuration
54
55Εάν ήδη χÏησιμοποιήτε το i3, αντιγÏάψτε το i3 config σας στο `~/.config/sway/config` και
56θα δουλέψει out of the box. Αλλιώς, αντιγÏάψτε το sample configuration αÏχείο στο
57`~/.config/sway/config`. Το οποίο συνήθως βÏίσκεται στο `/etc/sway/config`.
58Κάντε run `man 5 sway` για πληÏοφοÏίες Ï„Î¿Ï configuration.
59
60## ΤÏέχοντας
61
62ΤÏέξτε `sway` από ένα TTY. ΜεÏίκα display managers μποÏεί να δουλέψουν αλλά δέν είναι συμβατά με
63το sway (το gdm γνωÏίζεται να δουλέβει σχετικά καλά).
64
65[i3]: https://i3wm.org/
66[Wayland]: http://wayland.freedesktop.org/
67[FAQ]: https://github.com/swaywm/sway/wiki
68[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
69[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
70[GitHub releases]: https://github.com/swaywm/sway/releases
71[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
72[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
73[scdoc]: https://git.sr.ht/~sircmpwn/scdoc \ No newline at end of file
diff --git a/README.hu.md b/README.hu.md
new file mode 100644
index 00000000..4e006f25
--- /dev/null
+++ b/README.hu.md
@@ -0,0 +1,77 @@
1# sway
2
3A Sway egy [i3]-kompatibilis [Wayland] kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en).
4
5## Csomag aláírások
6
7A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHub releases] publikálva.
8
9## Telepítés
10
11### Csomagból
12
13A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway"
14csomagot telepíteni az általad használt eszközzel.
15
16Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC
17csatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért.
18
19### Fordítás forráskódból
20
21Olvasd el [ezt a wiki oldalt][Development setup], ha szeretnéd tesztelési vagy
22fejlesztési célokból lefordítani az aktuális (HEAD) állapotát a `sway`-nek és a
23`wlroots`-nak.
24
25Telepítsd a függőségeket:
26
27* meson \*
28* [wlroots]
29* wayland
30* wayland-protocols \*
31* pcre
32* json-c
33* pango
34* cairo
35* gdk-pixbuf2 (opcionális: system tray)
36* [scdoc] (opcionális: man pages) \*
37* git (opcionális: version info) \*
38
39_\*Fordításidejű függőség_
40
41Futtasd ezeket a parancsokat:
42
43 meson build
44 ninja -C build
45 sudo ninja -C build install
46
47Ha `logind` nélküli rendszert használsz, akkor be kell állítanod a `suid` bitet
48a futtaható állományon:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52A Sway indulás után nem sokkal el fogja engedni a root jogosultságait.
53
54## Konfiguráció
55
56Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a
57`~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold
58le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési
59útvonalon található.
60Futtasd a `man 5 sway` parancsot további információért a konfigurációval
61kapcsolatban.
62
63## Futtatás
64
65Futtasd a `sway` parancsot egy TTY felületről. Néhány bejelentkezéskezelő
66(display manager) működhet, de alapvetően nem támogatottak a sway által. (A
67gdm-ről ismeretes, hogy egész jól működik.)
68
69[i3]: https://i3wm.org/
70[Wayland]: http://wayland.freedesktop.org/
71[FAQ]: https://github.com/swaywm/sway/wiki
72[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
73[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
74[GitHub releases]: https://github.com/swaywm/sway/releases
75[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
76[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
77[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.ir.md b/README.ir.md
new file mode 100644
index 00000000..4542b93b
--- /dev/null
+++ b/README.ir.md
@@ -0,0 +1,60 @@
1# sway
2
3‏sway یک کامپوزیتور الهام گرÙته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال
4IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (‎#sway‏ در
5irc.libera.chat).
6
7برای حمایت از تیم توسعه sway به [صÙحه
8Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید.
9
10## امضای نسخه‌ها
11
12امضای نسخه‌ها با [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) در [GitHub](https://github.com/swaywm/sway/releases) منتشر می‌شود.
13
14## شیوه نصب
15
16### از بسته‌های رسمی
17
18‏sway در بسته‌های رسمی توزیع‌های مختل٠وجود دارد. بسته «sway» را نصب کنید. در صورتی Ú©Ù‡ بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صÙحه راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید.
19
20اگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استÙاده کنید یا به sir@cmpwn.com ایمیل بزنید.
21
22### کامپایل کردن کد
23
24چنانچه می‌خواهید آخرین نسخه کد sway Ùˆ wlroots را برای آزمایش یا توسعه بسازید به این [صÙحه راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید.
25
26بسته‌های مورد نیاز:
27
28* meson \*
29* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
30* wayland
31* wayland-protocols \*
32* pcre
33* json-c
34* pango
35* cairo
36* gdk-pixbuf2 (انتخابی: برای system tray)
37* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صÙحه‌های راهنما) \*
38* git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \*
39
40_\*نیازمندی‌های زمان کامپایل برنامه_
41
42این Ùرمان‌ها را اجرا کنید:
43
44 meson build
45 ninja -C build
46 sudo ninja -C build install
47
48روی سیستم‌های بدون logindØŒ باید Ùرمان زیر را برای suid کردن باینری sway اجرا کنید:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52‏sway پس از startup مجوزهای دسترسی root را رها می‌کند.
53
54### شخصی سازی و تنظیمات
55
56اگر در حال حاضر از i3 استÙاده می‌کنید، تنظیمات i3 خودتان را در Ùایل ‪`~/.config/sway/config`‬ Ú©Ù¾ÛŒ کنید Ùˆ بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، Ùایل نمونه تنظیمات را استÙاده کنید. این Ùایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید.
57
58## اجرا
59
60در محیط TTY کاÙیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طر٠sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد).
diff --git a/README.ja.md b/README.ja.md
index fa28f3da..786e169c 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -2,7 +2,7 @@
2 2
3Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ 3Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚
4[FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。 4[FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。
5[IRC ãƒãƒ£ãƒ³ãƒãƒ«](http://webchat.freenode.net/?channels=sway&uio=d4) (irc.freenode.netã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ 5[IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚
6 6
7[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 7[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
8 8
@@ -12,7 +12,7 @@ SirCmpwnã¯ã€æ—¥æœ¬èªžã§ã®ã‚µãƒãƒ¼ãƒˆã‚’IRCã¨GitHubã§è¡Œã„ã¾ã™ã€‚タイ
12 12
13## リリースã®ç½²å 13## リリースã®ç½²å
14 14
15Swayã®ãƒªãƒªãƒ¼ã‚¹ã¯[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)ã§ç½²åã•ã‚Œã€[GitHub](https://github.com/swaywm/sway/releases)ã§å…¬é–‹ã•ã‚Œã¦ã„ã¾ã™ã€‚ 15Swayã®ãƒªãƒªãƒ¼ã‚¹ã¯[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)ã§ç½²åã•ã‚Œã€[GitHub](https://github.com/swaywm/sway/releases)ã§å…¬é–‹ã•ã‚Œã¦ã„ã¾ã™ã€‚
16 16
17## インストール 17## インストール
18 18
@@ -27,7 +27,7 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚"
27次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: 27次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„:
28 28
29* meson \* 29* meson \*
30* [wlroots](https://github.com/swaywm/wlroots) 30* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
31* wayland 31* wayland
32* wayland-protocols \* 32* wayland-protocols \*
33* pcre 33* pcre
diff --git a/README.ko.md b/README.ko.md
index 9c3dd323..1086da0c 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -1,11 +1,11 @@
1# sway 1# sway
2 2
3sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다. 3sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다.
4[FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)ë„ ìžˆìŠµë‹ˆë‹¤. 4[FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤.
5 5
6## 릴리즈 서명 6## 릴리즈 서명
7 7
8릴리즈는 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)ì—ì„œ 서명ë˜ê³ , 8릴리즈는 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)ì—ì„œ 서명ë˜ê³ ,
9[GitHubì—ì„œ](https://github.com/swaywm/sway/releases) 공개ë˜ê³  있습니다. 9[GitHubì—ì„œ](https://github.com/swaywm/sway/releases) 공개ë˜ê³  있습니다.
10 10
11## 설치 11## 설치
@@ -24,7 +24,7 @@ IRC 채ë„ì„ ë°©ë¬¸í•˜ê±°ë‚˜ sir@cmpwn.com으로 ì´ë©”ì¼ì„ ë³´ë‚´ ìƒë‹´ ë°›
24ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: 24ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요:
25 25
26* meson \* 26* meson \*
27* [wlroots](https://github.com/swaywm/wlroots) 27* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
28* wayland 28* wayland
29* wayland-protocols \* 29* wayland-protocols \*
30* pcre 30* pcre
diff --git a/README.md b/README.md
index 4698afbe..8c252e9e 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
1# sway 1# sway
2 2
3**[English][en]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [中文-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [中文-ç¹é«”][zh-TW] - [Português][pt] - [Danish][dk] - [한국어][ko] - [Română][ro] 3**[English][en]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [中文-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [中文-ç¹é«”][zh-TW] - [Português][pt] - [Dansk][dk] - [한국어][ko] - [Română][ro] - [Magyar][hu] - [Türkçe][tr] - [Ùارسی][ir] - [Ελληνικά][gr]
4 4
5sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the 5sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the
6[IRC channel] \(#sway on irc.freenode.net). 6[IRC channel] \(#sway on irc.libera.chat).
7 7
8## Release Signatures 8## Release Signatures
9 9
@@ -16,9 +16,6 @@ Releases are signed with [E88F5E48] and published [on GitHub][GitHub releases].
16Sway is available in many distributions. Try installing the "sway" package for 16Sway is available in many distributions. Try installing the "sway" package for
17yours. 17yours.
18 18
19If you're interested in packaging sway for your distribution, stop by the IRC
20channel or shoot an email to sir@cmpwn.com for advice.
21
22### Compiling from Source 19### Compiling from Source
23 20
24Check out [this wiki page][Development setup] if you want to build the HEAD of 21Check out [this wiki page][Development setup] if you want to build the HEAD of
@@ -38,15 +35,15 @@ Install dependencies:
38* [scdoc] (optional: man pages) \* 35* [scdoc] (optional: man pages) \*
39* git (optional: version info) \* 36* git (optional: version info) \*
40 37
41_\*Compile-time dep_ 38_\* Compile-time dep_
42 39
43Run these commands: 40Run these commands:
44 41
45 meson build 42 meson build/
46 ninja -C build 43 ninja -C build/
47 sudo ninja -C build install 44 sudo ninja -C build/ install
48 45
49On systems without logind, you need to suid the sway binary: 46On systems without logind nor seatd, you need to suid the sway binary:
50 47
51 sudo chmod a+s /usr/local/bin/sway 48 sudo chmod a+s /usr/local/bin/sway
52 49
@@ -79,12 +76,16 @@ sway (gdm is known to work fairly well).
79[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md 76[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md
80[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md 77[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md
81[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md 78[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md
79[hu]: https://github.com/swaywm/sway/blob/master/README.hu.md
80[tr]: https://github.com/swaywm/sway/blob/master/README.tr.md
81[ir]: https://github.com/swaywm/sway/blob/master/README.ir.md
82[gr]: https://github.com/swaywm/sway/blob/master/README.gr.md
82[i3]: https://i3wm.org/ 83[i3]: https://i3wm.org/
83[Wayland]: http://wayland.freedesktop.org/ 84[Wayland]: http://wayland.freedesktop.org/
84[FAQ]: https://github.com/swaywm/sway/wiki 85[FAQ]: https://github.com/swaywm/sway/wiki
85[IRC channel]: http://webchat.freenode.net/?channels=sway&uio=d4 86[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
86[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 87[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
87[GitHub releases]: https://github.com/swaywm/sway/releases 88[GitHub releases]: https://github.com/swaywm/sway/releases
88[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 89[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
89[wlroots]: https://github.com/swaywm/wlroots 90[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
90[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 91[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.nl.md b/README.nl.md
index 86ebe398..c0a93063 100644
--- a/README.nl.md
+++ b/README.nl.md
@@ -2,12 +2,12 @@
2 2
3Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. 3Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor.
4Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC 4Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC
5kanaal](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway op 5kanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op
6irc.freenode.net). 6irc.libera.chat).
7 7
8## Releasehandtekeningen 8## Releasehandtekeningen
9 9
10Releases worden ondertekend met [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 10Releases worden ondertekend met [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
11en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases). 11en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases).
12 12
13## Installatie 13## Installatie
@@ -25,7 +25,7 @@ kanaal of stuur een e-mail naar sir@cmpwn.com voor advies.
25Afhankelijkheden installeren: 25Afhankelijkheden installeren:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre
diff --git a/README.pl.md b/README.pl.md
index b63b8567..6d376b68 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -1,12 +1,12 @@
1# sway 1# sway
2 2
3sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). 3sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/).
4Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](http://webchat.freenode.net/?channels=sway&uio=d4) 4Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/gamja/?channels=#sway)
5(#sway na irc.freenode.net). 5(#sway na irc.libera.chat).
6 6
7## Podpisy cyfrowe wydań 7## Podpisy cyfrowe wydań
8 8
9Wydania sÄ… podpisywane przy pomocy klucza [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 9Wydania sÄ… podpisywane przy pomocy klucza [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
10i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). 10i publikowane [na GitHubie](https://github.com/swaywm/sway/releases).
11 11
12## Instalacja 12## Instalacja
@@ -25,7 +25,7 @@ adres sir@cmpwn.com w celu uzyskania wskazówek.
25Zainstaluj zależności: 25Zainstaluj zależności:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre
diff --git a/README.pt.md b/README.pt.md
index ad7cab65..92a4b54d 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -2,12 +2,12 @@
2 2
3O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatível com o [i3](https://i3wm.org/). 3O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatível com o [i3](https://i3wm.org/).
4Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do 4Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do
5IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway em 5IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em
6irc.freenode.net). 6irc.libera.chat).
7 7
8## Assinatura das versões 8## Assinatura das versões
9 9
10As versões são assinadas com [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 10As versões são assinadas com [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
11e publicadas [no GitHub](https://github.com/swaywm/sway/releases). 11e publicadas [no GitHub](https://github.com/swaywm/sway/releases).
12 12
13## Instalação 13## Instalação
@@ -27,7 +27,7 @@ Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development
27Instale as dependências: 27Instale as dependências:
28 28
29* meson \* 29* meson \*
30* [wlroots](https://github.com/swaywm/wlroots) 30* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
31* wayland 31* wayland
32* wayland-protocols \* 32* wayland-protocols \*
33* pcre 33* pcre
diff --git a/README.ro.md b/README.ro.md
index dd895b56..f7785b8f 100644
--- a/README.ro.md
+++ b/README.ro.md
@@ -1,11 +1,11 @@
1# sway 1# sway
2 2
3sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). 3sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/).
4Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway pe irc.freenode.net). 4Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway pe irc.libera.chat).
5 5
6## Semnarea digitală 6## Semnarea digitală
7 7
8Noile versiuni sunt semnate cu [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 8Noile versiuni sunt semnate cu [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
9și postate [pe GitHub](https://github.com/swaywm/sway/releases). 9și postate [pe GitHub](https://github.com/swaywm/sway/releases).
10 10
11## Instalare 11## Instalare
@@ -22,7 +22,7 @@ Dacă sunteți interesați in a crea pachete pentru distribuția voastră, infor
22Dependențe pentru instalare: 22Dependențe pentru instalare:
23 23
24* meson \* 24* meson \*
25* [wlroots](https://github.com/swaywm/wlroots) 25* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
26* wayland 26* wayland
27* wayland-protocols \* 27* wayland-protocols \*
28* pcre 28* pcre
diff --git a/README.ru.md b/README.ru.md
index a870ec89..d563859c 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -1,42 +1,41 @@
1# sway 1# sway
2 2
3sway - Ñто [i3](https://i3wm.org/)-ÑовмеÑтимый композитор [Wayland](http://wayland.freedesktop.org/). 3sway - Ñто [i3]-ÑовмеÑтимый композитор [Wayland].
4Больше информации в [FAQ](https://github.com/swaywm/sway/wiki). ПриÑоединÑйтеÑÑŒ к 4Больше информации в [FAQ]. ПриÑоединÑйтеÑÑŒ к
5[IRC-каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 5[IRC-каналу][IRC channel] (#sway на
6irc.freenode.net). 6irc.libera.chat).
7 7
8## ПодпиÑи релизов 8## ПодпиÑи релизов
9 9
10Релизы подпиÑываютÑÑ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 10Релизы подпиÑываютÑÑ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ [E88F5E48] и публикуютÑÑ [на GitHub][GitHub releases].
11и публикуютÑÑ [на GitHub](https://github.com/swaywm/sway/releases).
12 11
13## УÑтановка 12## УÑтановка
14 13
15### Из репозиториев 14### Из репозиториев
16 15
17sway доÑтупен во многих диÑтрибутивах. Попробуйте уÑтановить пакет "sway". 16Sway доÑтупен во многих диÑтрибутивах. Попробуйте уÑтановить пакет "sway".
18ЕÑли он вдруг недоÑтупен, проверьте [Ñту Ñтраницу на wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages)
19Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о подробноÑÑ‚ÑÑ… уÑтановки Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾
20диÑтрибутива.
21 17
22ЕÑли вы заинтереÑованы поддерживать sway в вашем диÑтрибутиве, заглÑните в наш IRC-канал 18ЕÑли Ð²Ð°Ñ Ð¸Ð½Ñ‚ÐµÑ€ÐµÑует Ñоздание пакета sway Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ диÑтрибутива, зайдите на [IRC-канал][IRC channel]
23или обратитеÑÑŒ на sir@cmpwn.com за Ñоветом. 19или отправьте пиÑьмо на sir@cmpwn.com за Ñоветом.
24 20
25### Сборка из иÑходников 21### Сборка из иÑходников
26 22
23ПоÑетите [Ñту Ñтраницу на вики][Development setup], еÑли вы хотите поÑтроить поÑледнюю верÑию
24sway и wlroots Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ разработки.
25
27УÑтановите завиÑимоÑти: 26УÑтановите завиÑимоÑти:
28 27
29* meson \* 28* meson \*
30* [wlroots](https://github.com/swaywm/wlroots) 29* [wlroots]
31* wayland 30* wayland
32* wayland-protocols \* 31* wayland-protocols \*
33* pcre 32* pcre
34* json-c 33* json-c
35* pango 34* pango
36* cairo 35* cairo
37* gdk-pixbuf2 (необÑзательно: Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ треÑ) 36* gdk-pixbuf2 (опÑионально: Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ треÑ)
38* [scdoc](https://git.sr.ht/~sircmpwn/scdoc)½ÐµÐ¾Ð±Ñзательно: Ð´Ð»Ñ Ñборки man-Ñтраниц) \* 37* [scdoc] (опÑионально: Ð´Ð»Ñ man-Ñтраниц) \*
39* git \* 38* git (опционально: Ð´Ð»Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о верÑии) \*
40 39
41_\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_ 40_\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_
42 41
@@ -63,3 +62,13 @@ sway ÑброÑит root-права при запуÑке.
63 62
64Выполните команду `sway` прÑмо из TTY. Ðекоторые диÑплейные менеджеры могут работать, но они не поддерживаютÑÑ Ñо Ñтороны 63Выполните команду `sway` прÑмо из TTY. Ðекоторые диÑплейные менеджеры могут работать, но они не поддерживаютÑÑ Ñо Ñтороны
65sway (gdm работает довольно неплохо). 64sway (gdm работает довольно неплохо).
65
66[i3]: https://i3wm.org/
67[Wayland]: http://wayland.freedesktop.org/
68[FAQ]: https://github.com/swaywm/sway/wiki
69[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
70[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
71[GitHub releases]: https://github.com/swaywm/sway/releases
72[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
73[wlroots]: https://gitlab.freedesktop.org/wlroots/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..5c98a538
--- /dev/null
+++ b/README.tr.md
@@ -0,0 +1,68 @@
1# sway
2
3
4Sway, [i3]-uyumlu bir [Wayland] dizgicisidir. [SSS][FAQ]'yi okuyun.
5[IRC kanalı][IRC channel]na katılın \(irc.libera.chat'te #sway (İngilizce)).
6
7## Sürüm imzaları
8
9Sürümler [E88F5E48] ile imzalandı ve [GitHub][GitHub releases]'da yayınlandı.
10
11## Kurulum
12
13### Paketler ile
14
15Sway birçok dağıtımda mevcuttur. Sizinki için "sway" paketini yüklemeyi deneyin.
16
17Dağıtımınız için sway'i paketlemekle ilgileniyorsanız, IRC kanalına uğrayın veya tavsiye için sir@cmpwn.com adresine bir e-posta gönderin.
18
19### Kaynak koddan derleme
20
21Test veya geliştirme için sway ve wlroots'un HEAD'ini oluşturmak istiyorsanız [bu wiki sayfası][Development setup]na göz atın.
22
23Aşağıdaki bağımlılıkları yükleyin:
24
25* meson \*
26* [wlroots]
27* wayland
28* wayland-protocols \*
29* pcre
30* json-c
31* pango
32* cairo
33* gdk-pixbuf2 (isteğe bağlı: system tray)
34* [scdoc] (isteğe bağlı: man pages) \*
35* git (isteğe bağlı: version info) \*
36
37_\*Derleme-anı bağımlılıkları_
38
39Şu komutları çalıştırın:
40
41 meson build
42 ninja -C build
43 sudo ninja -C build install
44
45logind olmayan sistemlerde, sway ikilisine (binary) izin vermeniz (suid) gerekir:
46
47 sudo chmod a+s /usr/local/bin/sway
48
49Sway, başlangıçtan kısa bir süre sonra kök(root) izinlerini bırakacaktır.
50
51## Yapılandırma
52
53Zaten i3 kullanıyorsanız, i3 yapılandırmanızı `~/.config/sway/config` konumuna kopyalayın ve kutudan çıktığı gibi çalışacaktır. Aksi takdirde, örnek yapılandırma dosyasını `~/.config/sway/config` konumuna kopyalayın. Genellikle `/etc/sway/config` konumunda bulunur.
54Yapılandırma hakkında bilgi almak için `man 5 sway` komutunu çalıştırın.
55
56## Çalıştırma
57
58TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manager) çalışabilir ama Sway tarafından desteklenmez. (gdm'nin oldukça iyi çalıştığı bilinmektedir.)
59
60[i3]: https://i3wm.org/
61[Wayland]: http://wayland.freedesktop.org/
62[FAQ]: https://github.com/swaywm/sway/wiki
63[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
64[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
65[GitHub releases]: https://github.com/swaywm/sway/releases
66[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
67[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.uk.md b/README.uk.md
index 95047cb8..ff9ebec3 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -2,8 +2,8 @@
2 2
3Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). 3Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/).
4ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в 4ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в
5IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 5IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на
6irc.freenode.net). 6irc.libera.chat).
7 7
8## Підтримка українÑькою мовою 8## Підтримка українÑькою мовою
9 9
@@ -15,7 +15,7 @@ Hummer12007 у IRC-Ñпільноті. Будьте терплÑчі, вам оÐ
15 15
16## ПідпиÑи випуÑків 16## ПідпиÑи випуÑків
17 17
18ВипуÑки підпиÑані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 18ВипуÑки підпиÑані ключем [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
19та публікуютьÑÑ Ð½Ð° Ñторінці [GitHub](https://github.com/swaywm/sway/releases). 19та публікуютьÑÑ Ð½Ð° Ñторінці [GitHub](https://github.com/swaywm/sway/releases).
20 20
21## Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ 21## Ð’ÑтановленнÑ
@@ -36,7 +36,7 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
36Ð’Ñтановіть залежноÑÑ‚Ñ–: 36Ð’Ñтановіть залежноÑÑ‚Ñ–:
37 37
38* meson \* 38* meson \*
39* [wlroots](https://github.com/swaywm/wlroots) 39* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
40* wayland 40* wayland
41* wayland-protocols \* 41* wayland-protocols \*
42* pcre 42* pcre
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 9a3337ce..561d6c14 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -2,12 +2,12 @@
2 2
3sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. 3sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor.
4阅读 [FAQ](https://github.com/swaywm/sway/wiki). 加入 [IRC 4阅读 [FAQ](https://github.com/swaywm/sway/wiki). 加入 [IRC
5频é“](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 5频é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on
6irc.freenode.net). 6irc.libera.chat).
7 7
8## å‘布签å 8## å‘布签å
9 9
10å‘布是以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) ç­¾å 10å‘布是以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) ç­¾å
11并å‘布在 [GitHub](https://github.com/swaywm/sway/releases). 11并å‘布在 [GitHub](https://github.com/swaywm/sway/releases).
12 12
13## 安装 13## 安装
@@ -25,7 +25,7 @@ Sway 在很多å‘行版中å¯ç”¨. å°è¯•åœ¨ä½ çš„å‘行版中安装 "sway" 包.
25安装ä¾èµ–: 25安装ä¾èµ–:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre
diff --git a/README.zh-TW.md b/README.zh-TW.md
index 13a9d2f4..bc30b903 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -2,12 +2,12 @@
2 2
3sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 3sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。
4閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 加入 [IRC 4閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 加入 [IRC
5é »é“](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 5é »é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on
6irc.freenode.net) 6irc.libera.chat)
7 7
8## 發行簽章 8## 發行簽章
9 9
10所有發行的版本都會以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 簽署 10所有發行的版本都會以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 簽署
11並發佈於 [GitHub](https://github.com/swaywm/sway/releases) 11並發佈於 [GitHub](https://github.com/swaywm/sway/releases)
12 12
13## å®‰è£ 13## 安è£
@@ -25,7 +25,7 @@ Sway 在許多發行版都有æä¾›ã€‚è«‹è‡ªå·±å˜—è©¦æ–¼ä½ çš„ç™¼è¡Œç‰ˆå®‰è£ ã€
25相ä¾å¥—件: 25相ä¾å¥—件:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre
diff --git a/client/pool-buffer.c b/client/pool-buffer.c
index fd500c49..ea31edd3 100644
--- a/client/pool-buffer.c
+++ b/client/pool-buffer.c
@@ -1,6 +1,6 @@
1#define _POSIX_C_SOURCE 200809 1#define _POSIX_C_SOURCE 200809
2#include <assert.h> 2#include <assert.h>
3#include <cairo/cairo.h> 3#include <cairo.h>
4#include <fcntl.h> 4#include <fcntl.h>
5#include <pango/pangocairo.h> 5#include <pango/pangocairo.h>
6#include <stdio.h> 6#include <stdio.h>
diff --git a/common/background-image.c b/common/background-image.c
index de42e8e9..994a0805 100644
--- a/common/background-image.c
+++ b/common/background-image.c
@@ -1,6 +1,6 @@
1#include <assert.h> 1#include <assert.h>
2#include "background-image.h" 2#include "background-image.h"
3#include "cairo.h" 3#include "cairo_util.h"
4#include "log.h" 4#include "log.h"
5#if HAVE_GDK_PIXBUF 5#if HAVE_GDK_PIXBUF
6#include <gdk-pixbuf/gdk-pixbuf.h> 6#include <gdk-pixbuf/gdk-pixbuf.h>
diff --git a/common/cairo.c b/common/cairo.c
index 403dcf49..7c59d48c 100644
--- a/common/cairo.c
+++ b/common/cairo.c
@@ -1,6 +1,6 @@
1#include <stdint.h> 1#include <stdint.h>
2#include <cairo/cairo.h> 2#include <cairo.h>
3#include "cairo.h" 3#include "cairo_util.h"
4 4
5void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 5void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
6 cairo_set_source_rgba(cairo, 6 cairo_set_source_rgba(cairo,
diff --git a/common/pango.c b/common/pango.c
index fc3d0688..abc18281 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -1,4 +1,4 @@
1#include <cairo/cairo.h> 1#include <cairo.h>
2#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
3#include <stdarg.h> 3#include <stdarg.h>
4#include <stdbool.h> 4#include <stdbool.h>
@@ -6,7 +6,7 @@
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include "cairo.h" 9#include "cairo_util.h"
10#include "log.h" 10#include "log.h"
11#include "stringop.h" 11#include "stringop.h"
12 12
@@ -109,7 +109,23 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
109 free(buf); 109 free(buf);
110} 110}
111 111
112void pango_printf(cairo_t *cairo, const char *font, 112void get_text_metrics(const char *font, int *height, int *baseline) {
113 cairo_t *cairo = cairo_create(NULL);
114 PangoContext *pango = pango_cairo_create_context(cairo);
115 PangoFontDescription *description = pango_font_description_from_string(font);
116 // When passing NULL as a language, pango uses the current locale.
117 PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
118
119 *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
120 *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
121
122 pango_font_metrics_unref(metrics);
123 pango_font_description_free(description);
124 g_object_unref(pango);
125 cairo_destroy(cairo);
126}
127
128void render_text(cairo_t *cairo, const char *font,
113 double scale, bool markup, const char *fmt, ...) { 129 double scale, bool markup, const char *fmt, ...) {
114 va_list args; 130 va_list args;
115 va_start(args, fmt); 131 va_start(args, fmt);
diff --git a/common/util.c b/common/util.c
index 5ea94f48..5d4c0673 100644
--- a/common/util.c
+++ b/common/util.c
@@ -10,12 +10,6 @@
10#include "log.h" 10#include "log.h"
11#include "util.h" 11#include "util.h"
12 12
13uint32_t get_current_time_msec(void) {
14 struct timespec now;
15 clock_gettime(CLOCK_MONOTONIC, &now);
16 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
17}
18
19int wrap(int i, int max) { 13int wrap(int i, int max) {
20 return ((i % max) + max) % max; 14 return ((i % max) + max) % max;
21} 15}
@@ -86,6 +80,12 @@ enum movement_unit parse_movement_unit(const char *unit) {
86 80
87int parse_movement_amount(int argc, char **argv, 81int parse_movement_amount(int argc, char **argv,
88 struct movement_amount *amount) { 82 struct movement_amount *amount) {
83 if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) {
84 amount->amount = 0;
85 amount->unit = MOVEMENT_UNIT_INVALID;
86 return 0;
87 }
88
89 char *err; 89 char *err;
90 amount->amount = (int)strtol(argv[0], &err, 10); 90 amount->amount = (int)strtol(argv[0], &err, 10);
91 if (*err) { 91 if (*err) {
diff --git a/config.in b/config.in
index 08703bef..94050ba6 100644
--- a/config.in
+++ b/config.in
@@ -14,7 +14,7 @@ set $down j
14set $up k 14set $up k
15set $right l 15set $right l
16# Your preferred terminal emulator 16# Your preferred terminal emulator
17set $term alacritty 17set $term foot
18# Your preferred application launcher 18# Your preferred application launcher
19# Note: pass the final command to swaymsg so that the resulting window can be opened 19# Note: pass the final command to swaymsg so that the resulting window can be opened
20# on the original workspace that the command was run on. 20# on the original workspace that the command was run on.
@@ -82,7 +82,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
82 bindsym $mod+Shift+c reload 82 bindsym $mod+Shift+c reload
83 83
84 # Exit sway (logs you out of your Wayland session) 84 # Exit sway (logs you out of your Wayland session)
85 bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' 85 bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'
86# 86#
87# Moving around: 87# Moving around:
88# 88#
@@ -205,7 +205,7 @@ bar {
205 205
206 # When the status_command prints a new line to stdout, swaybar updates. 206 # When the status_command prints a new line to stdout, swaybar updates.
207 # The default just shows the current date and time. 207 # The default just shows the current date and time.
208 status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done 208 status_command while date +'%Y-%m-%d %I:%M:%S %p'; do sleep 1; done
209 209
210 colors { 210 colors {
211 statusline #ffffff 211 statusline #ffffff
diff --git a/contrib/_incr_version b/contrib/_incr_version
deleted file mode 100755
index a4fa2654..00000000
--- a/contrib/_incr_version
+++ /dev/null
@@ -1,21 +0,0 @@
1#!/bin/sh -eu
2old_version="$1"
3new_version="$2"
4
5if [ "$new_version" != "${new_version#v}" ]
6then
7 echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
8 exit 1
9fi
10
11sed -i meson.build -e "s/^ version: .*#release_version/ version: '$new_version', #release_version/g"
12
13printf "Minimum wlroots version? "
14read wlr_version_min
15printf "Maximum wlroots version? "
16read wlr_version_max
17
18sed -i meson.build -e "s/wlroots_version =.*/wlroots_version = ['>=$wlr_version_min', '<$wlr_version_max']/"
19
20git add meson.build
21git commit -m "Update version to $new_version"
diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py
index 297d91b2..3ec39928 100755
--- a/contrib/autoname-workspaces.py
+++ b/contrib/autoname-workspaces.py
@@ -22,24 +22,17 @@ DEFAULT_ICON = "ó°€"
22 22
23 23
24def icon_for_window(window): 24def icon_for_window(window):
25 app_id = window.app_id 25 name = None
26 if app_id is not None and len(app_id) > 0: 26 if window.app_id is not None and len(window.app_id) > 0:
27 app_id = app_id.lower() 27 name = window.app_id.lower()
28 if app_id in WINDOW_ICONS: 28 elif window.window_class is not None and len(window.window_class) > 0:
29 return WINDOW_ICONS[app_id] 29 name = window.window_class.lower()
30 logging.info("No icon available for window with app_id: %s" % str(app_id)) 30
31 else: 31 if name in WINDOW_ICONS:
32 # xwayland support 32 return WINDOW_ICONS[name]
33 class_name = window.window_class
34 if len(class_name) > 0:
35 class_name = class_name.lower()
36 if class_name in WINDOW_ICONS:
37 return WINDOW_ICONS[class_name]
38 logging.info(
39 "No icon available for window with class_name: %s" % str(class_name)
40 )
41 return DEFAULT_ICON
42 33
34 logging.info("No icon available for window with name: %s" % str(name))
35 return DEFAULT_ICON
43 36
44def rename_workspaces(ipc): 37def rename_workspaces(ipc):
45 for workspace in ipc.get_tree().workspaces(): 38 for workspace in ipc.get_tree().workspaces():
@@ -128,3 +121,4 @@ if __name__ == "__main__":
128 rename_workspaces(ipc) 121 rename_workspaces(ipc)
129 122
130 ipc.main() 123 ipc.main()
124
diff --git a/contrib/grimshot b/contrib/grimshot
index 461a5eef..4ce31f29 100755
--- a/contrib/grimshot
+++ b/contrib/grimshot
@@ -32,13 +32,13 @@ FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
32 32
33if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then 33if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
34 echo "Usage:" 34 echo "Usage:"
35 echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE]" 35 echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE|-]"
36 echo " grimshot check" 36 echo " grimshot check"
37 echo " grimshot usage" 37 echo " grimshot usage"
38 echo "" 38 echo ""
39 echo "Commands:" 39 echo "Commands:"
40 echo " copy: Copy the screenshot data into the clipboard." 40 echo " copy: Copy the screenshot data into the clipboard."
41 echo " save: Save the screenshot to a regular file." 41 echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
42 echo " check: Verify if required tools are installed and exit." 42 echo " check: Verify if required tools are installed and exit."
43 echo " usage: Show this message and exit." 43 echo " usage: Show this message and exit."
44 echo "" 44 echo ""
@@ -113,7 +113,7 @@ elif [ "$SUBJECT" = "area" ] ; then
113 GEOM=$(slurp -d) 113 GEOM=$(slurp -d)
114 # Check if user exited slurp without selecting the area 114 # Check if user exited slurp without selecting the area
115 if [ -z "$GEOM" ]; then 115 if [ -z "$GEOM" ]; then
116 exit 116 exit 1
117 fi 117 fi
118 WHAT="Area" 118 WHAT="Area"
119elif [ "$SUBJECT" = "active" ] ; then 119elif [ "$SUBJECT" = "active" ] ; then
@@ -132,7 +132,7 @@ elif [ "$SUBJECT" = "window" ] ; then
132 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) 132 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)
133 # Check if user exited slurp without selecting the area 133 # Check if user exited slurp without selecting the area
134 if [ -z "$GEOM" ]; then 134 if [ -z "$GEOM" ]; then
135 exit 135 exit 1
136 fi 136 fi
137 WHAT="Window" 137 WHAT="Window"
138else 138else
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1
index f6c8a377..e4baccfd 100644
--- a/contrib/grimshot.1
+++ b/contrib/grimshot.1
@@ -5,7 +5,7 @@
5.nh 5.nh
6.ad l 6.ad l
7.\" Begin generated content: 7.\" Begin generated content:
8.TH "grimshot" "1" "2020-12-20" 8.TH "grimshot" "1" "2021-02-23"
9.P 9.P
10.SH NAME 10.SH NAME
11.P 11.P
@@ -31,6 +31,7 @@ Show notifications to the user that a screenshot has been taken.\&
31Save the screenshot into a regular file.\& Grimshot will write images 31Save the screenshot into a regular file.\& Grimshot will write images
32files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined 32files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined
33in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& 33in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
34Set FILE to '-' to pipe the output to STDOUT.\&
34.P 35.P
35.RE 36.RE
36\fBcopy\fR 37\fBcopy\fR
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd
index 4ab58532..d2a57759 100644
--- a/contrib/grimshot.1.scd
+++ b/contrib/grimshot.1.scd
@@ -19,6 +19,7 @@ grimshot - a helper for screenshots within sway
19 Save the screenshot into a regular file. Grimshot will write images 19 Save the screenshot into a regular file. Grimshot will write images
20 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined 20 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
21 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. 21 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
22 Set FILE to '-' to pipe the output to STDOUT.
22 23
23*copy* 24*copy*
24 Copy the screenshot data (as image/png) into the clipboard. 25 Copy the screenshot data (as image/png) into the clipboard.
diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py
index 77b1f221..b81134dd 100755
--- a/contrib/inactive-windows-transparency.py
+++ b/contrib/inactive-windows-transparency.py
@@ -15,8 +15,13 @@ def on_window_focus(inactive_opacity, ipc, event):
15 global prev_focused 15 global prev_focused
16 global prev_workspace 16 global prev_workspace
17 17
18 focused_workspace = ipc.get_tree().find_focused()
19
20 if focused_workspace == None:
21 return
22
18 focused = event.container 23 focused = event.container
19 workspace = ipc.get_tree().find_focused().workspace().num 24 workspace = focused_workspace.workspace().num
20 25
21 if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 26 if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859
22 focused.command("opacity 1") 27 focused.command("opacity 1")
diff --git a/include/background-image.h b/include/background-image.h
index 15935ffd..a97ef375 100644
--- a/include/background-image.h
+++ b/include/background-image.h
@@ -1,6 +1,6 @@
1#ifndef _SWAY_BACKGROUND_IMAGE_H 1#ifndef _SWAY_BACKGROUND_IMAGE_H
2#define _SWAY_BACKGROUND_IMAGE_H 2#define _SWAY_BACKGROUND_IMAGE_H
3#include "cairo.h" 3#include "cairo_util.h"
4 4
5enum background_mode { 5enum background_mode {
6 BACKGROUND_MODE_STRETCH, 6 BACKGROUND_MODE_STRETCH,
diff --git a/include/cairo.h b/include/cairo_util.h
index c1275db2..dc049c6d 100644
--- a/include/cairo.h
+++ b/include/cairo_util.h
@@ -1,8 +1,8 @@
1#ifndef _SWAY_CAIRO_H 1#ifndef _SWAY_CAIRO_UTIL_H
2#define _SWAY_CAIRO_H 2#define _SWAY_CAIRO_UTIL_H
3#include "config.h" 3#include "config.h"
4#include <stdint.h> 4#include <stdint.h>
5#include <cairo/cairo.h> 5#include <cairo.h>
6#include <wayland-client-protocol.h> 6#include <wayland-client-protocol.h>
7 7
8void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 8void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
diff --git a/include/ipc-client.h b/include/ipc-client.h
index d3895023..9c5712d7 100644
--- a/include/ipc-client.h
+++ b/include/ipc-client.h
@@ -1,6 +1,9 @@
1#ifndef _SWAY_IPC_CLIENT_H 1#ifndef _SWAY_IPC_CLIENT_H
2#define _SWAY_IPC_CLIENT_H 2#define _SWAY_IPC_CLIENT_H
3 3
4// arbitrary number, it's probably sufficient, higher number = more memory usage
5#define JSON_MAX_DEPTH 512
6
4#include <stdbool.h> 7#include <stdbool.h>
5#include <stdint.h> 8#include <stdint.h>
6#include <sys/time.h> 9#include <sys/time.h>
diff --git a/include/pango.h b/include/pango.h
index 6ab83c16..93affc23 100644
--- a/include/pango.h
+++ b/include/pango.h
@@ -3,7 +3,7 @@
3#include <stdarg.h> 3#include <stdarg.h>
4#include <stdbool.h> 4#include <stdbool.h>
5#include <stdint.h> 5#include <stdint.h>
6#include <cairo/cairo.h> 6#include <cairo.h>
7#include <pango/pangocairo.h> 7#include <pango/pangocairo.h>
8 8
9/** 9/**
@@ -17,7 +17,8 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
17 const char *text, double scale, bool markup); 17 const char *text, double scale, bool markup);
18void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 18void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
19 int *baseline, double scale, bool markup, const char *fmt, ...); 19 int *baseline, double scale, bool markup, const char *fmt, ...);
20void pango_printf(cairo_t *cairo, const char *font, 20void get_text_metrics(const char *font, int *height, int *baseline);
21void render_text(cairo_t *cairo, const char *font,
21 double scale, bool markup, const char *fmt, ...); 22 double scale, bool markup, const char *fmt, ...);
22 23
23#endif 24#endif
diff --git a/include/pool-buffer.h b/include/pool-buffer.h
index 54f5be06..b7a95afe 100644
--- a/include/pool-buffer.h
+++ b/include/pool-buffer.h
@@ -1,6 +1,6 @@
1#ifndef _SWAY_BUFFERS_H 1#ifndef _SWAY_BUFFERS_H
2#define _SWAY_BUFFERS_H 2#define _SWAY_BUFFERS_H
3#include <cairo/cairo.h> 3#include <cairo.h>
4#include <pango/pangocairo.h> 4#include <pango/pangocairo.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stdint.h> 6#include <stdint.h>
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 964b3661..2746ef28 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -46,8 +46,8 @@ enum expected_args {
46struct cmd_results *checkarg(int argc, const char *name, 46struct cmd_results *checkarg(int argc, const char *name,
47 enum expected_args type, int val); 47 enum expected_args type, int val);
48 48
49struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, 49const struct cmd_handler *find_handler(char *line,
50 size_t handlers_size); 50 const struct cmd_handler *cmd_handlers, size_t handlers_size);
51 51
52/** 52/**
53 * Parse and executes a command. 53 * Parse and executes a command.
@@ -68,7 +68,7 @@ struct cmd_results *config_command(char *command, char **new_block);
68 * Parse and handle a sub command 68 * Parse and handle a sub command
69 */ 69 */
70struct cmd_results *config_subcommand(char **argv, int argc, 70struct cmd_results *config_subcommand(char **argv, int argc,
71 struct cmd_handler *handlers, size_t handlers_size); 71 const struct cmd_handler *handlers, size_t handlers_size);
72/* 72/*
73 * Parses a command policy rule. 73 * Parses a command policy rule.
74 */ 74 */
@@ -112,6 +112,7 @@ sway_cmd cmd_border;
112sway_cmd cmd_client_noop; 112sway_cmd cmd_client_noop;
113sway_cmd cmd_client_focused; 113sway_cmd cmd_client_focused;
114sway_cmd cmd_client_focused_inactive; 114sway_cmd cmd_client_focused_inactive;
115sway_cmd cmd_client_focused_tab_title;
115sway_cmd cmd_client_unfocused; 116sway_cmd cmd_client_unfocused;
116sway_cmd cmd_client_urgent; 117sway_cmd cmd_client_urgent;
117sway_cmd cmd_client_placeholder; 118sway_cmd cmd_client_placeholder;
@@ -282,7 +283,9 @@ sway_cmd output_cmd_dpms;
282sway_cmd output_cmd_enable; 283sway_cmd output_cmd_enable;
283sway_cmd output_cmd_max_render_time; 284sway_cmd output_cmd_max_render_time;
284sway_cmd output_cmd_mode; 285sway_cmd output_cmd_mode;
286sway_cmd output_cmd_modeline;
285sway_cmd output_cmd_position; 287sway_cmd output_cmd_position;
288sway_cmd output_cmd_render_bit_depth;
286sway_cmd output_cmd_scale; 289sway_cmd output_cmd_scale;
287sway_cmd output_cmd_scale_filter; 290sway_cmd output_cmd_scale_filter;
288sway_cmd output_cmd_subpixel; 291sway_cmd output_cmd_subpixel;
diff --git a/include/sway/config.h b/include/sway/config.h
index 59f22ae2..fda0e83f 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -5,9 +5,10 @@
5#include <string.h> 5#include <string.h>
6#include <time.h> 6#include <time.h>
7#include <wlr/interfaces/wlr_switch.h> 7#include <wlr/interfaces/wlr_switch.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_tablet_tool.h> 8#include <wlr/types/wlr_tablet_tool.h>
9#include <wlr/util/box.h>
10#include <xkbcommon/xkbcommon.h> 10#include <xkbcommon/xkbcommon.h>
11#include <xf86drmMode.h>
11#include "../include/config.h" 12#include "../include/config.h"
12#include "list.h" 13#include "list.h"
13#include "swaynag.h" 14#include "swaynag.h"
@@ -246,6 +247,12 @@ enum scale_filter_mode {
246 SCALE_FILTER_SMART, 247 SCALE_FILTER_SMART,
247}; 248};
248 249
250enum render_bit_depth {
251 RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8
252 RENDER_BIT_DEPTH_8,
253 RENDER_BIT_DEPTH_10,
254};
255
249/** 256/**
250 * Size and position configuration for a particular output. 257 * Size and position configuration for a particular output.
251 * 258 *
@@ -257,6 +264,7 @@ struct output_config {
257 int width, height; 264 int width, height;
258 float refresh_rate; 265 float refresh_rate;
259 int custom_mode; 266 int custom_mode;
267 drmModeModeInfo drm_mode;
260 int x, y; 268 int x, y;
261 float scale; 269 float scale;
262 enum scale_filter_mode scale_filter; 270 enum scale_filter_mode scale_filter;
@@ -264,6 +272,7 @@ struct output_config {
264 enum wl_output_subpixel subpixel; 272 enum wl_output_subpixel subpixel;
265 int max_render_time; // In milliseconds 273 int max_render_time; // In milliseconds
266 int adaptive_sync; 274 int adaptive_sync;
275 enum render_bit_depth render_bit_depth;
267 276
268 char *background; 277 char *background;
269 char *background_option; 278 char *background_option;
@@ -281,6 +290,12 @@ struct side_gaps {
281 int left; 290 int left;
282}; 291};
283 292
293enum smart_gaps_mode {
294 SMART_GAPS_OFF,
295 SMART_GAPS_ON,
296 SMART_GAPS_INVERSE_OUTER,
297};
298
284/** 299/**
285 * Stores configuration for a workspace, regardless of whether the workspace 300 * Stores configuration for a workspace, regardless of whether the workspace
286 * exists. 301 * exists.
@@ -292,6 +307,12 @@ struct workspace_config {
292 struct side_gaps gaps_outer; 307 struct side_gaps gaps_outer;
293}; 308};
294 309
310enum pango_markup_config {
311 PANGO_MARKUP_DISABLED = false,
312 PANGO_MARKUP_ENABLED = true,
313 PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix)
314};
315
295struct bar_config { 316struct bar_config {
296 char *swaybar_command; 317 char *swaybar_command;
297 struct wl_client *client; 318 struct wl_client *client;
@@ -323,7 +344,7 @@ struct bar_config {
323 char *position; 344 char *position;
324 list_t *bindings; 345 list_t *bindings;
325 char *status_command; 346 char *status_command;
326 bool pango_markup; 347 enum pango_markup_config pango_markup;
327 char *font; 348 char *font;
328 int height; // -1 not defined 349 int height; // -1 not defined
329 bool workspace_buttons; 350 bool workspace_buttons;
@@ -410,14 +431,6 @@ enum sway_popup_during_fullscreen {
410 POPUP_LEAVE, 431 POPUP_LEAVE,
411}; 432};
412 433
413enum command_context {
414 CONTEXT_CONFIG = 1 << 0,
415 CONTEXT_BINDING = 1 << 1,
416 CONTEXT_IPC = 1 << 2,
417 CONTEXT_CRITERIA = 1 << 3,
418 CONTEXT_ALL = 0xFFFFFFFF,
419};
420
421enum focus_follows_mouse_mode { 434enum focus_follows_mouse_mode {
422 FOLLOWS_NO, 435 FOLLOWS_NO,
423 FOLLOWS_YES, 436 FOLLOWS_YES,
@@ -480,8 +493,8 @@ struct sway_config {
480 enum sway_container_layout default_orientation; 493 enum sway_container_layout default_orientation;
481 enum sway_container_layout default_layout; 494 enum sway_container_layout default_layout;
482 char *font; 495 char *font;
483 size_t font_height; 496 int font_height;
484 size_t font_baseline; 497 int font_baseline;
485 bool pango_markup; 498 bool pango_markup;
486 int titlebar_border_thickness; 499 int titlebar_border_thickness;
487 int titlebar_h_padding; 500 int titlebar_h_padding;
@@ -512,7 +525,7 @@ struct sway_config {
512 bool tiling_drag; 525 bool tiling_drag;
513 int tiling_drag_threshold; 526 int tiling_drag_threshold;
514 527
515 bool smart_gaps; 528 enum smart_gaps_mode smart_gaps;
516 int gaps_inner; 529 int gaps_inner;
517 struct side_gaps gaps_outer; 530 struct side_gaps gaps_outer;
518 531
@@ -535,12 +548,15 @@ struct sway_config {
535 struct { 548 struct {
536 struct border_colors focused; 549 struct border_colors focused;
537 struct border_colors focused_inactive; 550 struct border_colors focused_inactive;
551 struct border_colors focused_tab_title;
538 struct border_colors unfocused; 552 struct border_colors unfocused;
539 struct border_colors urgent; 553 struct border_colors urgent;
540 struct border_colors placeholder; 554 struct border_colors placeholder;
541 float background[4]; 555 float background[4];
542 } border_colors; 556 } border_colors;
543 557
558 bool has_focused_tab_title;
559
544 // floating view 560 // floating view
545 int32_t floating_maximum_width; 561 int32_t floating_maximum_width;
546 int32_t floating_maximum_height; 562 int32_t floating_maximum_height;
@@ -559,7 +575,7 @@ struct sway_config {
559 struct sway_node *node; 575 struct sway_node *node;
560 struct sway_container *container; 576 struct sway_container *container;
561 struct sway_workspace *workspace; 577 struct sway_workspace *workspace;
562 bool using_criteria; 578 bool node_overridden; // True if the node is selected by means other than focus
563 struct { 579 struct {
564 int argc; 580 int argc;
565 char **argv; 581 char **argv;
@@ -690,14 +706,13 @@ void free_bar_binding(struct bar_binding *binding);
690void free_workspace_config(struct workspace_config *wsc); 706void free_workspace_config(struct workspace_config *wsc);
691 707
692/** 708/**
693 * Updates the value of config->font_height based on the max title height 709 * Updates the value of config->font_height based on the metrics for title's
694 * reported by each container. If recalculate is true, the containers will 710 * font as reported by pango.
695 * recalculate their heights before reporting. 711 *
696 *
697 * If the height has changed, all containers will be rearranged to take on the 712 * If the height has changed, all containers will be rearranged to take on the
698 * new size. 713 * new size.
699 */ 714 */
700void config_update_font_height(bool recalculate); 715void config_update_font_height(void);
701 716
702/** 717/**
703 * Convert bindsym into bindcode using the first configured layout. 718 * Convert bindsym into bindcode using the first configured layout.
diff --git a/include/sway/desktop.h b/include/sway/desktop.h
index c969a76b..7f2f5b3e 100644
--- a/include/sway/desktop.h
+++ b/include/sway/desktop.h
@@ -1,4 +1,4 @@
1#include <wlr/types/wlr_surface.h> 1#include <wlr/types/wlr_compositor.h>
2 2
3struct sway_container; 3struct sway_container;
4struct sway_view; 4struct sway_view;
diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h
index 0adafdb9..58d54c68 100644
--- a/include/sway/desktop/idle_inhibit_v1.h
+++ b/include/sway/desktop/idle_inhibit_v1.h
@@ -22,6 +22,7 @@ struct sway_idle_inhibit_manager_v1 {
22 22
23struct sway_idle_inhibitor_v1 { 23struct sway_idle_inhibitor_v1 {
24 struct sway_idle_inhibit_manager_v1 *manager; 24 struct sway_idle_inhibit_manager_v1 *manager;
25 struct wlr_idle_inhibitor_v1 *wlr_inhibitor;
25 struct sway_view *view; 26 struct sway_view *view;
26 enum sway_idle_inhibit_mode mode; 27 enum sway_idle_inhibit_mode mode;
27 28
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 175489c5..7dd58ba8 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -28,6 +28,12 @@ struct sway_view;
28 */ 28 */
29void transaction_commit_dirty(void); 29void transaction_commit_dirty(void);
30 30
31/*
32 * Same as transaction_commit_dirty, but signalling that this is a
33 * client-initiated change has already taken effect.
34 */
35void transaction_commit_dirty_client(void);
36
31/** 37/**
32 * Notify the transaction system that a view is ready for the new layout. 38 * Notify the transaction system that a view is ready for the new layout.
33 * 39 *
@@ -45,10 +51,4 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view,
45void transaction_notify_view_ready_by_geometry(struct sway_view *view, 51void transaction_notify_view_ready_by_geometry(struct sway_view *view,
46 double x, double y, int width, int height); 52 double x, double y, int width, int height);
47 53
48/**
49 * Unconditionally notify the transaction system that a view is ready for the
50 * new layout.
51 */
52void transaction_notify_view_ready_immediately(struct sway_view *view);
53
54#endif 54#endif
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 6a38190b..853f8838 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -4,7 +4,7 @@
4#include <stdint.h> 4#include <stdint.h>
5#include <wlr/types/wlr_pointer_constraints_v1.h> 5#include <wlr/types/wlr_pointer_constraints_v1.h>
6#include <wlr/types/wlr_pointer_gestures_v1.h> 6#include <wlr/types/wlr_pointer_gestures_v1.h>
7#include <wlr/types/wlr_surface.h> 7#include <wlr/types/wlr_compositor.h>
8#include "sway/input/seat.h" 8#include "sway/input/seat.h"
9#include "config.h" 9#include "config.h"
10 10
@@ -52,7 +52,9 @@ struct sway_cursor {
52 struct wl_listener touch_down; 52 struct wl_listener touch_down;
53 struct wl_listener touch_up; 53 struct wl_listener touch_up;
54 struct wl_listener touch_motion; 54 struct wl_listener touch_motion;
55 struct wl_listener touch_frame;
55 bool simulating_pointer_from_touch; 56 bool simulating_pointer_from_touch;
57 bool pointer_touch_up;
56 int32_t pointer_touch_id; 58 int32_t pointer_touch_id;
57 59
58 struct wl_listener tool_axis; 60 struct wl_listener tool_axis;
diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h
index de019976..890d632e 100644
--- a/include/sway/input/libinput.h
+++ b/include/sway/input/libinput.h
@@ -6,4 +6,6 @@ void sway_input_configure_libinput_device(struct sway_input_device *device);
6 6
7void sway_input_reset_libinput_device(struct sway_input_device *device); 7void sway_input_reset_libinput_device(struct sway_input_device *device);
8 8
9bool sway_libinput_device_is_builtin(struct sway_input_device *device);
10
9#endif 11#endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 4118df66..77c2278d 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -239,7 +239,10 @@ enum wlr_edges find_resize_edge(struct sway_container *cont,
239void seatop_begin_default(struct sway_seat *seat); 239void seatop_begin_default(struct sway_seat *seat);
240 240
241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
242 uint32_t time_msec, int sx, int sy); 242 uint32_t time_msec, double sx, double sy);
243
244void seatop_begin_down_on_surface(struct sway_seat *seat,
245 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy);
243 246
244void seatop_begin_move_floating(struct sway_seat *seat, 247void seatop_begin_move_floating(struct sway_seat *seat,
245 struct sway_container *con); 248 struct sway_container *con);
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
index 6cf9bdb3..c70fd935 100644
--- a/include/sway/input/text_input.h
+++ b/include/sway/input/text_input.h
@@ -3,7 +3,7 @@
3 3
4#include <wlr/types/wlr_text_input_v3.h> 4#include <wlr/types/wlr_text_input_v3.h>
5#include <wlr/types/wlr_input_method_v2.h> 5#include <wlr/types/wlr_input_method_v2.h>
6#include <wlr/types/wlr_surface.h> 6#include <wlr/types/wlr_compositor.h>
7#include "sway/input/seat.h" 7#include "sway/input/seat.h"
8 8
9/** 9/**
@@ -28,7 +28,10 @@ struct sway_input_method_relay {
28 28
29 struct wl_listener input_method_new; 29 struct wl_listener input_method_new;
30 struct wl_listener input_method_commit; 30 struct wl_listener input_method_commit;
31 struct wl_listener input_method_grab_keyboard;
31 struct wl_listener input_method_destroy; 32 struct wl_listener input_method_destroy;
33
34 struct wl_listener input_method_keyboard_grab_destroy;
32}; 35};
33 36
34struct sway_text_input { 37struct sway_text_input {
diff --git a/include/sway/layers.h b/include/sway/layers.h
index 457634c2..f8508493 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -1,8 +1,7 @@
1#ifndef _SWAY_LAYERS_H 1#ifndef _SWAY_LAYERS_H
2#define _SWAY_LAYERS_H 2#define _SWAY_LAYERS_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/types/wlr_box.h> 4#include <wlr/types/wlr_compositor.h>
5#include <wlr/types/wlr_surface.h>
6#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
7 6
8enum layer_parent { 7enum layer_parent {
@@ -23,7 +22,11 @@ struct sway_layer_surface {
23 struct wl_listener new_subsurface; 22 struct wl_listener new_subsurface;
24 23
25 struct wlr_box geo; 24 struct wlr_box geo;
25 bool mapped;
26 struct wlr_box extent;
26 enum zwlr_layer_shell_v1_layer layer; 27 enum zwlr_layer_shell_v1_layer layer;
28
29 struct wl_list subsurfaces;
27}; 30};
28 31
29struct sway_layer_popup { 32struct sway_layer_popup {
@@ -43,6 +46,7 @@ struct sway_layer_popup {
43struct sway_layer_subsurface { 46struct sway_layer_subsurface {
44 struct wlr_subsurface *wlr_subsurface; 47 struct wlr_subsurface *wlr_subsurface;
45 struct sway_layer_surface *layer_surface; 48 struct sway_layer_surface *layer_surface;
49 struct wl_list link;
46 50
47 struct wl_listener map; 51 struct wl_listener map;
48 struct wl_listener unmap; 52 struct wl_listener unmap;
diff --git a/include/sway/output.h b/include/sway/output.h
index 96986700..26b9709f 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -3,7 +3,6 @@
3#include <time.h> 3#include <time.h>
4#include <unistd.h> 4#include <unistd.h>
5#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_box.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
8#include "config.h" 7#include "config.h"
9#include "sway/tree/node.h" 8#include "sway/tree/node.h"
@@ -49,7 +48,7 @@ struct sway_output {
49 struct wl_listener damage_frame; 48 struct wl_listener damage_frame;
50 49
51 struct { 50 struct {
52 struct wl_signal destroy; 51 struct wl_signal disable;
53 } events; 52 } events;
54 53
55 struct timespec last_presentation; 54 struct timespec last_presentation;
@@ -72,8 +71,8 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
72void output_add_workspace(struct sway_output *output, 71void output_add_workspace(struct sway_output *output,
73 struct sway_workspace *workspace); 72 struct sway_workspace *workspace);
74 73
75typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, 74typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
76 struct wlr_surface *surface, struct wlr_box *box, float rotation, 75 struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box,
77 void *user_data); 76 void *user_data);
78 77
79void output_damage_whole(struct sway_output *output); 78void output_damage_whole(struct sway_output *output);
diff --git a/include/sway/server.h b/include/sway/server.h
index 0f5e3ab2..0bd860b2 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -4,11 +4,13 @@
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/backend.h> 5#include <wlr/backend.h>
6#include <wlr/backend/session.h> 6#include <wlr/backend/session.h>
7#include <wlr/render/allocator.h>
7#include <wlr/render/wlr_renderer.h> 8#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_compositor.h> 9#include <wlr/types/wlr_compositor.h>
9#include <wlr/types/wlr_data_device.h> 10#include <wlr/types/wlr_data_device.h>
10#include <wlr/types/wlr_input_method_v2.h> 11#include <wlr/types/wlr_input_method_v2.h>
11#include <wlr/types/wlr_foreign_toplevel_management_v1.h> 12#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
13#include <wlr/types/wlr_drm_lease_v1.h>
12#include <wlr/types/wlr_layer_shell_v1.h> 14#include <wlr/types/wlr_layer_shell_v1.h>
13#include <wlr/types/wlr_output_management_v1.h> 15#include <wlr/types/wlr_output_management_v1.h>
14#include <wlr/types/wlr_output_power_management_v1.h> 16#include <wlr/types/wlr_output_power_management_v1.h>
@@ -23,19 +25,24 @@
23#include "sway/xwayland.h" 25#include "sway/xwayland.h"
24#endif 26#endif
25 27
28struct sway_transaction;
29
26struct sway_server { 30struct sway_server {
27 struct wl_display *wl_display; 31 struct wl_display *wl_display;
28 struct wl_event_loop *wl_event_loop; 32 struct wl_event_loop *wl_event_loop;
29 const char *socket; 33 const char *socket;
30 34
31 struct wlr_backend *backend; 35 struct wlr_backend *backend;
32 struct wlr_backend *noop_backend;
33 // secondary headless backend used for creating virtual outputs on-the-fly 36 // secondary headless backend used for creating virtual outputs on-the-fly
34 struct wlr_backend *headless_backend; 37 struct wlr_backend *headless_backend;
38 struct wlr_renderer *renderer;
39 struct wlr_allocator *allocator;
35 40
36 struct wlr_compositor *compositor; 41 struct wlr_compositor *compositor;
37 struct wl_listener compositor_new_surface; 42 struct wl_listener compositor_new_surface;
38 43
44 struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
45
39 struct wlr_data_device_manager *data_device_manager; 46 struct wlr_data_device_manager *data_device_manager;
40 47
41 struct sway_input_manager *input; 48 struct sway_input_manager *input;
@@ -70,6 +77,9 @@ struct sway_server {
70 struct wl_listener xdg_decoration; 77 struct wl_listener xdg_decoration;
71 struct wl_list xdg_decorations; // sway_xdg_decoration::link 78 struct wl_list xdg_decorations; // sway_xdg_decoration::link
72 79
80 struct wlr_drm_lease_v1_manager *drm_lease_manager;
81 struct wl_listener drm_lease_request;
82
73 struct wlr_presentation *presentation; 83 struct wlr_presentation *presentation;
74 84
75 struct wlr_pointer_constraints_v1 *pointer_constraints; 85 struct wlr_pointer_constraints_v1 *pointer_constraints;
@@ -85,8 +95,25 @@ struct sway_server {
85 struct wlr_text_input_manager_v3 *text_input; 95 struct wlr_text_input_manager_v3 *text_input;
86 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 96 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
87 97
98 struct wlr_xdg_activation_v1 *xdg_activation_v1;
99 struct wl_listener xdg_activation_v1_request_activate;
100
101 // The timeout for transactions, after which a transaction is applied
102 // regardless of readiness.
88 size_t txn_timeout_ms; 103 size_t txn_timeout_ms;
89 list_t *transactions; 104
105 // Stores a transaction after it has been committed, but is waiting for
106 // views to ack the new dimensions before being applied. A queued
107 // transaction is frozen and must not have new instructions added to it.
108 struct sway_transaction *queued_transaction;
109
110 // Stores a pending transaction that will be committed once the existing
111 // queued transaction is applied and freed. The pending transaction can be
112 // updated with new instructions as needed.
113 struct sway_transaction *pending_transaction;
114
115 // Stores the nodes that have been marked as "dirty" and will be put into
116 // the pending transaction.
90 list_t *dirty_nodes; 117 list_t *dirty_nodes;
91}; 118};
92 119
@@ -96,6 +123,7 @@ struct sway_debug {
96 bool noatomic; // Ignore atomic layout updates 123 bool noatomic; // Ignore atomic layout updates
97 bool txn_timings; // Log verbose messages about transactions 124 bool txn_timings; // Log verbose messages about transactions
98 bool txn_wait; // Always wait for the timeout before applying 125 bool txn_wait; // Always wait for the timeout before applying
126 bool noscanout; // Disable direct scan-out
99 127
100 enum { 128 enum {
101 DAMAGE_DEFAULT, // Default behaviour 129 DAMAGE_DEFAULT, // Default behaviour
@@ -113,6 +141,8 @@ void server_fini(struct sway_server *server);
113bool server_start(struct sway_server *server); 141bool server_start(struct sway_server *server);
114void server_run(struct sway_server *server); 142void server_run(struct sway_server *server);
115 143
144void restore_nofile_limit(void);
145
116void handle_compositor_new_surface(struct wl_listener *listener, void *data); 146void handle_compositor_new_surface(struct wl_listener *listener, void *data);
117void handle_new_output(struct wl_listener *listener, void *data); 147void handle_new_output(struct wl_listener *listener, void *data);
118 148
@@ -125,5 +155,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data);
125void handle_server_decoration(struct wl_listener *listener, void *data); 155void handle_server_decoration(struct wl_listener *listener, void *data);
126void handle_xdg_decoration(struct wl_listener *listener, void *data); 156void handle_xdg_decoration(struct wl_listener *listener, void *data);
127void handle_pointer_constraint(struct wl_listener *listener, void *data); 157void handle_pointer_constraint(struct wl_listener *listener, void *data);
158void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
159 void *data);
128 160
129#endif 161#endif
diff --git a/include/sway/surface.h b/include/sway/surface.h
index 4da96c02..fb1cd775 100644
--- a/include/sway/surface.h
+++ b/include/sway/surface.h
@@ -1,6 +1,6 @@
1#ifndef _SWAY_SURFACE_H 1#ifndef _SWAY_SURFACE_H
2#define _SWAY_SURFACE_H 2#define _SWAY_SURFACE_H
3#include <wlr/types/wlr_surface.h> 3#include <wlr/types/wlr_compositor.h>
4 4
5struct sway_surface { 5struct sway_surface {
6 struct wlr_surface *wlr_surface; 6 struct wlr_surface *wlr_surface;
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 7e9df59f..a5f74de9 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -2,8 +2,7 @@
2#define _SWAY_CONTAINER_H 2#define _SWAY_CONTAINER_H
3#include <stdint.h> 3#include <stdint.h>
4#include <sys/types.h> 4#include <sys/types.h>
5#include <wlr/types/wlr_box.h> 5#include <wlr/types/wlr_compositor.h>
6#include <wlr/types/wlr_surface.h>
7#include "list.h" 6#include "list.h"
8#include "sway/tree/node.h" 7#include "sway/tree/node.h"
9 8
@@ -46,9 +45,9 @@ struct sway_container_state {
46 45
47 enum sway_fullscreen_mode fullscreen_mode; 46 enum sway_fullscreen_mode fullscreen_mode;
48 47
49 struct sway_workspace *workspace; 48 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
50 struct sway_container *parent; 49 struct sway_container *parent; // NULL if container in root of workspace
51 list_t *children; 50 list_t *children; // struct sway_container
52 51
53 struct sway_container *focused_inactive_child; 52 struct sway_container *focused_inactive_child;
54 bool focused; 53 bool focused;
@@ -60,6 +59,7 @@ struct sway_container_state {
60 bool border_left; 59 bool border_left;
61 bool border_right; 60 bool border_right;
62 61
62 // These are in layout coordinates.
63 double content_x, content_y; 63 double content_x, content_y;
64 double content_width, content_height; 64 double content_width, content_height;
65}; 65};
@@ -68,14 +68,12 @@ struct sway_container {
68 struct sway_node node; 68 struct sway_node node;
69 struct sway_view *view; 69 struct sway_view *view;
70 70
71 // The pending state is the main container properties, and the current state is in the below struct.
72 // This means most places of the code can refer to the main variables (pending state) and it'll just work.
73 struct sway_container_state current; 71 struct sway_container_state current;
72 struct sway_container_state pending;
74 73
75 char *title; // The view's title (unformatted) 74 char *title; // The view's title (unformatted)
76 char *formatted_title; // The title displayed in the title bar 75 char *formatted_title; // The title displayed in the title bar
77 76
78 enum sway_container_layout layout;
79 enum sway_container_layout prev_split_layout; 77 enum sway_container_layout prev_split_layout;
80 78
81 // Whether stickiness has been enabled on this container. Use 79 // Whether stickiness has been enabled on this container. Use
@@ -86,11 +84,13 @@ struct sway_container {
86 // For C_ROOT, this has no meaning 84 // For C_ROOT, this has no meaning
87 // For other types, this is the position in layout coordinates 85 // For other types, this is the position in layout coordinates
88 // Includes borders 86 // Includes borders
89 double x, y;
90 double width, height;
91 double saved_x, saved_y; 87 double saved_x, saved_y;
92 double saved_width, saved_height; 88 double saved_width, saved_height;
93 89
90 // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD
91 // border which we use to restore when the view returns to SSD.
92 enum sway_container_border saved_border;
93
94 // The share of the space of parent container this container occupies 94 // The share of the space of parent container this container occupies
95 double width_fraction; 95 double width_fraction;
96 double height_fraction; 96 double height_fraction;
@@ -100,33 +100,11 @@ struct sway_container {
100 double child_total_width; 100 double child_total_width;
101 double child_total_height; 101 double child_total_height;
102 102
103 // These are in layout coordinates.
104 double content_x, content_y;
105 int content_width, content_height;
106
107 // In most cases this is the same as the content x and y, but if the view 103 // In most cases this is the same as the content x and y, but if the view
108 // refuses to resize to the content dimensions then it can be smaller. 104 // refuses to resize to the content dimensions then it can be smaller.
109 // These are in layout coordinates. 105 // These are in layout coordinates.
110 double surface_x, surface_y; 106 double surface_x, surface_y;
111 107
112 enum sway_fullscreen_mode fullscreen_mode;
113
114 enum sway_container_border border;
115
116 // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD
117 // border which we use to restore when the view returns to SSD.
118 enum sway_container_border saved_border;
119
120 int border_thickness;
121 bool border_top;
122 bool border_bottom;
123 bool border_left;
124 bool border_right;
125
126 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
127 struct sway_container *parent; // NULL if container in root of workspace
128 list_t *children; // struct sway_container
129
130 // Outputs currently being intersected 108 // Outputs currently being intersected
131 list_t *outputs; // struct sway_output 109 list_t *outputs; // struct sway_output
132 110
@@ -139,14 +117,14 @@ struct sway_container {
139 117
140 struct wlr_texture *title_focused; 118 struct wlr_texture *title_focused;
141 struct wlr_texture *title_focused_inactive; 119 struct wlr_texture *title_focused_inactive;
120 struct wlr_texture *title_focused_tab_title;
142 struct wlr_texture *title_unfocused; 121 struct wlr_texture *title_unfocused;
143 struct wlr_texture *title_urgent; 122 struct wlr_texture *title_urgent;
144 size_t title_height;
145 size_t title_baseline;
146 123
147 list_t *marks; // char * 124 list_t *marks; // char *
148 struct wlr_texture *marks_focused; 125 struct wlr_texture *marks_focused;
149 struct wlr_texture *marks_focused_inactive; 126 struct wlr_texture *marks_focused_inactive;
127 struct wlr_texture *marks_focused_tab_title;
150 struct wlr_texture *marks_unfocused; 128 struct wlr_texture *marks_unfocused;
151 struct wlr_texture *marks_urgent; 129 struct wlr_texture *marks_urgent;
152 130
@@ -185,6 +163,11 @@ void container_for_each_child(struct sway_container *container,
185 void (*f)(struct sway_container *container, void *data), void *data); 163 void (*f)(struct sway_container *container, void *data), void *data);
186 164
187/** 165/**
166 * Returns the fullscreen container obstructing this container if it exists.
167 */
168struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container);
169
170/**
188 * Returns true if the given container is an ancestor of this container. 171 * Returns true if the given container is an ancestor of this container.
189 */ 172 */
190bool container_has_ancestor(struct sway_container *container, 173bool container_has_ancestor(struct sway_container *container,
@@ -200,11 +183,6 @@ struct sway_container *container_flatten(struct sway_container *container);
200 183
201void container_update_title_textures(struct sway_container *container); 184void container_update_title_textures(struct sway_container *container);
202 185
203/**
204 * Calculate the container's title_height property.
205 */
206void container_calculate_title_height(struct sway_container *container);
207
208size_t container_build_representation(enum sway_container_layout layout, 186size_t container_build_representation(enum sway_container_layout layout,
209 list_t *children, char *buffer); 187 list_t *children, char *buffer);
210 188
@@ -231,10 +209,17 @@ void container_set_geometry_from_content(struct sway_container *con);
231/** 209/**
232 * Determine if the given container is itself floating. 210 * Determine if the given container is itself floating.
233 * This will return false for any descendants of a floating container. 211 * This will return false for any descendants of a floating container.
212 *
213 * Uses pending container state.
234 */ 214 */
235bool container_is_floating(struct sway_container *container); 215bool container_is_floating(struct sway_container *container);
236 216
237/** 217/**
218 * Same as above, but for current container state.
219 */
220bool container_is_current_floating(struct sway_container *container);
221
222/**
238 * Get a container's box in layout coordinates. 223 * Get a container's box in layout coordinates.
239 */ 224 */
240void container_get_box(struct sway_container *container, struct wlr_box *box); 225void container_get_box(struct sway_container *container, struct wlr_box *box);
@@ -299,6 +284,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container);
299/** 284/**
300 * Return the output which will be used for scale purposes. 285 * Return the output which will be used for scale purposes.
301 * This is the most recently entered output. 286 * This is the most recently entered output.
287 * If the container is not on any output, return NULL.
302 */ 288 */
303struct sway_output *container_get_effective_output(struct sway_container *con); 289struct sway_output *container_get_effective_output(struct sway_container *con);
304 290
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index e8f4d573..5d4a2f2d 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -31,7 +31,7 @@ struct sway_root {
31 list_t *scratchpad; // struct sway_container 31 list_t *scratchpad; // struct sway_container
32 32
33 // For when there's no connected outputs 33 // For when there's no connected outputs
34 struct sway_output *noop_output; 34 struct sway_output *fallback_output;
35 35
36 struct sway_container *fullscreen_global; 36 struct sway_container *fullscreen_global;
37 37
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index e071e6c9..789a67c0 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -1,7 +1,7 @@
1#ifndef _SWAY_VIEW_H 1#ifndef _SWAY_VIEW_H
2#define _SWAY_VIEW_H 2#define _SWAY_VIEW_H
3#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_compositor.h>
5#include "config.h" 5#include "config.h"
6#if HAVE_XWAYLAND 6#if HAVE_XWAYLAND
7#include <wlr/xwayland.h> 7#include <wlr/xwayland.h>
@@ -112,7 +112,6 @@ struct sway_view {
112#if HAVE_XWAYLAND 112#if HAVE_XWAYLAND
113 struct wlr_xwayland_surface *wlr_xwayland_surface; 113 struct wlr_xwayland_surface *wlr_xwayland_surface;
114#endif 114#endif
115 struct wlr_wl_shell_surface *wlr_wl_shell_surface;
116 }; 115 };
117 116
118 struct { 117 struct {
@@ -132,7 +131,6 @@ struct sway_xdg_shell_view {
132 struct wl_listener commit; 131 struct wl_listener commit;
133 struct wl_listener request_move; 132 struct wl_listener request_move;
134 struct wl_listener request_resize; 133 struct wl_listener request_resize;
135 struct wl_listener request_maximize;
136 struct wl_listener request_fullscreen; 134 struct wl_listener request_fullscreen;
137 struct wl_listener set_title; 135 struct wl_listener set_title;
138 struct wl_listener set_app_id; 136 struct wl_listener set_app_id;
@@ -184,7 +182,7 @@ struct sway_xwayland_unmanaged {
184struct sway_view_child; 182struct sway_view_child;
185 183
186struct sway_view_child_impl { 184struct sway_view_child_impl {
187 void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy); 185 void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy);
188 void (*destroy)(struct sway_view_child *child); 186 void (*destroy)(struct sway_view_child *child);
189}; 187};
190 188
@@ -311,12 +309,22 @@ void view_destroy(struct sway_view *view);
311 309
312void view_begin_destroy(struct sway_view *view); 310void view_begin_destroy(struct sway_view *view);
313 311
312/**
313 * Map a view, ie. make it visible in the tree.
314 *
315 * `fullscreen` should be set to true (and optionally `fullscreen_output`
316 * should be populated) if the view should be made fullscreen immediately.
317 *
318 * `decoration` should be set to true if the client prefers CSD. The client's
319 * preference may be ignored.
320 */
314void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, 321void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
315 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); 322 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
316 323
317void view_unmap(struct sway_view *view); 324void view_unmap(struct sway_view *view);
318 325
319void view_update_size(struct sway_view *view, int width, int height); 326void view_update_size(struct sway_view *view);
327void view_center_surface(struct sway_view *view);
320 328
321void view_child_init(struct sway_view_child *child, 329void view_child_init(struct sway_view_child *child,
322 const struct sway_view_child_impl *impl, struct sway_view *view, 330 const struct sway_view_child_impl *impl, struct sway_view *view,
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index fdd92f64..b3d93a81 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -60,20 +60,20 @@ void workspace_consider_destroy(struct sway_workspace *ws);
60 60
61char *workspace_next_name(const char *output_name); 61char *workspace_next_name(const char *output_name);
62 62
63bool workspace_switch(struct sway_workspace *workspace, 63struct sway_workspace *workspace_auto_back_and_forth(
64 bool no_auto_back_and_forth); 64 struct sway_workspace *workspace);
65
66bool workspace_switch(struct sway_workspace *workspace);
65 67
66struct sway_workspace *workspace_by_number(const char* name); 68struct sway_workspace *workspace_by_number(const char* name);
67 69
68struct sway_workspace *workspace_by_name(const char*); 70struct sway_workspace *workspace_by_name(const char*);
69 71
70struct sway_workspace *workspace_output_next( 72struct sway_workspace *workspace_output_next(struct sway_workspace *current);
71 struct sway_workspace *current, bool create);
72 73
73struct sway_workspace *workspace_next(struct sway_workspace *current); 74struct sway_workspace *workspace_next(struct sway_workspace *current);
74 75
75struct sway_workspace *workspace_output_prev( 76struct sway_workspace *workspace_output_prev(struct sway_workspace *current);
76 struct sway_workspace *current, bool create);
77 77
78struct sway_workspace *workspace_prev(struct sway_workspace *current); 78struct sway_workspace *workspace_prev(struct sway_workspace *current);
79 79
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h
index df8cdd09..1aec6d6c 100644
--- a/include/swaybar/i3bar.h
+++ b/include/swaybar/i3bar.h
@@ -19,6 +19,7 @@ struct i3bar_block {
19 // Airblader features 19 // Airblader features
20 uint32_t background; 20 uint32_t background;
21 uint32_t border; 21 uint32_t border;
22 bool border_set;
22 int border_top; 23 int border_top;
23 int border_bottom; 24 int border_bottom;
24 int border_left; 25 int border_left;
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h
index 9e39e716..baa6ee8b 100644
--- a/include/swaynag/swaynag.h
+++ b/include/swaynag/swaynag.h
@@ -5,7 +5,6 @@
5#include "list.h" 5#include "list.h"
6#include "pool-buffer.h" 6#include "pool-buffer.h"
7#include "swaynag/types.h" 7#include "swaynag/types.h"
8#include "xdg-output-unstable-v1-client-protocol.h"
9 8
10#define SWAYNAG_MAX_HEIGHT 500 9#define SWAYNAG_MAX_HEIGHT 500
11 10
@@ -75,13 +74,11 @@ struct swaynag_details {
75 74
76struct swaynag { 75struct swaynag {
77 bool run_display; 76 bool run_display;
78 int querying_outputs;
79 77
80 struct wl_display *display; 78 struct wl_display *display;
81 struct wl_compositor *compositor; 79 struct wl_compositor *compositor;
82 struct wl_seat *seat; 80 struct wl_seat *seat;
83 struct wl_shm *shm; 81 struct wl_shm *shm;
84 struct zxdg_output_manager_v1 *xdg_output_manager;
85 struct wl_list outputs; // swaynag_output::link 82 struct wl_list outputs; // swaynag_output::link
86 struct wl_list seats; // swaynag_seat::link 83 struct wl_list seats; // swaynag_seat::link
87 struct swaynag_output *output; 84 struct swaynag_output *output;
diff --git a/include/swaynag/types.h b/include/swaynag/types.h
index 24da9418..3c3b2754 100644
--- a/include/swaynag/types.h
+++ b/include/swaynag/types.h
@@ -7,6 +7,7 @@ struct swaynag_type {
7 char *font; 7 char *font;
8 char *output; 8 char *output;
9 uint32_t anchors; 9 uint32_t anchors;
10 int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset
10 11
11 // Colors 12 // Colors
12 uint32_t button_text; 13 uint32_t button_text;
diff --git a/include/util.h b/include/util.h
index c80da1cb..f887d489 100644
--- a/include/util.h
+++ b/include/util.h
@@ -30,12 +30,6 @@ int parse_movement_amount(int argc, char **argv,
30 struct movement_amount *amount); 30 struct movement_amount *amount);
31 31
32/** 32/**
33 * Get the current time, in milliseconds.
34 */
35
36uint32_t get_current_time_msec(void);
37
38/**
39 * Wrap i into the range [0, max] 33 * Wrap i into the range [0, max]
40 */ 34 */
41int wrap(int i, int max); 35int wrap(int i, int max);
diff --git a/meson.build b/meson.build
index 38a55678..71183007 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,9 @@
1project( 1project(
2 'sway', 2 'sway',
3 'c', 3 'c',
4 version: '1.5', #release_version 4 version: '1.8-dev',
5 license: 'MIT', 5 license: 'MIT',
6 meson_version: '>=0.53.0', 6 meson_version: '>=0.60.0',
7 default_options: [ 7 default_options: [
8 'c_std=c11', 8 'c_std=c11',
9 'warning_level=2', 9 'warning_level=2',
@@ -35,56 +35,49 @@ if is_freebsd
35 add_project_arguments('-D_C11_SOURCE', language: 'c') 35 add_project_arguments('-D_C11_SOURCE', language: 'c')
36endif 36endif
37 37
38jsonc = dependency('json-c', version: '>=0.13') 38# Execute the wlroots subproject, if any
39pcre = dependency('libpcre') 39wlroots_version = ['>=0.16.0', '<0.17.0']
40wayland_server = dependency('wayland-server') 40subproject(
41wayland_client = dependency('wayland-client')
42wayland_cursor = dependency('wayland-cursor')
43wayland_egl = dependency('wayland-egl')
44wayland_protos = dependency('wayland-protocols', version: '>=1.14')
45xkbcommon = dependency('xkbcommon')
46cairo = dependency('cairo')
47pango = dependency('pango')
48pangocairo = dependency('pangocairo')
49gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
50pixman = dependency('pixman-1')
51glesv2 = dependency('glesv2')
52libevdev = dependency('libevdev')
53libinput = dependency('libinput', version: '>=1.6.0')
54xcb = dependency('xcb', required: get_option('xwayland'))
55bash_comp = dependency('bash-completion', required: false)
56fish_comp = dependency('fish', required: false)
57math = cc.find_library('m')
58rt = cc.find_library('rt')
59
60# Try first to find wlroots as a subproject, then as a system dependency
61wlroots_version = ['>=0.12.0', '<0.13.0']
62wlroots_proj = subproject(
63 'wlroots', 41 'wlroots',
64 default_options: ['examples=false'], 42 default_options: ['examples=false'],
65 required: false, 43 required: false,
66 version: wlroots_version, 44 version: wlroots_version,
67) 45)
46
47jsonc = dependency('json-c', version: '>=0.13')
48pcre = dependency('libpcre')
49wayland_server = dependency('wayland-server', version: '>=1.20.0')
50wayland_client = dependency('wayland-client')
51wayland_cursor = dependency('wayland-cursor')
52wayland_egl = dependency('wayland-egl')
53wayland_protos = dependency('wayland-protocols', version: '>=1.24')
54wlroots = dependency('wlroots', version: wlroots_version)
55xkbcommon = dependency('xkbcommon')
56cairo = dependency('cairo')
57pango = dependency('pango')
58pangocairo = dependency('pangocairo')
59gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
60pixman = dependency('pixman-1')
61glesv2 = dependency('glesv2')
62libevdev = dependency('libevdev')
63libinput = dependency('libinput', version: '>=1.6.0')
64xcb = dependency('xcb', required: get_option('xwayland'))
65drm_full = dependency('libdrm') # only needed for drm_fourcc.h
66drm = drm_full.partial_dependency(compile_args: true, includes: true)
67libudev = dependency('libudev')
68bash_comp = dependency('bash-completion', required: false)
69fish_comp = dependency('fish', required: false)
70math = cc.find_library('m')
71rt = cc.find_library('rt')
72
68wlroots_features = { 73wlroots_features = {
69 'xwayland': false, 74 'xwayland': false,
70 'systemd': false,
71 'elogind': false,
72 'libseat': false,
73} 75}
74if wlroots_proj.found() 76foreach name, _ : wlroots_features
75 wlroots = wlroots_proj.get_variable('wlroots') 77 var_name = 'have_' + name.underscorify()
76 wlroots_conf = wlroots_proj.get_variable('conf_data') 78 have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
77 foreach name, _ : wlroots_features 79 wlroots_features += { name: have }
78 has = wlroots_conf.get('WLR_HAS_' + name.to_upper()) == 1 80endforeach
79 wlroots_features += { name: has }
80 endforeach
81else
82 wlroots = dependency('wlroots', version: wlroots_version)
83 foreach name, _ : wlroots_features
84 has = cc.get_define('WLR_HAS_' + name.to_upper(), prefix: '#include <wlr/config.h>', dependencies: wlroots) == '1'
85 wlroots_features += { name: has }
86 endforeach
87endif
88 81
89if get_option('xwayland').enabled() and not wlroots_features['xwayland'] 82if get_option('xwayland').enabled() and not wlroots_features['xwayland']
90 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') 83 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
@@ -95,19 +88,11 @@ if get_option('sd-bus-provider') == 'auto'
95 if not get_option('tray').disabled() 88 if not get_option('tray').disabled()
96 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') 89 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto')
97 endif 90 endif
98 sdbus = dependency('libsystemd', 91 sdbus = dependency(['libsystemd', 'libelogind'],
99 required: false, 92 required: false,
100 version: '>=239', 93 version: '>=239',
101 not_found_message: 'libsystemd not found, trying libelogind',
102 ) 94 )
103 if not sdbus.found() 95 if not sdbus.found()
104 sdbus = dependency('libelogind',
105 required: false,
106 version: '>=239',
107 not_found_message: 'libelogind not found, trying basu',
108 )
109 endif
110 if not sdbus.found()
111 sdbus = dependency('basu', required: false) 96 sdbus = dependency('basu', required: false)
112 endif 97 endif
113else 98else
@@ -131,8 +116,7 @@ conf_data.set10('HAVE_TRAY', have_tray)
131 116
132scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) 117scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
133if scdoc.found() 118if scdoc.found()
134 scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) 119 scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true)
135 sh = find_program('sh', native: true)
136 mandir = get_option('mandir') 120 mandir = get_option('mandir')
137 man_files = [ 121 man_files = [
138 'sway/sway.1.scd', 122 'sway/sway.1.scd',
@@ -143,9 +127,15 @@ if scdoc.found()
143 'sway/sway-output.5.scd', 127 'sway/sway-output.5.scd',
144 'swaybar/swaybar-protocol.7.scd', 128 'swaybar/swaybar-protocol.7.scd',
145 'swaymsg/swaymsg.1.scd', 129 'swaymsg/swaymsg.1.scd',
146 'swaynag/swaynag.1.scd',
147 'swaynag/swaynag.5.scd',
148 ] 130 ]
131
132 if get_option('swaynag')
133 man_files += [
134 'swaynag/swaynag.1.scd',
135 'swaynag/swaynag.5.scd',
136 ]
137 endif
138
149 foreach filename : man_files 139 foreach filename : man_files
150 topic = filename.split('.')[-3].split('/')[-1] 140 topic = filename.split('.')[-3].split('/')[-1]
151 section = filename.split('.')[-2] 141 section = filename.split('.')[-2]
@@ -155,10 +145,10 @@ if scdoc.found()
155 output, 145 output,
156 input: filename, 146 input: filename,
157 output: output, 147 output: output,
158 command: [ 148 command: scdoc_prog,
159 sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
160 ],
161 install: true, 149 install: true,
150 feed: true,
151 capture: true,
162 install_dir: '@0@/man@1@'.format(mandir, section) 152 install_dir: '@0@/man@1@'.format(mandir, section)
163 ) 153 )
164 endforeach 154 endforeach
@@ -169,8 +159,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir
169version = '"@0@"'.format(meson.project_version()) 159version = '"@0@"'.format(meson.project_version())
170git = find_program('git', native: true, required: false) 160git = find_program('git', native: true, required: false)
171if git.found() 161if git.found()
172 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) 162 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
173 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) 163 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
174 if git_commit.returncode() == 0 and git_branch.returncode() == 0 164 if git_commit.returncode() == 0 and git_branch.returncode() == 0
175 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( 165 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format(
176 meson.project_version(), 166 meson.project_version(),
@@ -183,7 +173,7 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
183 173
184# Compute the relative path used by compiler invocations. 174# Compute the relative path used by compiler invocations.
185source_root = meson.current_source_dir().split('/') 175source_root = meson.current_source_dir().split('/')
186build_root = meson.build_root().split('/') 176build_root = meson.global_build_root().split('/')
187relative_dir_parts = [] 177relative_dir_parts = []
188i = 0 178i = 0
189in_prefix = true 179in_prefix = true
@@ -227,9 +217,15 @@ subdir('common')
227subdir('sway') 217subdir('sway')
228subdir('swaymsg') 218subdir('swaymsg')
229 219
230subdir('client') 220if get_option('swaybar') or get_option('swaynag')
231subdir('swaybar') 221 subdir('client')
232subdir('swaynag') 222endif
223if get_option('swaybar')
224 subdir('swaybar')
225endif
226if get_option('swaynag')
227 subdir('swaynag')
228endif
233 229
234config = configuration_data() 230config = configuration_data()
235config.set('datadir', join_paths(prefix, datadir)) 231config.set('datadir', join_paths(prefix, datadir))
@@ -277,13 +273,17 @@ endif
277if get_option('bash-completions') 273if get_option('bash-completions')
278 bash_files = files( 274 bash_files = files(
279 'completions/bash/sway', 275 'completions/bash/sway',
280 'completions/bash/swaybar',
281 'completions/bash/swaymsg', 276 'completions/bash/swaymsg',
282 ) 277 )
278
279 if get_option('swaybar')
280 bash_files += files('completions/bash/swaybar')
281 endif
282
283 if bash_comp.found() 283 if bash_comp.found()
284 bash_install_dir = bash_comp.get_pkgconfig_variable( 284 bash_install_dir = bash_comp.get_variable(
285 'completionsdir', 285 pkgconfig: 'completionsdir',
286 define_variable: ['datadir', datadir] 286 pkgconfig_define: ['datadir', datadir]
287 ) 287 )
288 else 288 else
289 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') 289 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
@@ -296,12 +296,16 @@ if get_option('fish-completions')
296 fish_files = files( 296 fish_files = files(
297 'completions/fish/sway.fish', 297 'completions/fish/sway.fish',
298 'completions/fish/swaymsg.fish', 298 'completions/fish/swaymsg.fish',
299 'completions/fish/swaynag.fish',
300 ) 299 )
300
301 if get_option('swaynag')
302 fish_files += files('completions/fish/swaynag.fish')
303 endif
304
301 if fish_comp.found() 305 if fish_comp.found()
302 fish_install_dir = fish_comp.get_pkgconfig_variable( 306 fish_install_dir = fish_comp.get_variable(
303 'completionsdir', 307 pkgconfig: 'completionsdir',
304 define_variable: ['datadir', datadir] 308 pkgconfig_define: ['datadir', datadir]
305 ) 309 )
306 else 310 else
307 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') 311 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
@@ -313,12 +317,7 @@ endif
313summary({ 317summary({
314 'xwayland': have_xwayland, 318 'xwayland': have_xwayland,
315 'gdk-pixbuf': gdk_pixbuf.found(), 319 'gdk-pixbuf': gdk_pixbuf.found(),
316 'sd-bus': sdbus.found(),
317 'tray': have_tray, 320 'tray': have_tray,
318 'man-pages': scdoc.found(), 321 'man-pages': scdoc.found(),
319}, bool_yn: true) 322}, bool_yn: true)
320 323
321if not wlroots_features['systemd'] and not wlroots_features['elogind'] and not wlroots_features['libseat']
322 warning('The sway binary must be setuid when compiled without (e)logind or libseat')
323 warning('You must do this manually post-install: chmod a+s /path/to/sway')
324endif
diff --git a/meson_options.txt b/meson_options.txt
index e36900b6..6ba67554 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,6 +2,8 @@ option('default-wallpaper', type: 'boolean', value: true, description: 'Install
2option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') 2option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
3option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') 3option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')
4option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') 4option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.')
5option('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar')
6option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag')
5option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') 7option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
6option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') 8option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray')
7option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg') 9option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg')
diff --git a/protocols/meson.build b/protocols/meson.build
index 124e9777..df24a4e5 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -1,9 +1,9 @@
1wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') 1wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
2 2
3wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) 3wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true)
4if wayland_scanner_dep.found() 4if wayland_scanner_dep.found()
5 wayland_scanner = find_program( 5 wayland_scanner = find_program(
6 wayland_scanner_dep.get_pkgconfig_variable('wayland_scanner'), 6 wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
7 native: true, 7 native: true,
8 ) 8 )
9else 9else
@@ -15,6 +15,7 @@ protocols = [
15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], 16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
17 [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], 17 [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'],
18 [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
18 ['wlr-layer-shell-unstable-v1.xml'], 19 ['wlr-layer-shell-unstable-v1.xml'],
19 ['idle.xml'], 20 ['idle.xml'],
20 ['wlr-input-inhibitor-unstable-v1.xml'], 21 ['wlr-input-inhibitor-unstable-v1.xml'],
diff --git a/sway/commands.c b/sway/commands.c
index fe1e98b5..5a1fd32e 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -42,7 +42,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
42} 42}
43 43
44/* Keep alphabetized */ 44/* Keep alphabetized */
45static struct cmd_handler handlers[] = { 45static const struct cmd_handler handlers[] = {
46 { "assign", cmd_assign }, 46 { "assign", cmd_assign },
47 { "bar", cmd_bar }, 47 { "bar", cmd_bar },
48 { "bindcode", cmd_bindcode }, 48 { "bindcode", cmd_bindcode },
@@ -51,6 +51,7 @@ static struct cmd_handler handlers[] = {
51 { "client.background", cmd_client_noop }, 51 { "client.background", cmd_client_noop },
52 { "client.focused", cmd_client_focused }, 52 { "client.focused", cmd_client_focused },
53 { "client.focused_inactive", cmd_client_focused_inactive }, 53 { "client.focused_inactive", cmd_client_focused_inactive },
54 { "client.focused_tab_title", cmd_client_focused_tab_title },
54 { "client.placeholder", cmd_client_noop }, 55 { "client.placeholder", cmd_client_noop },
55 { "client.unfocused", cmd_client_unfocused }, 56 { "client.unfocused", cmd_client_unfocused },
56 { "client.urgent", cmd_client_urgent }, 57 { "client.urgent", cmd_client_urgent },
@@ -98,7 +99,7 @@ static struct cmd_handler handlers[] = {
98}; 99};
99 100
100/* Config-time only commands. Keep alphabetized */ 101/* Config-time only commands. Keep alphabetized */
101static struct cmd_handler config_handlers[] = { 102static const struct cmd_handler config_handlers[] = {
102 { "default_orientation", cmd_default_orientation }, 103 { "default_orientation", cmd_default_orientation },
103 { "include", cmd_include }, 104 { "include", cmd_include },
104 { "swaybg_command", cmd_swaybg_command }, 105 { "swaybg_command", cmd_swaybg_command },
@@ -108,7 +109,7 @@ static struct cmd_handler config_handlers[] = {
108}; 109};
109 110
110/* Runtime-only commands. Keep alphabetized */ 111/* Runtime-only commands. Keep alphabetized */
111static struct cmd_handler command_handlers[] = { 112static const struct cmd_handler command_handlers[] = {
112 { "border", cmd_border }, 113 { "border", cmd_border },
113 { "create_output", cmd_create_output }, 114 { "create_output", cmd_create_output },
114 { "exit", cmd_exit }, 115 { "exit", cmd_exit },
@@ -144,22 +145,22 @@ static int handler_compare(const void *_a, const void *_b) {
144 return strcasecmp(a->command, b->command); 145 return strcasecmp(a->command, b->command);
145} 146}
146 147
147struct cmd_handler *find_handler(char *line, struct cmd_handler *handlers, 148const struct cmd_handler *find_handler(char *line,
148 size_t handlers_size) { 149 const struct cmd_handler *handlers, size_t handlers_size) {
149 if (!handlers || !handlers_size) { 150 if (!handlers || !handlers_size) {
150 return NULL; 151 return NULL;
151 } 152 }
152 struct cmd_handler query = { .command = line }; 153 const struct cmd_handler query = { .command = line };
153 return bsearch(&query, handlers, 154 return bsearch(&query, handlers,
154 handlers_size / sizeof(struct cmd_handler), 155 handlers_size / sizeof(struct cmd_handler),
155 sizeof(struct cmd_handler), handler_compare); 156 sizeof(struct cmd_handler), handler_compare);
156} 157}
157 158
158static struct cmd_handler *find_handler_ex(char *line, 159static const struct cmd_handler *find_handler_ex(char *line,
159 struct cmd_handler *config_handlers, size_t config_handlers_size, 160 const struct cmd_handler *config_handlers, size_t config_handlers_size,
160 struct cmd_handler *command_handlers, size_t command_handlers_size, 161 const struct cmd_handler *command_handlers, size_t command_handlers_size,
161 struct cmd_handler *handlers, size_t handlers_size) { 162 const struct cmd_handler *handlers, size_t handlers_size) {
162 struct cmd_handler *handler = NULL; 163 const struct cmd_handler *handler = NULL;
163 if (config->reading) { 164 if (config->reading) {
164 handler = find_handler(line, config_handlers, config_handlers_size); 165 handler = find_handler(line, config_handlers, config_handlers_size);
165 } else if (config->active) { 166 } else if (config->active) {
@@ -168,16 +169,17 @@ static struct cmd_handler *find_handler_ex(char *line,
168 return handler ? handler : find_handler(line, handlers, handlers_size); 169 return handler ? handler : find_handler(line, handlers, handlers_size);
169} 170}
170 171
171static struct cmd_handler *find_core_handler(char *line) { 172static const struct cmd_handler *find_core_handler(char *line) {
172 return find_handler_ex(line, config_handlers, sizeof(config_handlers), 173 return find_handler_ex(line, config_handlers, sizeof(config_handlers),
173 command_handlers, sizeof(command_handlers), 174 command_handlers, sizeof(command_handlers),
174 handlers, sizeof(handlers)); 175 handlers, sizeof(handlers));
175} 176}
176 177
177static void set_config_node(struct sway_node *node) { 178static void set_config_node(struct sway_node *node, bool node_overridden) {
178 config->handler_context.node = node; 179 config->handler_context.node = node;
179 config->handler_context.container = NULL; 180 config->handler_context.container = NULL;
180 config->handler_context.workspace = NULL; 181 config->handler_context.workspace = NULL;
182 config->handler_context.node_overridden = node_overridden;
181 183
182 if (node == NULL) { 184 if (node == NULL) {
183 return; 185 return;
@@ -186,7 +188,7 @@ static void set_config_node(struct sway_node *node) {
186 switch (node->type) { 188 switch (node->type) {
187 case N_CONTAINER: 189 case N_CONTAINER:
188 config->handler_context.container = node->sway_container; 190 config->handler_context.container = node->sway_container;
189 config->handler_context.workspace = node->sway_container->workspace; 191 config->handler_context.workspace = node->sway_container->pending.workspace;
190 break; 192 break;
191 case N_WORKSPACE: 193 case N_WORKSPACE:
192 config->handler_context.workspace = node->sway_workspace; 194 config->handler_context.workspace = node->sway_workspace;
@@ -202,6 +204,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
202 char *cmd; 204 char *cmd;
203 char matched_delim = ';'; 205 char matched_delim = ';';
204 list_t *containers = NULL; 206 list_t *containers = NULL;
207 bool using_criteria = false;
205 208
206 if (seat == NULL) { 209 if (seat == NULL) {
207 // passing a NULL seat means we just pick the default seat 210 // passing a NULL seat means we just pick the default seat
@@ -225,7 +228,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
225 for (; isspace(*head); ++head) {} 228 for (; isspace(*head); ++head) {}
226 // Extract criteria (valid for this command list only). 229 // Extract criteria (valid for this command list only).
227 if (matched_delim == ';') { 230 if (matched_delim == ';') {
228 config->handler_context.using_criteria = false; 231 using_criteria = false;
229 if (*head == '[') { 232 if (*head == '[') {
230 char *error = NULL; 233 char *error = NULL;
231 struct criteria *criteria = criteria_parse(head, &error); 234 struct criteria *criteria = criteria_parse(head, &error);
@@ -239,7 +242,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
239 containers = criteria_get_containers(criteria); 242 containers = criteria_get_containers(criteria);
240 head += strlen(criteria->raw); 243 head += strlen(criteria->raw);
241 criteria_destroy(criteria); 244 criteria_destroy(criteria);
242 config->handler_context.using_criteria = true; 245 using_criteria = true;
243 // Skip leading whitespace 246 // Skip leading whitespace
244 for (; isspace(*head); ++head) {} 247 for (; isspace(*head); ++head) {}
245 } 248 }
@@ -265,7 +268,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
265 } 268 }
266 } 269 }
267 } 270 }
268 struct cmd_handler *handler = find_core_handler(argv[0]); 271 const struct cmd_handler *handler = find_core_handler(argv[0]);
269 if (!handler) { 272 if (!handler) {
270 list_add(res_list, cmd_results_new(CMD_INVALID, 273 list_add(res_list, cmd_results_new(CMD_INVALID,
271 "Unknown/invalid command '%s'", argv[0])); 274 "Unknown/invalid command '%s'", argv[0]));
@@ -278,11 +281,14 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
278 argv[i] = do_var_replacement(argv[i]); 281 argv[i] = do_var_replacement(argv[i]);
279 } 282 }
280 283
281 if (!config->handler_context.using_criteria) { 284
282 // The container or workspace which this command will run on. 285 if (!using_criteria) {
283 struct sway_node *node = con ? &con->node : 286 if (con) {
284 seat_get_focus_inactive(seat, &root->node); 287 set_config_node(&con->node, true);
285 set_config_node(node); 288 } else {
289 set_config_node(seat_get_focus_inactive(seat, &root->node),
290 false);
291 }
286 struct cmd_results *res = handler->handle(argc-1, argv+1); 292 struct cmd_results *res = handler->handle(argc-1, argv+1);
287 list_add(res_list, res); 293 list_add(res_list, res);
288 if (res->status == CMD_INVALID) { 294 if (res->status == CMD_INVALID) {
@@ -296,7 +302,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
296 struct cmd_results *fail_res = NULL; 302 struct cmd_results *fail_res = NULL;
297 for (int i = 0; i < containers->length; ++i) { 303 for (int i = 0; i < containers->length; ++i) {
298 struct sway_container *container = containers->items[i]; 304 struct sway_container *container = containers->items[i];
299 set_config_node(&container->node); 305 set_config_node(&container->node, true);
300 struct cmd_results *res = handler->handle(argc-1, argv+1); 306 struct cmd_results *res = handler->handle(argc-1, argv+1);
301 if (res->status == CMD_SUCCESS) { 307 if (res->status == CMD_SUCCESS) {
302 free_cmd_results(res); 308 free_cmd_results(res);
@@ -370,7 +376,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
370 376
371 // Determine the command handler 377 // Determine the command handler
372 sway_log(SWAY_INFO, "Config command: %s", exec); 378 sway_log(SWAY_INFO, "Config command: %s", exec);
373 struct cmd_handler *handler = find_core_handler(argv[0]); 379 const struct cmd_handler *handler = find_core_handler(argv[0]);
374 if (!handler || !handler->handle) { 380 if (!handler || !handler->handle) {
375 const char *error = handler 381 const char *error = handler
376 ? "Command '%s' is shimmed, but unimplemented" 382 ? "Command '%s' is shimmed, but unimplemented"
@@ -418,12 +424,12 @@ cleanup:
418} 424}
419 425
420struct cmd_results *config_subcommand(char **argv, int argc, 426struct cmd_results *config_subcommand(char **argv, int argc,
421 struct cmd_handler *handlers, size_t handlers_size) { 427 const struct cmd_handler *handlers, size_t handlers_size) {
422 char *command = join_args(argv, argc); 428 char *command = join_args(argv, argc);
423 sway_log(SWAY_DEBUG, "Subcommand: %s", command); 429 sway_log(SWAY_DEBUG, "Subcommand: %s", command);
424 free(command); 430 free(command);
425 431
426 struct cmd_handler *handler = find_handler(argv[0], handlers, 432 const struct cmd_handler *handler = find_handler(argv[0], handlers,
427 handlers_size); 433 handlers_size);
428 if (!handler) { 434 if (!handler) {
429 return cmd_results_new(CMD_INVALID, 435 return cmd_results_new(CMD_INVALID,
@@ -453,41 +459,13 @@ struct cmd_results *config_commands_command(char *exec) {
453 goto cleanup; 459 goto cleanup;
454 } 460 }
455 461
456 struct cmd_handler *handler = find_handler(cmd, NULL, 0); 462 const struct cmd_handler *handler = find_handler(cmd, NULL, 0);
457 if (!handler && strcmp(cmd, "*") != 0) { 463 if (!handler && strcmp(cmd, "*") != 0) {
458 results = cmd_results_new(CMD_INVALID, 464 results = cmd_results_new(CMD_INVALID,
459 "Unknown/invalid command '%s'", cmd); 465 "Unknown/invalid command '%s'", cmd);
460 goto cleanup; 466 goto cleanup;
461 } 467 }
462 468
463 enum command_context context = 0;
464
465 struct {
466 char *name;
467 enum command_context context;
468 } context_names[] = {
469 { "config", CONTEXT_CONFIG },
470 { "binding", CONTEXT_BINDING },
471 { "ipc", CONTEXT_IPC },
472 { "criteria", CONTEXT_CRITERIA },
473 { "all", CONTEXT_ALL },
474 };
475
476 for (int i = 1; i < argc; ++i) {
477 size_t j;
478 for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
479 if (strcmp(context_names[j].name, argv[i]) == 0) {
480 break;
481 }
482 }
483 if (j == sizeof(context_names) / sizeof(context_names[0])) {
484 results = cmd_results_new(CMD_INVALID,
485 "Invalid command context %s", argv[i]);
486 goto cleanup;
487 }
488 context |= context_names[j].context;
489 }
490
491 results = cmd_results_new(CMD_SUCCESS, NULL); 469 results = cmd_results_new(CMD_SUCCESS, NULL);
492 470
493cleanup: 471cleanup:
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index d42b7fc2..8571d282 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -8,7 +8,7 @@
8#include "log.h" 8#include "log.h"
9 9
10// Must be in alphabetical order for bsearch 10// Must be in alphabetical order for bsearch
11static struct cmd_handler bar_handlers[] = { 11static const struct cmd_handler bar_handlers[] = {
12 { "bindcode", bar_cmd_bindcode }, 12 { "bindcode", bar_cmd_bindcode },
13 { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, 13 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
14 { "bindsym", bar_cmd_bindsym }, 14 { "bindsym", bar_cmd_bindsym },
@@ -41,7 +41,7 @@ static struct cmd_handler bar_handlers[] = {
41}; 41};
42 42
43// Must be in alphabetical order for bsearch 43// Must be in alphabetical order for bsearch
44static struct cmd_handler bar_config_handlers[] = { 44static const struct cmd_handler bar_config_handlers[] = {
45 { "id", bar_cmd_id }, 45 { "id", bar_cmd_id },
46 { "swaybar_command", bar_cmd_swaybar_command }, 46 { "swaybar_command", bar_cmd_swaybar_command },
47}; 47};
@@ -116,6 +116,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
116 if (res && res->status != CMD_SUCCESS) { 116 if (res && res->status != CMD_SUCCESS) {
117 if (id) { 117 if (id) {
118 free_bar_config(config->current_bar); 118 free_bar_config(config->current_bar);
119 config->current_bar = NULL;
119 id = NULL; 120 id = NULL;
120 } 121 }
121 return res; 122 return res;
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 2d5b22bf..275fa3c6 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -4,7 +4,7 @@
4#include "util.h" 4#include "util.h"
5 5
6// Must be in alphabetical order for bsearch 6// Must be in alphabetical order for bsearch
7static struct cmd_handler bar_colors_handlers[] = { 7static const struct cmd_handler bar_colors_handlers[] = {
8 { "active_workspace", bar_colors_cmd_active_workspace }, 8 { "active_workspace", bar_colors_cmd_active_workspace },
9 { "background", bar_colors_cmd_background }, 9 { "background", bar_colors_cmd_background },
10 { "binding_mode", bar_colors_cmd_binding_mode }, 10 { "binding_mode", bar_colors_cmd_binding_mode },
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 62987f3e..891c87af 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -11,7 +11,20 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
11 } 11 }
12 char *font = join_args(argv, argc); 12 char *font = join_args(argv, argc);
13 free(config->current_bar->font); 13 free(config->current_bar->font);
14 config->current_bar->font = font; 14
15 if (strncmp(font, "pango:", 6) == 0) {
16 if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
17 config->current_bar->pango_markup = true;
18 }
19 config->current_bar->font = strdup(font + 6);
20 } else {
21 if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
22 config->current_bar->pango_markup = false;
23 }
24 config->current_bar->font = strdup(font);
25 }
26
27 free(font);
15 sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", 28 sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s",
16 config->current_bar->font, config->current_bar->id); 29 config->current_bar->font, config->current_bar->id);
17 return cmd_results_new(CMD_SUCCESS, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 1f08a5d2..8b661e3a 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -54,7 +54,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
54 } 54 }
55 55
56 const char *state = argv[0]; 56 const char *state = argv[0];
57 if (config->reading) { 57 if (config->current_bar) {
58 error = bar_set_hidden_state(config->current_bar, state); 58 error = bar_set_hidden_state(config->current_bar, state);
59 } else { 59 } else {
60 const char *id = argc == 2 ? argv[1] : NULL; 60 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 8b3fb275..7c2f423b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -58,7 +58,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
58 } 58 }
59 59
60 const char *mode = argv[0]; 60 const char *mode = argv[0];
61 if (config->reading) { 61 if (config->current_bar) {
62 error = bar_set_mode(config->current_bar, mode); 62 error = bar_set_mode(config->current_bar, mode);
63 } else { 63 } else {
64 const char *id = argc == 2 ? argv[1] : NULL; 64 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index f6e58d99..25be415e 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -8,6 +8,7 @@
8#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
9#include "sway/commands.h" 9#include "sway/commands.h"
10#include "sway/config.h" 10#include "sway/config.h"
11#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 12#include "sway/input/cursor.h"
12#include "sway/input/keyboard.h" 13#include "sway/input/keyboard.h"
13#include "sway/ipc-server.h" 14#include "sway/ipc-server.h"
@@ -559,8 +560,8 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
559 free_switch_binding(binding); 560 free_switch_binding(binding);
560 return cmd_results_new(CMD_FAILURE, 561 return cmd_results_new(CMD_FAILURE,
561 "Invalid %s command " 562 "Invalid %s command "
562 "(expected switch state: unknown state %d)", 563 "(expected switch state: unknown state %s)",
563 bindtype, split->items[0]); 564 bindtype, split->items[1]);
564 } 565 }
565 list_free_items_and_destroy(split); 566 list_free_items_and_destroy(split);
566 567
@@ -642,6 +643,8 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
642 if (success) { 643 if (success) {
643 ipc_event_binding(binding); 644 ipc_event_binding(binding);
644 } 645 }
646
647 transaction_commit_dirty();
645} 648}
646 649
647/** 650/**
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 647663ac..7818fc96 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -19,11 +19,11 @@ static void set_border(struct sway_container *con,
19 view_set_csd_from_server(con->view, false); 19 view_set_csd_from_server(con->view, false);
20 } else if (!con->view->using_csd && new_border == B_CSD) { 20 } else if (!con->view->using_csd && new_border == B_CSD) {
21 view_set_csd_from_server(con->view, true); 21 view_set_csd_from_server(con->view, true);
22 con->saved_border = con->border; 22 con->saved_border = con->pending.border;
23 } 23 }
24 } 24 }
25 if (new_border != B_CSD || container_is_floating(con)) { 25 if (new_border != B_CSD || container_is_floating(con)) {
26 con->border = new_border; 26 con->pending.border = new_border;
27 } 27 }
28 if (con->view) { 28 if (con->view) {
29 con->view->using_csd = new_border == B_CSD; 29 con->view->using_csd = new_border == B_CSD;
@@ -35,7 +35,7 @@ static void border_toggle(struct sway_container *con) {
35 set_border(con, B_NONE); 35 set_border(con, B_NONE);
36 return; 36 return;
37 } 37 }
38 switch (con->border) { 38 switch (con->pending.border) {
39 case B_NONE: 39 case B_NONE:
40 set_border(con, B_PIXEL); 40 set_border(con, B_PIXEL);
41 break; 41 break;
@@ -88,7 +88,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
88 "or 'border pixel <px>'"); 88 "or 'border pixel <px>'");
89 } 89 }
90 if (argc == 2) { 90 if (argc == 2) {
91 container->border_thickness = atoi(argv[1]); 91 container->pending.border_thickness = atoi(argv[1]);
92 } 92 }
93 93
94 if (container_is_floating(container)) { 94 if (container_is_floating(container)) {
diff --git a/sway/commands/client.c b/sway/commands/client.c
index dd0694df..77263145 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -18,6 +18,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
18 return error; 18 return error;
19 } 19 }
20 20
21 if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) {
22 sway_log(SWAY_ERROR,
23 "Warning: indicator and child_border colors have no effect for %s",
24 cmd_name);
25 }
26
21 struct border_colors colors = {0}; 27 struct border_colors colors = {0};
22 const char *ind_hex = argc > 3 ? argv[3] : default_indicator; 28 const char *ind_hex = argc > 3 ? argv[3] : default_indicator;
23 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background 29 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background
@@ -80,3 +86,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) {
80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); 86 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]);
81 return cmd_results_new(CMD_SUCCESS, NULL); 87 return cmd_results_new(CMD_SUCCESS, NULL);
82} 88}
89
90struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {
91 struct cmd_results *result = handle_command(argc, argv,
92 "client.focused_tab_title",
93 &config->border_colors.focused_tab_title, "#2e9ef4ff");
94 if (result && result->status == CMD_SUCCESS) {
95 config->has_focused_tab_title = true;
96 }
97 return result;
98}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 39e48a44..b35065c1 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -7,6 +7,7 @@
7#include <signal.h> 7#include <signal.h>
8#include "sway/commands.h" 8#include "sway/commands.h"
9#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/server.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/root.h" 12#include "sway/tree/root.h"
12#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -26,7 +27,7 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
26 27
27struct cmd_results *cmd_exec_process(int argc, char **argv) { 28struct cmd_results *cmd_exec_process(int argc, char **argv) {
28 struct cmd_results *error = NULL; 29 struct cmd_results *error = NULL;
29 char *tmp = NULL; 30 char *cmd = NULL;
30 if (strcmp(argv[0], "--no-startup-id") == 0) { 31 if (strcmp(argv[0], "--no-startup-id") == 0) {
31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 32 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored.");
32 --argc; ++argv; 33 --argc; ++argv;
@@ -36,17 +37,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
36 } 37 }
37 38
38 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { 39 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) {
39 tmp = strdup(argv[0]); 40 cmd = strdup(argv[0]);
40 strip_quotes(tmp); 41 strip_quotes(cmd);
41 } else { 42 } else {
42 tmp = join_args(argv, argc); 43 cmd = join_args(argv, argc);
43 } 44 }
44 45
45 // Put argument into cmd array
46 char cmd[4096];
47 strncpy(cmd, tmp, sizeof(cmd) - 1);
48 cmd[sizeof(cmd) - 1] = 0;
49 free(tmp);
50 sway_log(SWAY_DEBUG, "Executing %s", cmd); 46 sway_log(SWAY_DEBUG, "Executing %s", cmd);
51 47
52 int fd[2]; 48 int fd[2];
@@ -58,15 +54,18 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
58 // Fork process 54 // Fork process
59 if ((pid = fork()) == 0) { 55 if ((pid = fork()) == 0) {
60 // Fork child process again 56 // Fork child process again
57 restore_nofile_limit();
61 setsid(); 58 setsid();
62 sigset_t set; 59 sigset_t set;
63 sigemptyset(&set); 60 sigemptyset(&set);
64 sigprocmask(SIG_SETMASK, &set, NULL); 61 sigprocmask(SIG_SETMASK, &set, NULL);
62 signal(SIGPIPE, SIG_DFL);
65 close(fd[0]); 63 close(fd[0]);
66 if ((child = fork()) == 0) { 64 if ((child = fork()) == 0) {
67 close(fd[1]); 65 close(fd[1]);
68 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); 66 execlp("sh", "sh", "-c", cmd, (void *)NULL);
69 _exit(0); 67 sway_log_errno(SWAY_ERROR, "execlp failed");
68 _exit(1);
70 } 69 }
71 ssize_t s = 0; 70 ssize_t s = 0;
72 while ((size_t)s < sizeof(pid_t)) { 71 while ((size_t)s < sizeof(pid_t)) {
@@ -75,10 +74,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
75 close(fd[1]); 74 close(fd[1]);
76 _exit(0); // Close child process 75 _exit(0); // Close child process
77 } else if (pid < 0) { 76 } else if (pid < 0) {
77 free(cmd);
78 close(fd[0]); 78 close(fd[0]);
79 close(fd[1]); 79 close(fd[1]);
80 return cmd_results_new(CMD_FAILURE, "fork() failed"); 80 return cmd_results_new(CMD_FAILURE, "fork() failed");
81 } 81 }
82 free(cmd);
82 close(fd[1]); // close write 83 close(fd[1]); // close write
83 ssize_t s = 0; 84 ssize_t s = 0;
84 while ((size_t)s < sizeof(pid_t)) { 85 while ((size_t)s < sizeof(pid_t)) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index ce123345..74f6522c 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -40,8 +40,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
40 // If the container is in a floating split container, 40 // If the container is in a floating split container,
41 // operate on the split container instead of the child. 41 // operate on the split container instead of the child.
42 if (container_is_floating_or_child(container)) { 42 if (container_is_floating_or_child(container)) {
43 while (container->parent) { 43 while (container->pending.parent) {
44 container = container->parent; 44 container = container->pending.parent;
45 } 45 }
46 } 46 }
47 47
@@ -51,8 +51,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
51 container_set_floating(container, wants_floating); 51 container_set_floating(container, wants_floating);
52 52
53 // Floating containers in the scratchpad should be ignored 53 // Floating containers in the scratchpad should be ignored
54 if (container->workspace) { 54 if (container->pending.workspace) {
55 arrange_workspace(container->workspace); 55 arrange_workspace(container->pending.workspace);
56 } 56 }
57 57
58 return cmd_results_new(CMD_SUCCESS, NULL); 58 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 79b7aed5..2e8584c9 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container,
54 } else { 54 } else {
55 return false; 55 return false;
56 } 56 }
57 57
58 return true; 58 return true;
59} 59}
60 60
@@ -141,9 +141,9 @@ static struct sway_node *node_get_in_direction_tiling(
141 struct sway_container *wrap_candidate = NULL; 141 struct sway_container *wrap_candidate = NULL;
142 struct sway_container *current = container; 142 struct sway_container *current = container;
143 while (current) { 143 while (current) {
144 if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) { 144 if (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
145 // Fullscreen container with a direction - go straight to outputs 145 // Fullscreen container with a direction - go straight to outputs
146 struct sway_output *output = current->workspace->output; 146 struct sway_output *output = current->pending.workspace->output;
147 struct sway_output *new_output = 147 struct sway_output *new_output =
148 output_get_in_direction(output, dir); 148 output_get_in_direction(output, dir);
149 if (!new_output) { 149 if (!new_output) {
@@ -151,7 +151,7 @@ static struct sway_node *node_get_in_direction_tiling(
151 } 151 }
152 return get_node_in_output_direction(new_output, dir); 152 return get_node_in_output_direction(new_output, dir);
153 } 153 }
154 if (current->fullscreen_mode == FULLSCREEN_GLOBAL) { 154 if (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
155 return NULL; 155 return NULL;
156 } 156 }
157 157
@@ -202,11 +202,11 @@ static struct sway_node *node_get_in_direction_tiling(
202 } 202 }
203 } 203 }
204 204
205 current = current->parent; 205 current = current->pending.parent;
206 } 206 }
207 207
208 // Check a different output 208 // Check a different output
209 struct sway_output *output = container->workspace->output; 209 struct sway_output *output = container->pending.workspace->output;
210 struct sway_output *new_output = output_get_in_direction(output, dir); 210 struct sway_output *new_output = output_get_in_direction(output, dir);
211 if ((config->focus_wrapping != WRAP_WORKSPACE || 211 if ((config->focus_wrapping != WRAP_WORKSPACE ||
212 container->node.type == N_WORKSPACE) && new_output) { 212 container->node.type == N_WORKSPACE) && new_output) {
@@ -226,23 +226,23 @@ static struct sway_node *node_get_in_direction_tiling(
226static struct sway_node *node_get_in_direction_floating( 226static struct sway_node *node_get_in_direction_floating(
227 struct sway_container *con, struct sway_seat *seat, 227 struct sway_container *con, struct sway_seat *seat,
228 enum wlr_direction dir) { 228 enum wlr_direction dir) {
229 double ref_lx = con->x + con->width / 2; 229 double ref_lx = con->pending.x + con->pending.width / 2;
230 double ref_ly = con->y + con->height / 2; 230 double ref_ly = con->pending.y + con->pending.height / 2;
231 double closest_distance = DBL_MAX; 231 double closest_distance = DBL_MAX;
232 struct sway_container *closest_con = NULL; 232 struct sway_container *closest_con = NULL;
233 233
234 if (!con->workspace) { 234 if (!con->pending.workspace) {
235 return NULL; 235 return NULL;
236 } 236 }
237 237
238 for (int i = 0; i < con->workspace->floating->length; i++) { 238 for (int i = 0; i < con->pending.workspace->floating->length; i++) {
239 struct sway_container *floater = con->workspace->floating->items[i]; 239 struct sway_container *floater = con->pending.workspace->floating->items[i];
240 if (floater == con) { 240 if (floater == con) {
241 continue; 241 continue;
242 } 242 }
243 float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT 243 float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT
244 ? (floater->x + floater->width / 2) - ref_lx 244 ? (floater->pending.x + floater->pending.width / 2) - ref_lx
245 : (floater->y + floater->height / 2) - ref_ly; 245 : (floater->pending.y + floater->pending.height / 2) - ref_ly;
246 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { 246 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) {
247 distance = -distance; 247 distance = -distance;
248 } 248 }
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
267 new_focus = seat_get_focus_inactive_tiling(seat, ws); 267 new_focus = seat_get_focus_inactive_tiling(seat, ws);
268 } 268 }
269 if (new_focus) { 269 if (new_focus) {
270 struct sway_container *new_focus_view =
271 seat_get_focus_inactive_view(seat, &new_focus->node);
272 if (new_focus_view) {
273 new_focus = new_focus_view;
274 }
270 seat_set_focus_container(seat, new_focus); 275 seat_set_focus_container(seat, new_focus);
271 276
272 // If we're on the floating layer and the floating container area 277 // If we're on the floating layer and the floating container area
@@ -334,7 +339,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
334static struct cmd_results *focus_parent(void) { 339static struct cmd_results *focus_parent(void) {
335 struct sway_seat *seat = config->handler_context.seat; 340 struct sway_seat *seat = config->handler_context.seat;
336 struct sway_container *con = config->handler_context.container; 341 struct sway_container *con = config->handler_context.container;
337 if (!con || con->fullscreen_mode) { 342 if (!con || con->pending.fullscreen_mode) {
338 return cmd_results_new(CMD_SUCCESS, NULL); 343 return cmd_results_new(CMD_SUCCESS, NULL);
339 } 344 }
340 struct sway_node *parent = node_get_parent(&con->node); 345 struct sway_node *parent = node_get_parent(&con->node);
@@ -377,6 +382,13 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
377 if (container_is_scratchpad_hidden_or_child(container)) { 382 if (container_is_scratchpad_hidden_or_child(container)) {
378 root_scratchpad_show(container); 383 root_scratchpad_show(container);
379 } 384 }
385 // if we are switching to a container under a fullscreen window, we first
386 // need to exit fullscreen so that the newly focused container becomes visible
387 struct sway_container *obstructing = container_obstructing_fullscreen_container(container);
388 if (obstructing) {
389 container_fullscreen_disable(obstructing);
390 arrange_root();
391 }
380 seat_set_focus_container(seat, container); 392 seat_set_focus_container(seat, container);
381 seat_consider_warp_to_focus(seat); 393 seat_consider_warp_to_focus(seat);
382 container_raise_floating(container); 394 container_raise_floating(container);
@@ -439,7 +451,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
439 return cmd_results_new(CMD_FAILURE, ""); 451 return cmd_results_new(CMD_FAILURE, "");
440 } 452 }
441 struct sway_node *next_focus = NULL; 453 struct sway_node *next_focus = NULL;
442 if (container_is_floating(container)) { 454 if (container_is_floating(container) &&
455 container->pending.fullscreen_mode == FULLSCREEN_NONE) {
443 next_focus = node_get_in_direction_floating(container, seat, direction); 456 next_focus = node_get_in_direction_floating(container, seat, direction);
444 } else { 457 } else {
445 next_focus = node_get_in_direction_tiling(container, seat, direction, descend); 458 next_focus = node_get_in_direction_tiling(container, seat, direction, descend);
diff --git a/sway/commands/font.c b/sway/commands/font.c
index c54365b5..cea720f5 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -22,6 +22,6 @@ struct cmd_results *cmd_font(int argc, char **argv) {
22 } 22 }
23 23
24 free(font); 24 free(font);
25 config_update_font_height(true); 25 config_update_font_height();
26 return cmd_results_new(CMD_SUCCESS, NULL); 26 return cmd_results_new(CMD_SUCCESS, NULL);
27} 27}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 3392a7f7..21c1e9a0 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -18,30 +18,19 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
18 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
19 "Can't run this command while there's no outputs connected."); 19 "Can't run this command while there's no outputs connected.");
20 } 20 }
21 struct sway_node *node = config->handler_context.node;
22 struct sway_container *container = config->handler_context.container; 21 struct sway_container *container = config->handler_context.container;
23 struct sway_workspace *workspace = config->handler_context.workspace;
24 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
25 return cmd_results_new(CMD_FAILURE,
26 "Can't fullscreen an empty workspace");
27 }
28 22
29 // If in the scratchpad, operate on the highest container 23 if (!container) {
30 if (container && !container->workspace) { 24 // If the focus is not a container, do nothing successfully
31 while (container->parent) { 25 return cmd_results_new(CMD_SUCCESS, NULL);
32 container = container->parent; 26 } else if (!container->pending.workspace) {
33 } 27 // If in the scratchpad, operate on the highest container
34 } 28 while (container->pending.parent) {
35 29 container = container->pending.parent;
36 bool is_fullscreen = false;
37 for (struct sway_container *curr = container; curr; curr = curr->parent) {
38 if (curr->fullscreen_mode != FULLSCREEN_NONE) {
39 container = curr;
40 is_fullscreen = true;
41 break;
42 } 30 }
43 } 31 }
44 32
33 bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE;
45 bool global = false; 34 bool global = false;
46 bool enable = !is_fullscreen; 35 bool enable = !is_fullscreen;
47 36
@@ -57,13 +46,6 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
57 global = strcasecmp(argv[1], "global") == 0; 46 global = strcasecmp(argv[1], "global") == 0;
58 } 47 }
59 48
60 if (enable && node->type == N_WORKSPACE) {
61 // Wrap the workspace's children in a container so we can fullscreen it
62 container = workspace_wrap_children(workspace);
63 workspace->layout = L_HORIZ;
64 seat_set_focus_container(config->handler_context.seat, container);
65 }
66
67 enum sway_fullscreen_mode mode = FULLSCREEN_NONE; 49 enum sway_fullscreen_mode mode = FULLSCREEN_NONE;
68 if (enable) { 50 if (enable) {
69 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; 51 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE;
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 021df843..1deeb56e 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -11,7 +11,8 @@
11enum gaps_op { 11enum gaps_op {
12 GAPS_OP_SET, 12 GAPS_OP_SET,
13 GAPS_OP_ADD, 13 GAPS_OP_ADD,
14 GAPS_OP_SUBTRACT 14 GAPS_OP_SUBTRACT,
15 GAPS_OP_TOGGLE
15}; 16};
16 17
17struct gaps_data { 18struct gaps_data {
@@ -102,6 +103,9 @@ static void apply_gaps_op(int *prop, enum gaps_op op, int amount) {
102 case GAPS_OP_SUBTRACT: 103 case GAPS_OP_SUBTRACT:
103 *prop -= amount; 104 *prop -= amount;
104 break; 105 break;
106 case GAPS_OP_TOGGLE:
107 *prop = *prop ? 0 : amount;
108 break;
105 } 109 }
106} 110}
107 111
@@ -133,9 +137,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) {
133} 137}
134 138
135// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all 139// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all
136// set|plus|minus <px> 140// set|plus|minus|toggle <px>
137static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" 141static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|"
138 "top|right|bottom|left current|all set|plus|minus <px>'"; 142 "top|right|bottom|left current|all set|plus|minus|toggle <px>'";
139static struct cmd_results *gaps_set_runtime(int argc, char **argv) { 143static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
140 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 144 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
141 if (error) { 145 if (error) {
@@ -180,6 +184,8 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
180 data.operation = GAPS_OP_ADD; 184 data.operation = GAPS_OP_ADD;
181 } else if (strcasecmp(argv[2], "minus") == 0) { 185 } else if (strcasecmp(argv[2], "minus") == 0) {
182 data.operation = GAPS_OP_SUBTRACT; 186 data.operation = GAPS_OP_SUBTRACT;
187 } else if (strcasecmp(argv[2], "toggle") == 0) {
188 data.operation = GAPS_OP_TOGGLE;
183 } else { 189 } else {
184 return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); 190 return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime);
185 } 191 }
@@ -200,7 +206,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
200} 206}
201 207
202// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces 208// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces
203// gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only 209// gaps inner|outer|<dir>|<side> current|all set|plus|minus|toggle <px> - runtime only
204// <dir> = horizontal|vertical 210// <dir> = horizontal|vertical
205// <side> = top|right|bottom|left 211// <side> = top|right|bottom|left
206struct cmd_results *cmd_gaps(int argc, char **argv) { 212struct cmd_results *cmd_gaps(int argc, char **argv) {
diff --git a/sway/commands/input.c b/sway/commands/input.c
index c9bb8e06..77acb671 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -7,7 +7,7 @@
7#include "stringop.h" 7#include "stringop.h"
8 8
9// must be in order for the bsearch 9// must be in order for the bsearch
10static struct cmd_handler input_handlers[] = { 10static const struct cmd_handler input_handlers[] = {
11 { "accel_profile", input_cmd_accel_profile }, 11 { "accel_profile", input_cmd_accel_profile },
12 { "calibration_matrix", input_cmd_calibration_matrix }, 12 { "calibration_matrix", input_cmd_calibration_matrix },
13 { "click_method", input_cmd_click_method }, 13 { "click_method", input_cmd_click_method },
@@ -40,7 +40,7 @@ static struct cmd_handler input_handlers[] = {
40}; 40};
41 41
42// must be in order for the bsearch 42// must be in order for the bsearch
43static struct cmd_handler input_config_handlers[] = { 43static const struct cmd_handler input_config_handlers[] = {
44 { "xkb_capslock", input_cmd_xkb_capslock }, 44 { "xkb_capslock", input_cmd_xkb_capslock },
45 { "xkb_numlock", input_cmd_xkb_numlock }, 45 { "xkb_numlock", input_cmd_xkb_numlock },
46}; 46};
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c
index e85495e5..284b57d0 100644
--- a/sway/commands/input/map_to_region.c
+++ b/sway/commands/input/map_to_region.c
@@ -1,7 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wlr/types/wlr_box.h>
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/config.h" 5#include "sway/config.h"
7 6
diff --git a/sway/commands/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..1a05a7a6 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);
@@ -874,6 +874,10 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
874 return cmd_results_new(CMD_INVALID, "Invalid x position specified"); 874 return cmd_results_new(CMD_INVALID, "Invalid x position specified");
875 } 875 }
876 876
877 if (argc < 1) {
878 return cmd_results_new(CMD_FAILURE, expected_position_syntax);
879 }
880
877 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 881 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
878 // Y direction 882 // Y direction
879 num_consumed_args = parse_movement_amount(argc, argv, &ly); 883 num_consumed_args = parse_movement_amount(argc, argv, &ly);
@@ -886,7 +890,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
886 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 890 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
887 } 891 }
888 892
889 struct sway_workspace *ws = container->workspace; 893 struct sway_workspace *ws = container->pending.workspace;
890 if (!ws) { 894 if (!ws) {
891 struct sway_seat *seat = config->handler_context.seat; 895 struct sway_seat *seat = config->handler_context.seat;
892 ws = seat_get_focused_workspace(seat); 896 ws = seat_get_focused_workspace(seat);
@@ -960,14 +964,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) {
960 // If the container is in a floating split container, 964 // If the container is in a floating split container,
961 // operate on the split container instead of the child. 965 // operate on the split container instead of the child.
962 if (container_is_floating_or_child(con)) { 966 if (container_is_floating_or_child(con)) {
963 while (con->parent) { 967 while (con->pending.parent) {
964 con = con->parent; 968 con = con->pending.parent;
965 } 969 }
966 } 970 }
967 971
968 if (!con->scratchpad) { 972 if (!con->scratchpad) {
969 root_scratchpad_add_container(con, NULL); 973 root_scratchpad_add_container(con, NULL);
970 } else if (con->workspace) { 974 } else if (con->pending.workspace) {
971 root_scratchpad_hide(con); 975 root_scratchpad_hide(con);
972 } 976 }
973 return cmd_results_new(CMD_SUCCESS, NULL); 977 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 5186a2ba..125df5a7 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -6,7 +6,7 @@
6#include "log.h" 6#include "log.h"
7 7
8// must be in order for the bsearch 8// must be in order for the bsearch
9static struct cmd_handler output_handlers[] = { 9static const struct cmd_handler output_handlers[] = {
10 { "adaptive_sync", output_cmd_adaptive_sync }, 10 { "adaptive_sync", output_cmd_adaptive_sync },
11 { "background", output_cmd_background }, 11 { "background", output_cmd_background },
12 { "bg", output_cmd_background }, 12 { "bg", output_cmd_background },
@@ -15,8 +15,10 @@ static struct cmd_handler output_handlers[] = {
15 { "enable", output_cmd_enable }, 15 { "enable", output_cmd_enable },
16 { "max_render_time", output_cmd_max_render_time }, 16 { "max_render_time", output_cmd_max_render_time },
17 { "mode", output_cmd_mode }, 17 { "mode", output_cmd_mode },
18 { "modeline", output_cmd_modeline },
18 { "pos", output_cmd_position }, 19 { "pos", output_cmd_position },
19 { "position", output_cmd_position }, 20 { "position", output_cmd_position },
21 { "render_bit_depth", output_cmd_render_bit_depth },
20 { "res", output_cmd_mode }, 22 { "res", output_cmd_mode },
21 { "resolution", output_cmd_mode }, 23 { "resolution", output_cmd_mode },
22 { "scale", output_cmd_scale }, 24 { "scale", output_cmd_scale },
@@ -32,9 +34,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
32 return error; 34 return error;
33 } 35 }
34 36
35 // The NOOP-1 output is a dummy output used when there's no outputs 37 // The HEADLESS-1 output is a dummy output used when there's no outputs
36 // connected. It should never be configured. 38 // connected. It should never be configured.
37 if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { 39 if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
38 return cmd_results_new(CMD_FAILURE, 40 return cmd_results_new(CMD_FAILURE,
39 "Refusing to configure the no op output"); 41 "Refusing to configure the no op output");
40 } 42 }
@@ -51,7 +53,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
51 if (!sway_output) { 53 if (!sway_output) {
52 return cmd_results_new(CMD_FAILURE, "Unknown output"); 54 return cmd_results_new(CMD_FAILURE, "Unknown output");
53 } 55 }
54 if (sway_output == root->noop_output) { 56 if (sway_output == root->fallback_output) {
55 return cmd_results_new(CMD_FAILURE, 57 return cmd_results_new(CMD_FAILURE,
56 "Refusing to configure the no op output"); 58 "Refusing to configure the no op output");
57 } 59 }
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 9d75a80e..638c0ade 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,6 +1,8 @@
1#include "sway/commands.h" 1#include "sway/commands.h"
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/output.h"
3#include "util.h" 4#include "util.h"
5#include <strings.h>
4 6
5struct cmd_results *output_cmd_dpms(int argc, char **argv) { 7struct cmd_results *output_cmd_dpms(int argc, char **argv) {
6 if (!config->handler_context.output_config) { 8 if (!config->handler_context.output_config) {
@@ -10,7 +12,28 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) {
10 return cmd_results_new(CMD_INVALID, "Missing dpms argument."); 12 return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
11 } 13 }
12 14
13 if (parse_boolean(argv[0], true)) { 15 enum config_dpms current_dpms = DPMS_ON;
16
17 if (strcasecmp(argv[0], "toggle") == 0) {
18
19 const char *oc_name = config->handler_context.output_config->name;
20 if (strcmp(oc_name, "*") == 0) {
21 return cmd_results_new(CMD_INVALID,
22 "Cannot apply toggle to all outputs.");
23 }
24
25 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
26 if (!sway_output || !sway_output->wlr_output) {
27 return cmd_results_new(CMD_FAILURE,
28 "Cannot apply toggle to unknown output %s", oc_name);
29 }
30
31 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
32 current_dpms = DPMS_OFF;
33 }
34 }
35
36 if (parse_boolean(argv[0], current_dpms == DPMS_ON)) {
14 config->handler_context.output_config->dpms_state = DPMS_ON; 37 config->handler_context.output_config->dpms_state = DPMS_ON;
15 } else { 38 } else {
16 config->handler_context.output_config->dpms_state = DPMS_OFF; 39 config->handler_context.output_config->dpms_state = DPMS_OFF;
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c
index 5b710713..019d625a 100644
--- a/sway/commands/output/mode.c
+++ b/sway/commands/output/mode.c
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
20 output->custom_mode = 0; 20 output->custom_mode = 0;
21 } 21 }
22 22
23 // Reset custom modeline, if any
24 output->drm_mode.type = 0;
25
23 char *end; 26 char *end;
24 output->width = strtol(*argv, &end, 10); 27 output->width = strtol(*argv, &end, 10);
25 if (*end) { 28 if (*end) {
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
58 return NULL; 61 return NULL;
59} 62}
60 63
64static bool parse_modeline(char **argv, drmModeModeInfo *mode) {
65 mode->type = DRM_MODE_TYPE_USERDEF;
66 mode->clock = strtof(argv[0], NULL) * 1000;
67 mode->hdisplay = strtol(argv[1], NULL, 10);
68 mode->hsync_start = strtol(argv[2], NULL, 10);
69 mode->hsync_end = strtol(argv[3], NULL, 10);
70 mode->htotal = strtol(argv[4], NULL, 10);
71 mode->vdisplay = strtol(argv[5], NULL, 10);
72 mode->vsync_start = strtol(argv[6], NULL, 10);
73 mode->vsync_end = strtol(argv[7], NULL, 10);
74 mode->vtotal = strtol(argv[8], NULL, 10);
75
76 mode->vrefresh = mode->clock * 1000.0 * 1000.0
77 / mode->htotal / mode->vtotal;
78 if (strcasecmp(argv[9], "+hsync") == 0) {
79 mode->flags |= DRM_MODE_FLAG_PHSYNC;
80 } else if (strcasecmp(argv[9], "-hsync") == 0) {
81 mode->flags |= DRM_MODE_FLAG_NHSYNC;
82 } else {
83 return false;
84 }
85
86 if (strcasecmp(argv[10], "+vsync") == 0) {
87 mode->flags |= DRM_MODE_FLAG_PVSYNC;
88 } else if (strcasecmp(argv[10], "-vsync") == 0) {
89 mode->flags |= DRM_MODE_FLAG_NVSYNC;
90 } else {
91 return false;
92 }
93
94 snprintf(mode->name, sizeof(mode->name), "%dx%d@%d",
95 mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000);
96
97 return true;
98}
99
100struct cmd_results *output_cmd_modeline(int argc, char **argv) {
101 if (!config->handler_context.output_config) {
102 return cmd_results_new(CMD_FAILURE, "Missing output config");
103 }
104 if (!argc) {
105 return cmd_results_new(CMD_INVALID, "Missing modeline argument.");
106 }
107
108 struct output_config *output = config->handler_context.output_config;
109
110 if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) {
111 return cmd_results_new(CMD_INVALID, "Invalid modeline");
112 }
113
114 config->handler_context.leftovers.argc = argc - 12;
115 config->handler_context.leftovers.argv = argv + 12;
116 return NULL;
117}
118
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c
new file mode 100644
index 00000000..c419321e
--- /dev/null
+++ b/sway/commands/output/render_bit_depth.c
@@ -0,0 +1,29 @@
1#include <drm_fourcc.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5
6struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
7 if (!config->handler_context.output_config) {
8 return cmd_results_new(CMD_FAILURE, "Missing output config");
9 }
10 if (!argc) {
11 return cmd_results_new(CMD_INVALID, "Missing bit depth argument.");
12 }
13
14 if (strcmp(*argv, "8") == 0) {
15 config->handler_context.output_config->render_bit_depth =
16 RENDER_BIT_DEPTH_8;
17 } else if (strcmp(*argv, "10") == 0) {
18 config->handler_context.output_config->render_bit_depth =
19 RENDER_BIT_DEPTH_10;
20 } else {
21 return cmd_results_new(CMD_INVALID,
22 "Invalid bit depth. Must be a value in (8|10).");
23 }
24
25 config->handler_context.leftovers.argc = argc - 1;
26 config->handler_context.leftovers.argv = argv + 1;
27 return NULL;
28}
29
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 3c994d54..76f14bba 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -48,7 +48,6 @@ static void do_reload(void *data) {
48 } 48 }
49 list_free_items_and_destroy(bar_ids); 49 list_free_items_and_destroy(bar_ids);
50 50
51 config_update_font_height(true);
52 root_for_each_container(rebuild_textures_iterator, NULL); 51 root_for_each_container(rebuild_textures_iterator, NULL);
53 52
54 arrange_root(); 53 arrange_root();
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ca36e858..425069de 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -57,7 +57,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
57 (allow_last || index < siblings->length - 1)) { 57 (allow_last || index < siblings->length - 1)) {
58 return con; 58 return con;
59 } 59 }
60 con = con->parent; 60 con = con->pending.parent;
61 } 61 }
62 62
63 return NULL; 63 return NULL;
@@ -115,13 +115,13 @@ void container_resize_tiled(struct sway_container *con,
115 int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; 115 int sibling_amount = prev ? ceil((double)amount / 2.0) : amount;
116 116
117 if (is_horizontal(axis)) { 117 if (is_horizontal(axis)) {
118 if (con->width + amount < MIN_SANE_W) { 118 if (con->pending.width + amount < MIN_SANE_W) {
119 return; 119 return;
120 } 120 }
121 if (next->width - sibling_amount < MIN_SANE_W) { 121 if (next->pending.width - sibling_amount < MIN_SANE_W) {
122 return; 122 return;
123 } 123 }
124 if (prev && prev->width - sibling_amount < MIN_SANE_W) { 124 if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) {
125 return; 125 return;
126 } 126 }
127 if (con->child_total_width <= 0) { 127 if (con->child_total_width <= 0) {
@@ -133,7 +133,7 @@ void container_resize_tiled(struct sway_container *con,
133 list_t *siblings = container_get_siblings(con); 133 list_t *siblings = container_get_siblings(con);
134 for (int i = 0; i < siblings->length; ++i) { 134 for (int i = 0; i < siblings->length; ++i) {
135 struct sway_container *con = siblings->items[i]; 135 struct sway_container *con = siblings->items[i];
136 con->width_fraction = con->width / con->child_total_width; 136 con->width_fraction = con->pending.width / con->child_total_width;
137 } 137 }
138 138
139 double amount_fraction = (double)amount / con->child_total_width; 139 double amount_fraction = (double)amount / con->child_total_width;
@@ -146,13 +146,13 @@ void container_resize_tiled(struct sway_container *con,
146 prev->width_fraction -= sibling_amount_fraction; 146 prev->width_fraction -= sibling_amount_fraction;
147 } 147 }
148 } else { 148 } else {
149 if (con->height + amount < MIN_SANE_H) { 149 if (con->pending.height + amount < MIN_SANE_H) {
150 return; 150 return;
151 } 151 }
152 if (next->height - sibling_amount < MIN_SANE_H) { 152 if (next->pending.height - sibling_amount < MIN_SANE_H) {
153 return; 153 return;
154 } 154 }
155 if (prev && prev->height - sibling_amount < MIN_SANE_H) { 155 if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) {
156 return; 156 return;
157 } 157 }
158 if (con->child_total_height <= 0) { 158 if (con->child_total_height <= 0) {
@@ -164,7 +164,7 @@ void container_resize_tiled(struct sway_container *con,
164 list_t *siblings = container_get_siblings(con); 164 list_t *siblings = container_get_siblings(con);
165 for (int i = 0; i < siblings->length; ++i) { 165 for (int i = 0; i < siblings->length; ++i) {
166 struct sway_container *con = siblings->items[i]; 166 struct sway_container *con = siblings->items[i];
167 con->height_fraction = con->height / con->child_total_height; 167 con->height_fraction = con->pending.height / con->child_total_height;
168 } 168 }
169 169
170 double amount_fraction = (double)amount / con->child_total_height; 170 double amount_fraction = (double)amount / con->child_total_height;
@@ -178,10 +178,10 @@ void container_resize_tiled(struct sway_container *con,
178 } 178 }
179 } 179 }
180 180
181 if (con->parent) { 181 if (con->pending.parent) {
182 arrange_container(con->parent); 182 arrange_container(con->pending.parent);
183 } else { 183 } else {
184 arrange_workspace(con->workspace); 184 arrange_workspace(con->pending.workspace);
185 } 185 }
186} 186}
187 187
@@ -203,15 +203,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis,
203 int min_width, max_width, min_height, max_height; 203 int min_width, max_width, min_height, max_height;
204 floating_calculate_constraints(&min_width, &max_width, 204 floating_calculate_constraints(&min_width, &max_width,
205 &min_height, &max_height); 205 &min_height, &max_height);
206 if (con->width + grow_width < min_width) { 206 if (con->pending.width + grow_width < min_width) {
207 grow_width = min_width - con->width; 207 grow_width = min_width - con->pending.width;
208 } else if (con->width + grow_width > max_width) { 208 } else if (con->pending.width + grow_width > max_width) {
209 grow_width = max_width - con->width; 209 grow_width = max_width - con->pending.width;
210 } 210 }
211 if (con->height + grow_height < min_height) { 211 if (con->pending.height + grow_height < min_height) {
212 grow_height = min_height - con->height; 212 grow_height = min_height - con->pending.height;
213 } else if (con->height + grow_height > max_height) { 213 } else if (con->pending.height + grow_height > max_height) {
214 grow_height = max_height - con->height; 214 grow_height = max_height - con->pending.height;
215 } 215 }
216 int grow_x = 0, grow_y = 0; 216 int grow_x = 0, grow_y = 0;
217 217
@@ -227,15 +227,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis,
227 if (grow_width == 0 && grow_height == 0) { 227 if (grow_width == 0 && grow_height == 0) {
228 return cmd_results_new(CMD_INVALID, "Cannot resize any further"); 228 return cmd_results_new(CMD_INVALID, "Cannot resize any further");
229 } 229 }
230 con->x += grow_x; 230 con->pending.x += grow_x;
231 con->y += grow_y; 231 con->pending.y += grow_y;
232 con->width += grow_width; 232 con->pending.width += grow_width;
233 con->height += grow_height; 233 con->pending.height += grow_height;
234 234
235 con->content_x += grow_x; 235 con->pending.content_x += grow_x;
236 con->content_y += grow_y; 236 con->pending.content_y += grow_y;
237 con->content_width += grow_width; 237 con->pending.content_width += grow_width;
238 con->content_height += grow_height; 238 con->pending.content_height += grow_height;
239 239
240 arrange_container(con); 240 arrange_container(con);
241 241
@@ -256,9 +256,9 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
256 float pct = amount->amount / 100.0f; 256 float pct = amount->amount / 100.0f;
257 257
258 if (is_horizontal(axis)) { 258 if (is_horizontal(axis)) {
259 amount->amount = (float)current->width * pct; 259 amount->amount = (float)current->pending.width * pct;
260 } else { 260 } else {
261 amount->amount = (float)current->height * pct; 261 amount->amount = (float)current->pending.height * pct;
262 } 262 }
263 } 263 }
264 264
@@ -281,20 +281,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
281 if (width->unit == MOVEMENT_UNIT_PPT || 281 if (width->unit == MOVEMENT_UNIT_PPT ||
282 width->unit == MOVEMENT_UNIT_DEFAULT) { 282 width->unit == MOVEMENT_UNIT_DEFAULT) {
283 // Convert to px 283 // Convert to px
284 struct sway_container *parent = con->parent; 284 struct sway_container *parent = con->pending.parent;
285 while (parent && parent->layout != L_HORIZ) { 285 while (parent && parent->pending.layout != L_HORIZ) {
286 parent = parent->parent; 286 parent = parent->pending.parent;
287 } 287 }
288 if (parent) { 288 if (parent) {
289 width->amount = parent->width * width->amount / 100; 289 width->amount = parent->pending.width * width->amount / 100;
290 } else { 290 } else {
291 width->amount = con->workspace->width * width->amount / 100; 291 width->amount = con->pending.workspace->width * width->amount / 100;
292 } 292 }
293 width->unit = MOVEMENT_UNIT_PX; 293 width->unit = MOVEMENT_UNIT_PX;
294 } 294 }
295 if (width->unit == MOVEMENT_UNIT_PX) { 295 if (width->unit == MOVEMENT_UNIT_PX) {
296 container_resize_tiled(con, AXIS_HORIZONTAL, 296 container_resize_tiled(con, AXIS_HORIZONTAL,
297 width->amount - con->width); 297 width->amount - con->pending.width);
298 } 298 }
299 } 299 }
300 300
@@ -302,20 +302,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
302 if (height->unit == MOVEMENT_UNIT_PPT || 302 if (height->unit == MOVEMENT_UNIT_PPT ||
303 height->unit == MOVEMENT_UNIT_DEFAULT) { 303 height->unit == MOVEMENT_UNIT_DEFAULT) {
304 // Convert to px 304 // Convert to px
305 struct sway_container *parent = con->parent; 305 struct sway_container *parent = con->pending.parent;
306 while (parent && parent->layout != L_VERT) { 306 while (parent && parent->pending.layout != L_VERT) {
307 parent = parent->parent; 307 parent = parent->pending.parent;
308 } 308 }
309 if (parent) { 309 if (parent) {
310 height->amount = parent->height * height->amount / 100; 310 height->amount = parent->pending.height * height->amount / 100;
311 } else { 311 } else {
312 height->amount = con->workspace->height * height->amount / 100; 312 height->amount = con->pending.workspace->height * height->amount / 100;
313 } 313 }
314 height->unit = MOVEMENT_UNIT_PX; 314 height->unit = MOVEMENT_UNIT_PX;
315 } 315 }
316 if (height->unit == MOVEMENT_UNIT_PX) { 316 if (height->unit == MOVEMENT_UNIT_PX) {
317 container_resize_tiled(con, AXIS_VERTICAL, 317 container_resize_tiled(con, AXIS_VERTICAL,
318 height->amount - con->height); 318 height->amount - con->pending.height);
319 } 319 }
320 } 320 }
321 321
@@ -339,15 +339,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
339 "Cannot resize a hidden scratchpad container by ppt"); 339 "Cannot resize a hidden scratchpad container by ppt");
340 } 340 }
341 // Convert to px 341 // Convert to px
342 width->amount = con->workspace->width * width->amount / 100; 342 width->amount = con->pending.workspace->width * width->amount / 100;
343 width->unit = MOVEMENT_UNIT_PX; 343 width->unit = MOVEMENT_UNIT_PX;
344 // Falls through 344 // Falls through
345 case MOVEMENT_UNIT_PX: 345 case MOVEMENT_UNIT_PX:
346 case MOVEMENT_UNIT_DEFAULT: 346 case MOVEMENT_UNIT_DEFAULT:
347 width->amount = fmax(min_width, fmin(width->amount, max_width)); 347 width->amount = fmax(min_width, fmin(width->amount, max_width));
348 grow_width = width->amount - con->width; 348 grow_width = width->amount - con->pending.width;
349 con->x -= grow_width / 2; 349 con->pending.x -= grow_width / 2;
350 con->width = width->amount; 350 con->pending.width = width->amount;
351 break; 351 break;
352 case MOVEMENT_UNIT_INVALID: 352 case MOVEMENT_UNIT_INVALID:
353 sway_assert(false, "invalid width unit"); 353 sway_assert(false, "invalid width unit");
@@ -363,15 +363,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
363 "Cannot resize a hidden scratchpad container by ppt"); 363 "Cannot resize a hidden scratchpad container by ppt");
364 } 364 }
365 // Convert to px 365 // Convert to px
366 height->amount = con->workspace->height * height->amount / 100; 366 height->amount = con->pending.workspace->height * height->amount / 100;
367 height->unit = MOVEMENT_UNIT_PX; 367 height->unit = MOVEMENT_UNIT_PX;
368 // Falls through 368 // Falls through
369 case MOVEMENT_UNIT_PX: 369 case MOVEMENT_UNIT_PX:
370 case MOVEMENT_UNIT_DEFAULT: 370 case MOVEMENT_UNIT_DEFAULT:
371 height->amount = fmax(min_height, fmin(height->amount, max_height)); 371 height->amount = fmax(min_height, fmin(height->amount, max_height));
372 grow_height = height->amount - con->height; 372 grow_height = height->amount - con->pending.height;
373 con->y -= grow_height / 2; 373 con->pending.y -= grow_height / 2;
374 con->height = height->amount; 374 con->pending.height = height->amount;
375 break; 375 break;
376 case MOVEMENT_UNIT_INVALID: 376 case MOVEMENT_UNIT_INVALID:
377 sway_assert(false, "invalid height unit"); 377 sway_assert(false, "invalid height unit");
@@ -379,10 +379,10 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
379 } 379 }
380 } 380 }
381 381
382 con->content_x -= grow_width / 2; 382 con->pending.content_x -= grow_width / 2;
383 con->content_y -= grow_height / 2; 383 con->pending.content_y -= grow_height / 2;
384 con->content_width += grow_width; 384 con->pending.content_width += grow_width;
385 con->content_height += grow_height; 385 con->pending.content_height += grow_height;
386 386
387 arrange_container(con); 387 arrange_container(con);
388 388
@@ -437,10 +437,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
437 // If 0, don't resize that dimension 437 // If 0, don't resize that dimension
438 struct sway_container *con = config->handler_context.container; 438 struct sway_container *con = config->handler_context.container;
439 if (width.amount <= 0) { 439 if (width.amount <= 0) {
440 width.amount = con->width; 440 width.amount = con->pending.width;
441 } 441 }
442 if (height.amount <= 0) { 442 if (height.amount <= 0) {
443 height.amount = con->height; 443 height.amount = con->pending.height;
444 } 444 }
445 445
446 if (container_is_floating(con)) { 446 if (container_is_floating(con)) {
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
index 34871bc6..c995f2f0 100644
--- a/sway/commands/scratchpad.c
+++ b/sway/commands/scratchpad.c
@@ -21,8 +21,8 @@ static void scratchpad_toggle_auto(void) {
21 // If the focus is in a floating split container, 21 // If the focus is in a floating split container,
22 // operate on the split container instead of the child. 22 // operate on the split container instead of the child.
23 if (focus && container_is_floating_or_child(focus)) { 23 if (focus && container_is_floating_or_child(focus)) {
24 while (focus->parent) { 24 while (focus->pending.parent) {
25 focus = focus->parent; 25 focus = focus->pending.parent;
26 } 26 }
27 } 27 }
28 28
@@ -52,7 +52,7 @@ static void scratchpad_toggle_auto(void) {
52 // In this case we move it to the current workspace. 52 // In this case we move it to the current workspace.
53 for (int i = 0; i < root->scratchpad->length; ++i) { 53 for (int i = 0; i < root->scratchpad->length; ++i) {
54 struct sway_container *con = root->scratchpad->items[i]; 54 struct sway_container *con = root->scratchpad->items[i];
55 if (con->parent) { 55 if (con->pending.parent) {
56 sway_log(SWAY_DEBUG, 56 sway_log(SWAY_DEBUG,
57 "Moving a visible scratchpad window (%s) to this workspace", 57 "Moving a visible scratchpad window (%s) to this workspace",
58 con->title); 58 con->title);
@@ -80,7 +80,7 @@ static void scratchpad_toggle_container(struct sway_container *con) {
80 struct sway_seat *seat = input_manager_current_seat(); 80 struct sway_seat *seat = input_manager_current_seat();
81 struct sway_workspace *ws = seat_get_focused_workspace(seat); 81 struct sway_workspace *ws = seat_get_focused_workspace(seat);
82 // Check if it matches a currently visible scratchpad window and hide it. 82 // Check if it matches a currently visible scratchpad window and hide it.
83 if (con->workspace && ws == con->workspace) { 83 if (con->pending.workspace && ws == con->pending.workspace) {
84 root_scratchpad_hide(con); 84 root_scratchpad_hide(con);
85 return; 85 return;
86 } 86 }
@@ -105,21 +105,22 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
105 return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); 105 return cmd_results_new(CMD_INVALID, "Scratchpad is empty");
106 } 106 }
107 107
108 if (config->handler_context.using_criteria) { 108 if (config->handler_context.node_overridden) {
109 struct sway_container *con = config->handler_context.container; 109 struct sway_container *con = config->handler_context.container;
110 110
111 // If the container is in a floating split container, 111 // If the container is in a floating split container,
112 // operate on the split container instead of the child. 112 // operate on the split container instead of the child.
113 if (container_is_floating_or_child(con)) { 113 if (con && container_is_floating_or_child(con)) {
114 while (con->parent) { 114 while (con->pending.parent) {
115 con = con->parent; 115 con = con->pending.parent;
116 } 116 }
117 } 117 }
118 118
119 // If using criteria, this command is executed for every container which 119 // If using criteria, this command is executed for every container which
120 // matches the criteria. If this container isn't in the scratchpad, 120 // matches the criteria. If this container isn't in the scratchpad,
121 // we'll just silently return a success. 121 // we'll just silently return a success. The same is true if the
122 if (!con->scratchpad) { 122 // overridden node is not a container.
123 if (!con || !con->scratchpad) {
123 return cmd_results_new(CMD_SUCCESS, NULL); 124 return cmd_results_new(CMD_SUCCESS, NULL);
124 } 125 }
125 scratchpad_toggle_container(con); 126 scratchpad_toggle_container(con);
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 84c6ba53..2d197b69 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -8,13 +8,13 @@
8 8
9// must be in order for the bsearch 9// must be in order for the bsearch
10// these handlers perform actions on the seat 10// these handlers perform actions on the seat
11static struct cmd_handler seat_action_handlers[] = { 11static const struct cmd_handler seat_action_handlers[] = {
12 { "cursor", seat_cmd_cursor }, 12 { "cursor", seat_cmd_cursor },
13}; 13};
14 14
15// must be in order for the bsearch 15// must be in order for the bsearch
16// these handlers alter the seat config 16// these handlers alter the seat config
17static struct cmd_handler seat_handlers[] = { 17static const struct cmd_handler seat_handlers[] = {
18 { "attach", seat_cmd_attach }, 18 { "attach", seat_cmd_attach },
19 { "fallback", seat_cmd_fallback }, 19 { "fallback", seat_cmd_fallback },
20 { "hide_cursor", seat_cmd_hide_cursor }, 20 { "hide_cursor", seat_cmd_hide_cursor },
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 7615eef9..00bfdab6 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -12,7 +12,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) {
12 if (!config->handler_context.seat_config) { 12 if (!config->handler_context.seat_config) {
13 return cmd_results_new(CMD_FAILURE, "No seat defined"); 13 return cmd_results_new(CMD_FAILURE, "No seat defined");
14 } 14 }
15 if (config->reading) { 15 if (!config->active) {
16 return cmd_results_new(CMD_DEFER, NULL); 16 return cmd_results_new(CMD_DEFER, NULL);
17 } 17 }
18 18
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index b27f9ccd..a6d165dc 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
15 return error; 15 return error;
16 } 16 }
17 17
18 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); 18 if (strcmp(argv[0], "inverse_outer") == 0) {
19 config->smart_gaps = SMART_GAPS_INVERSE_OUTER;
20 } else {
21 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps)
22 ? SMART_GAPS_ON : SMART_GAPS_OFF;
23 }
19 24
20 arrange_root(); 25 arrange_root();
21 26
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 782bab02..c8a2cfc1 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -14,7 +14,7 @@ static struct cmd_results *do_split(int layout) {
14 struct sway_workspace *ws = config->handler_context.workspace; 14 struct sway_workspace *ws = config->handler_context.workspace;
15 if (con) { 15 if (con) {
16 if (container_is_scratchpad_hidden_or_child(con) && 16 if (container_is_scratchpad_hidden_or_child(con) &&
17 con->fullscreen_mode != FULLSCREEN_GLOBAL) { 17 con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
18 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
19 "Cannot split a hidden scratchpad container"); 19 "Cannot split a hidden scratchpad container");
20 } 20 }
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) {
32 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
33} 33}
34 34
35static struct cmd_results *do_unsplit() {
36 struct sway_container *con = config->handler_context.container;
37 struct sway_workspace *ws = config->handler_context.workspace;
38
39 if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) {
40 container_flatten(con->pending.parent);
41 } else {
42 return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings");
43 }
44
45 if (root->fullscreen_global) {
46 arrange_root();
47 } else {
48 arrange_workspace(ws);
49 }
50 return cmd_results_new(CMD_SUCCESS, NULL);
51}
52
35struct cmd_results *cmd_split(int argc, char **argv) { 53struct cmd_results *cmd_split(int argc, char **argv) {
36 struct cmd_results *error = NULL; 54 struct cmd_results *error = NULL;
37 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { 55 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
55 } else { 73 } else {
56 return do_split(L_VERT); 74 return do_split(L_VERT);
57 } 75 }
76 } else if (strcasecmp(argv[0], "n") == 0 ||
77 strcasecmp(argv[0], "none") == 0) {
78 return do_unsplit();
58 } else { 79 } else {
59 return cmd_results_new(CMD_FAILURE, 80 return cmd_results_new(CMD_FAILURE,
60 "Invalid split command (expected either horizontal or vertical)."); 81 "Invalid split command (expected either horizontal or vertical).");
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index 3c93a276..9b09a0f9 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -29,14 +29,14 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
29 !container_is_scratchpad_hidden(container)) { 29 !container_is_scratchpad_hidden(container)) {
30 // move container to active workspace 30 // move container to active workspace
31 struct sway_workspace *active_workspace = 31 struct sway_workspace *active_workspace =
32 output_get_active_workspace(container->workspace->output); 32 output_get_active_workspace(container->pending.workspace->output);
33 if (!sway_assert(active_workspace, 33 if (!sway_assert(active_workspace,
34 "Expected output to have a workspace")) { 34 "Expected output to have a workspace")) {
35 return cmd_results_new(CMD_FAILURE, 35 return cmd_results_new(CMD_FAILURE,
36 "Expected output to have a workspace"); 36 "Expected output to have a workspace");
37 } 37 }
38 if (container->workspace != active_workspace) { 38 if (container->pending.workspace != active_workspace) {
39 struct sway_workspace *old_workspace = container->workspace; 39 struct sway_workspace *old_workspace = container->pending.workspace;
40 container_detach(container); 40 container_detach(container);
41 workspace_add_floating(active_workspace, container); 41 workspace_add_floating(active_workspace, container);
42 container_handle_fullscreen_reparent(container); 42 container_handle_fullscreen_reparent(container);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index a7f9691b..ce5e5128 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -16,46 +16,46 @@ static const char expected_syntax[] =
16static void swap_places(struct sway_container *con1, 16static void swap_places(struct sway_container *con1,
17 struct sway_container *con2) { 17 struct sway_container *con2) {
18 struct sway_container *temp = malloc(sizeof(struct sway_container)); 18 struct sway_container *temp = malloc(sizeof(struct sway_container));
19 temp->x = con1->x; 19 temp->pending.x = con1->pending.x;
20 temp->y = con1->y; 20 temp->pending.y = con1->pending.y;
21 temp->width = con1->width; 21 temp->pending.width = con1->pending.width;
22 temp->height = con1->height; 22 temp->pending.height = con1->pending.height;
23 temp->width_fraction = con1->width_fraction; 23 temp->width_fraction = con1->width_fraction;
24 temp->height_fraction = con1->height_fraction; 24 temp->height_fraction = con1->height_fraction;
25 temp->parent = con1->parent; 25 temp->pending.parent = con1->pending.parent;
26 temp->workspace = con1->workspace; 26 temp->pending.workspace = con1->pending.workspace;
27 bool temp_floating = container_is_floating(con1); 27 bool temp_floating = container_is_floating(con1);
28 28
29 con1->x = con2->x; 29 con1->pending.x = con2->pending.x;
30 con1->y = con2->y; 30 con1->pending.y = con2->pending.y;
31 con1->width = con2->width; 31 con1->pending.width = con2->pending.width;
32 con1->height = con2->height; 32 con1->pending.height = con2->pending.height;
33 con1->width_fraction = con2->width_fraction; 33 con1->width_fraction = con2->width_fraction;
34 con1->height_fraction = con2->height_fraction; 34 con1->height_fraction = con2->height_fraction;
35 35
36 con2->x = temp->x; 36 con2->pending.x = temp->pending.x;
37 con2->y = temp->y; 37 con2->pending.y = temp->pending.y;
38 con2->width = temp->width; 38 con2->pending.width = temp->pending.width;
39 con2->height = temp->height; 39 con2->pending.height = temp->pending.height;
40 con2->width_fraction = temp->width_fraction; 40 con2->width_fraction = temp->width_fraction;
41 con2->height_fraction = temp->height_fraction; 41 con2->height_fraction = temp->height_fraction;
42 42
43 int temp_index = container_sibling_index(con1); 43 int temp_index = container_sibling_index(con1);
44 if (con2->parent) { 44 if (con2->pending.parent) {
45 container_insert_child(con2->parent, con1, 45 container_insert_child(con2->pending.parent, con1,
46 container_sibling_index(con2)); 46 container_sibling_index(con2));
47 } else if (container_is_floating(con2)) { 47 } else if (container_is_floating(con2)) {
48 workspace_add_floating(con2->workspace, con1); 48 workspace_add_floating(con2->pending.workspace, con1);
49 } else { 49 } else {
50 workspace_insert_tiling(con2->workspace, con1, 50 workspace_insert_tiling(con2->pending.workspace, con1,
51 container_sibling_index(con2)); 51 container_sibling_index(con2));
52 } 52 }
53 if (temp->parent) { 53 if (temp->pending.parent) {
54 container_insert_child(temp->parent, con2, temp_index); 54 container_insert_child(temp->pending.parent, con2, temp_index);
55 } else if (temp_floating) { 55 } else if (temp_floating) {
56 workspace_add_floating(temp->workspace, con2); 56 workspace_add_floating(temp->pending.workspace, con2);
57 } else { 57 } else {
58 workspace_insert_tiling(temp->workspace, con2, temp_index); 58 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
59 } 59 }
60 60
61 free(temp); 61 free(temp);
@@ -65,8 +65,8 @@ static void swap_focus(struct sway_container *con1,
65 struct sway_container *con2, struct sway_seat *seat, 65 struct sway_container *con2, struct sway_seat *seat,
66 struct sway_container *focus) { 66 struct sway_container *focus) {
67 if (focus == con1 || focus == con2) { 67 if (focus == con1 || focus == con2) {
68 struct sway_workspace *ws1 = con1->workspace; 68 struct sway_workspace *ws1 = con1->pending.workspace;
69 struct sway_workspace *ws2 = con2->workspace; 69 struct sway_workspace *ws2 = con2->pending.workspace;
70 enum sway_container_layout layout1 = container_parent_layout(con1); 70 enum sway_container_layout layout1 = container_parent_layout(con1);
71 enum sway_container_layout layout2 = container_parent_layout(con2); 71 enum sway_container_layout layout2 = container_parent_layout(con2);
72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { 72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
@@ -125,8 +125,8 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
125 root_scratchpad_remove_container(con2); 125 root_scratchpad_remove_container(con2);
126 } 126 }
127 127
128 enum sway_fullscreen_mode fs1 = con1->fullscreen_mode; 128 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
129 enum sway_fullscreen_mode fs2 = con2->fullscreen_mode; 129 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
130 if (fs1) { 130 if (fs1) {
131 container_fullscreen_disable(con1); 131 container_fullscreen_disable(con1);
132 } 132 }
@@ -137,9 +137,9 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
137 struct sway_seat *seat = config->handler_context.seat; 137 struct sway_seat *seat = config->handler_context.seat;
138 struct sway_container *focus = seat_get_focused_container(seat); 138 struct sway_container *focus = seat_get_focused_container(seat);
139 struct sway_workspace *vis1 = 139 struct sway_workspace *vis1 =
140 output_get_active_workspace(con1->workspace->output); 140 output_get_active_workspace(con1->pending.workspace->output);
141 struct sway_workspace *vis2 = 141 struct sway_workspace *vis2 =
142 output_get_active_workspace(con2->workspace->output); 142 output_get_active_workspace(con2->pending.workspace->output);
143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" 143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
144 "workspace. This should not happen")) { 144 "workspace. This should not happen")) {
145 return; 145 return;
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 9d312470..a2446b7e 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -23,6 +23,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
23 } 23 }
24 view->title_format = format; 24 view->title_format = format;
25 view_update_title(view, true); 25 view_update_title(view, true);
26 config_update_font_height(true);
27 return cmd_results_new(CMD_SUCCESS, NULL); 26 return cmd_results_new(CMD_SUCCESS, NULL);
28} 27}
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index cedfcfb2..19274dfb 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -21,7 +21,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) {
21struct cmd_results *cmd_unmark(int argc, char **argv) { 21struct cmd_results *cmd_unmark(int argc, char **argv) {
22 // Determine the container 22 // Determine the container
23 struct sway_container *con = NULL; 23 struct sway_container *con = NULL;
24 if (config->handler_context.using_criteria) { 24 if (config->handler_context.node_overridden) {
25 con = config->handler_context.container; 25 con = config->handler_context.container;
26 } 26 }
27 27
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 9ff1c97d..79f3667a 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -182,21 +182,16 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
182 "Can't switch workspaces while fullscreen global"); 182 "Can't switch workspaces while fullscreen global");
183 } 183 }
184 184
185 bool no_auto_back_and_forth = false; 185 bool auto_back_and_forth = true;
186 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { 186 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
187 no_auto_back_and_forth = true; 187 auto_back_and_forth = false;
188 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { 188 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) {
189 return error; 189 return error;
190 } 190 }
191 ++argv; 191 ++argv;
192 } 192 }
193 193
194 bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0;
195 struct sway_seat *seat = config->handler_context.seat; 194 struct sway_seat *seat = config->handler_context.seat;
196 struct sway_workspace *current = seat_get_focused_workspace(seat);
197 if (!current) {
198 return cmd_results_new(CMD_FAILURE, "No workspace to switch from");
199 }
200 195
201 struct sway_workspace *ws = NULL; 196 struct sway_workspace *ws = NULL;
202 if (strcasecmp(argv[0], "number") == 0) { 197 if (strcasecmp(argv[0], "number") == 0) {
@@ -213,14 +208,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
213 ws = workspace_create(NULL, name); 208 ws = workspace_create(NULL, name);
214 free(name); 209 free(name);
215 } 210 }
211 if (ws && auto_back_and_forth) {
212 ws = workspace_auto_back_and_forth(ws);
213 }
216 } else if (strcasecmp(argv[0], "next") == 0 || 214 } else if (strcasecmp(argv[0], "next") == 0 ||
217 strcasecmp(argv[0], "prev") == 0 || 215 strcasecmp(argv[0], "prev") == 0 ||
216 strcasecmp(argv[0], "next_on_output") == 0 ||
217 strcasecmp(argv[0], "prev_on_output") == 0 ||
218 strcasecmp(argv[0], "current") == 0) { 218 strcasecmp(argv[0], "current") == 0) {
219 ws = workspace_by_name(argv[0]); 219 ws = workspace_by_name(argv[0]);
220 } else if (strcasecmp(argv[0], "next_on_output") == 0) {
221 ws = workspace_output_next(current, create);
222 } else if (strcasecmp(argv[0], "prev_on_output") == 0) {
223 ws = workspace_output_prev(current, create);
224 } else if (strcasecmp(argv[0], "back_and_forth") == 0) { 220 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
225 if (!seat->prev_workspace_name) { 221 if (!seat->prev_workspace_name) {
226 return cmd_results_new(CMD_INVALID, 222 return cmd_results_new(CMD_INVALID,
@@ -235,11 +231,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
235 ws = workspace_create(NULL, name); 231 ws = workspace_create(NULL, name);
236 } 232 }
237 free(name); 233 free(name);
234 if (ws && auto_back_and_forth) {
235 ws = workspace_auto_back_and_forth(ws);
236 }
238 } 237 }
239 if (!ws) { 238 if (!ws) {
240 return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); 239 return cmd_results_new(CMD_FAILURE, "No workspace to switch to");
241 } 240 }
242 workspace_switch(ws, no_auto_back_and_forth); 241 workspace_switch(ws);
243 seat_consider_warp_to_focus(seat); 242 seat_consider_warp_to_focus(seat);
244 } 243 }
245 return cmd_results_new(CMD_SUCCESS, NULL); 244 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/config.c b/sway/config.c
index 6e665434..e4745a5c 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -26,7 +26,7 @@
26#include "sway/tree/arrange.h" 26#include "sway/tree/arrange.h"
27#include "sway/tree/root.h" 27#include "sway/tree/root.h"
28#include "sway/tree/workspace.h" 28#include "sway/tree/workspace.h"
29#include "cairo.h" 29#include "cairo_util.h"
30#include "pango.h" 30#include "pango.h"
31#include "stringop.h" 31#include "stringop.h"
32#include "list.h" 32#include "list.h"
@@ -236,7 +236,6 @@ static void config_defaults(struct sway_config *config) {
236 config->default_layout = L_NONE; 236 config->default_layout = L_NONE;
237 config->default_orientation = L_NONE; 237 config->default_orientation = L_NONE;
238 if (!(config->font = strdup("monospace 10"))) goto cleanup; 238 if (!(config->font = strdup("monospace 10"))) goto cleanup;
239 config->font_height = 17; // height of monospace 10
240 config->urgent_timeout = 500; 239 config->urgent_timeout = 500;
241 config->focus_on_window_activation = FOWA_URGENT; 240 config->focus_on_window_activation = FOWA_URGENT;
242 config->popup_during_fullscreen = POPUP_SMART; 241 config->popup_during_fullscreen = POPUP_SMART;
@@ -267,7 +266,7 @@ static void config_defaults(struct sway_config *config) {
267 config->tiling_drag = true; 266 config->tiling_drag = true;
268 config->tiling_drag_threshold = 9; 267 config->tiling_drag_threshold = 9;
269 268
270 config->smart_gaps = false; 269 config->smart_gaps = SMART_GAPS_OFF;
271 config->gaps_inner = 0; 270 config->gaps_inner = 0;
272 config->gaps_outer.top = 0; 271 config->gaps_outer.top = 0;
273 config->gaps_outer.right = 0; 272 config->gaps_outer.right = 0;
@@ -291,6 +290,8 @@ static void config_defaults(struct sway_config *config) {
291 config->hide_edge_borders_smart = ESMART_OFF; 290 config->hide_edge_borders_smart = ESMART_OFF;
292 config->hide_lone_tab = false; 291 config->hide_lone_tab = false;
293 292
293 config->has_focused_tab_title = false;
294
294 // border colors 295 // border colors
295 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); 296 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF);
296 color_to_rgba(config->border_colors.focused.background, 0x285577FF); 297 color_to_rgba(config->border_colors.focused.background, 0x285577FF);
@@ -338,35 +339,62 @@ static bool file_exists(const char *path) {
338 return path && access(path, R_OK) != -1; 339 return path && access(path, R_OK) != -1;
339} 340}
340 341
342static char *config_path(const char *prefix, const char *config_folder) {
343 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
344 return NULL;
345 }
346
347 const char *filename = "config";
348
349 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
350 char *path = calloc(size, sizeof(char));
351 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
352 return path;
353}
354
341static char *get_config_path(void) { 355static char *get_config_path(void) {
342 static const char *config_paths[] = { 356 char *path = NULL;
343 "$HOME/.sway/config", 357 const char *home = getenv("HOME");
344 "$XDG_CONFIG_HOME/sway/config", 358 char *config_home_fallback = NULL;
345 "$HOME/.i3/config", 359
346 "$XDG_CONFIG_HOME/i3/config", 360 const char *config_home = getenv("XDG_CONFIG_HOME");
347 SYSCONFDIR "/sway/config", 361 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
348 SYSCONFDIR "/i3/config", 362 size_t size_fallback = 1 + strlen(home) + strlen("/.config");
363 config_home_fallback = calloc(size_fallback, sizeof(char));
364 if (config_home_fallback != NULL)
365 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
366 config_home = config_home_fallback;
367 }
368
369 struct config_path {
370 const char *prefix;
371 const char *config_folder;
349 }; 372 };
350 373
351 char *config_home = getenv("XDG_CONFIG_HOME"); 374 struct config_path config_paths[] = {
352 if (!config_home || !*config_home) { 375 { .prefix = home, .config_folder = ".sway"},
353 config_paths[1] = "$HOME/.config/sway/config"; 376 { .prefix = config_home, .config_folder = "sway"},
354 config_paths[3] = "$HOME/.config/i3/config"; 377 { .prefix = home, .config_folder = ".i3"},
355 } 378 { .prefix = config_home, .config_folder = "i3"},
379 { .prefix = SYSCONFDIR, .config_folder = "sway"},
380 { .prefix = SYSCONFDIR, .config_folder = "i3"}
381 };
356 382
357 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { 383 size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);
358 wordexp_t p; 384 for (size_t i = 0; i < num_config_paths; i++) {
359 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { 385 path = config_path(config_paths[i].prefix, config_paths[i].config_folder);
360 char *path = strdup(p.we_wordv[0]); 386 if (!path) {
361 wordfree(&p); 387 continue;
362 if (file_exists(path)) { 388 }
363 return path; 389 if (file_exists(path)) {
364 } 390 break;
365 free(path);
366 } 391 }
392 free(path);
393 path = NULL;
367 } 394 }
368 395
369 return NULL; 396 free(config_home_fallback);
397 return path;
370} 398}
371 399
372static bool load_config(const char *path, struct sway_config *config, 400static bool load_config(const char *path, struct sway_config *config,
@@ -514,6 +542,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
514 return success; 542 return success;
515 } 543 }
516 544
545 // Only really necessary if not explicitly `font` is set in the config.
546 config_update_font_height();
547
517 if (is_active && !validating) { 548 if (is_active && !validating) {
518 input_manager_verify_fallback_seat(); 549 input_manager_verify_fallback_seat();
519 550
@@ -964,31 +995,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
964 return lenient_strcmp(wsa->workspace, wsb->workspace); 995 return lenient_strcmp(wsa->workspace, wsb->workspace);
965} 996}
966 997
967static void find_font_height_iterator(struct sway_container *con, void *data) {
968 size_t amount_below_baseline = con->title_height - con->title_baseline;
969 size_t extended_height = config->font_baseline + amount_below_baseline;
970 if (extended_height > config->font_height) {
971 config->font_height = extended_height;
972 }
973}
974
975static void find_baseline_iterator(struct sway_container *con, void *data) {
976 bool *recalculate = data;
977 if (*recalculate) {
978 container_calculate_title_height(con);
979 }
980 if (con->title_baseline > config->font_baseline) {
981 config->font_baseline = con->title_baseline;
982 }
983}
984 998
985void config_update_font_height(bool recalculate) { 999void config_update_font_height(void) {
986 size_t prev_max_height = config->font_height; 1000 int prev_max_height = config->font_height;
987 config->font_height = 0;
988 config->font_baseline = 0;
989 1001
990 root_for_each_container(find_baseline_iterator, &recalculate); 1002 get_text_metrics(config->font, &config->font_height, &config->font_baseline);
991 root_for_each_container(find_font_height_iterator, NULL);
992 1003
993 if (config->font_height != prev_max_height) { 1004 if (config->font_height != prev_max_height) {
994 arrange_root(); 1005 arrange_root();
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 767534a6..d1b342e6 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -91,7 +91,7 @@ struct bar_config *default_bar_config(void) {
91 } 91 }
92 bar->outputs = NULL; 92 bar->outputs = NULL;
93 bar->position = strdup("bottom"); 93 bar->position = strdup("bottom");
94 bar->pango_markup = false; 94 bar->pango_markup = PANGO_MARKUP_DEFAULT;
95 bar->swaybar_command = NULL; 95 bar->swaybar_command = NULL;
96 bar->font = NULL; 96 bar->font = NULL;
97 bar->height = 0; 97 bar->height = 0;
@@ -217,6 +217,9 @@ static void invoke_swaybar(struct bar_config *bar) {
217 sigset_t set; 217 sigset_t set;
218 sigemptyset(&set); 218 sigemptyset(&set);
219 sigprocmask(SIG_SETMASK, &set, NULL); 219 sigprocmask(SIG_SETMASK, &set, NULL);
220 signal(SIGPIPE, SIG_DFL);
221
222 restore_nofile_limit();
220 223
221 pid = fork(); 224 pid = fork();
222 if (pid < 0) { 225 if (pid < 0) {
diff --git a/sway/config/output.c b/sway/config/output.c
index c9ec6745..fa509252 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,5 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <drm_fourcc.h>
3#include <stdbool.h> 4#include <stdbool.h>
4#include <string.h> 5#include <string.h>
5#include <sys/socket.h> 6#include <sys/socket.h>
@@ -8,6 +9,7 @@
8#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
10#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/backend/drm.h>
11#include "sway/config.h" 13#include "sway/config.h"
12#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
13#include "sway/output.h" 15#include "sway/output.h"
@@ -58,6 +60,7 @@ struct output_config *new_output_config(const char *name) {
58 oc->width = oc->height = -1; 60 oc->width = oc->height = -1;
59 oc->refresh_rate = -1; 61 oc->refresh_rate = -1;
60 oc->custom_mode = -1; 62 oc->custom_mode = -1;
63 oc->drm_mode.type = -1;
61 oc->x = oc->y = -1; 64 oc->x = oc->y = -1;
62 oc->scale = -1; 65 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT; 66 oc->scale_filter = SCALE_FILTER_DEFAULT;
@@ -65,6 +68,7 @@ struct output_config *new_output_config(const char *name) {
65 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; 68 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
66 oc->max_render_time = -1; 69 oc->max_render_time = -1;
67 oc->adaptive_sync = -1; 70 oc->adaptive_sync = -1;
71 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
68 return oc; 72 return oc;
69} 73}
70 74
@@ -99,6 +103,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 103 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 104 dst->custom_mode = src->custom_mode;
101 } 105 }
106 if (src->drm_mode.type != (uint32_t) -1) {
107 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
108 }
102 if (src->transform != -1) { 109 if (src->transform != -1) {
103 dst->transform = src->transform; 110 dst->transform = src->transform;
104 } 111 }
@@ -108,6 +115,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
108 if (src->adaptive_sync != -1) { 115 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync; 116 dst->adaptive_sync = src->adaptive_sync;
110 } 117 }
118 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
119 dst->render_bit_depth = src->render_bit_depth;
120 }
111 if (src->background) { 121 if (src->background) {
112 free(dst->background); 122 free(dst->background);
113 dst->background = strdup(src->background); 123 dst->background = strdup(src->background);
@@ -271,6 +281,18 @@ static void set_mode(struct wlr_output *output, int width, int height,
271 wlr_output_set_mode(output, best); 281 wlr_output_set_mode(output, best);
272} 282}
273 283
284static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
285 if (!wlr_output_is_drm(output)) {
286 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
287 return;
288 }
289 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
290 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
291 if (mode) {
292 wlr_output_set_mode(output, mode);
293 }
294}
295
274/* Some manufacturers hardcode the aspect-ratio of the output in the physical 296/* Some manufacturers hardcode the aspect-ratio of the output in the physical
275 * size field. */ 297 * size field. */
276static bool phys_size_is_aspect_ratio(struct wlr_output *output) { 298static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
@@ -334,9 +356,26 @@ static int compute_default_scale(struct wlr_output *output) {
334 return 2; 356 return 2;
335} 357}
336 358
359/* Lists of formats to try, in order, when a specific render bit depth has
360 * been asked for. The second to last format in each list should always
361 * be XRGB8888, as a reliable backup in case the others are not available;
362 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */
363static const uint32_t *bit_depth_preferences[] = {
364 [RENDER_BIT_DEPTH_8] = (const uint32_t []){
365 DRM_FORMAT_XRGB8888,
366 DRM_FORMAT_INVALID,
367 },
368 [RENDER_BIT_DEPTH_10] = (const uint32_t []){
369 DRM_FORMAT_XRGB2101010,
370 DRM_FORMAT_XBGR2101010,
371 DRM_FORMAT_XRGB8888,
372 DRM_FORMAT_INVALID,
373 },
374};
375
337static void queue_output_config(struct output_config *oc, 376static void queue_output_config(struct output_config *oc,
338 struct sway_output *output) { 377 struct sway_output *output) {
339 if (output == root->noop_output) { 378 if (output == root->fallback_output) {
340 return; 379 return;
341 } 380 }
342 381
@@ -351,14 +390,36 @@ static void queue_output_config(struct output_config *oc,
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 390 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 391 wlr_output_enable(wlr_output, true);
353 392
354 if (oc && oc->width > 0 && oc->height > 0) { 393 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
394 sway_log(SWAY_DEBUG, "Set %s modeline",
395 wlr_output->name);
396 set_modeline(wlr_output, &oc->drm_mode);
397 } else if (oc && oc->width > 0 && oc->height > 0) {
355 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 398 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 399 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 400 set_mode(wlr_output, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 401 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 402 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 403 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 404 struct wlr_output_mode *preferred_mode =
405 wlr_output_preferred_mode(wlr_output);
406 wlr_output_set_mode(wlr_output, preferred_mode);
407
408 if (!wlr_output_test(wlr_output)) {
409 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
410 "falling back to another mode");
411 struct wlr_output_mode *mode;
412 wl_list_for_each(mode, &wlr_output->modes, link) {
413 if (mode == preferred_mode) {
414 continue;
415 }
416
417 wlr_output_set_mode(wlr_output, mode);
418 if (wlr_output_test(wlr_output)) {
419 break;
420 }
421 }
422 }
362 } 423 }
363 424
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 425 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -367,9 +428,16 @@ static void queue_output_config(struct output_config *oc,
367 wlr_output_set_subpixel(wlr_output, oc->subpixel); 428 wlr_output_set_subpixel(wlr_output, oc->subpixel);
368 } 429 }
369 430
431 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
370 if (oc && oc->transform >= 0) { 432 if (oc && oc->transform >= 0) {
371 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 433 tr = oc->transform;
372 wlr_output_set_transform(wlr_output, oc->transform); 434 } else if (wlr_output_is_drm(wlr_output)) {
435 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
436 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
437 }
438 if (wlr_output->transform != tr) {
439 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
440 wlr_output_set_transform(wlr_output, tr);
373 } 441 }
374 442
375 // Apply the scale last before the commit, because the scale auto-detection 443 // Apply the scale last before the commit, because the scale auto-detection
@@ -391,10 +459,26 @@ static void queue_output_config(struct output_config *oc,
391 oc->adaptive_sync); 459 oc->adaptive_sync);
392 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 460 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1);
393 } 461 }
462
463 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
464 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth];
465 assert(fmts);
466
467 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
468 wlr_output_set_render_format(wlr_output, fmts[i]);
469 if (wlr_output_test(wlr_output)) {
470 break;
471 }
472
473 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
474 "failed to work, falling back to next in "
475 "list, 0x%08x", fmts[i], fmts[i + 1]);
476 }
477 }
394} 478}
395 479
396bool apply_output_config(struct output_config *oc, struct sway_output *output) { 480bool apply_output_config(struct output_config *oc, struct sway_output *output) {
397 if (output == root->noop_output) { 481 if (output == root->fallback_output) {
398 return false; 482 return false;
399 } 483 }
400 484
@@ -483,11 +567,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
483 // this output came online, and some config items (like map_to_output) are 567 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present. 568 // dependent on an output being present.
485 input_manager_configure_all_inputs(); 569 input_manager_configure_all_inputs();
570 // Reconfigure the cursor images, since the scale may have changed.
571 input_manager_configure_xcursor();
486 return true; 572 return true;
487} 573}
488 574
489bool test_output_config(struct output_config *oc, struct sway_output *output) { 575bool test_output_config(struct output_config *oc, struct sway_output *output) {
490 if (output == root->noop_output) { 576 if (output == root->fallback_output) {
491 return false; 577 return false;
492 } 578 }
493 579
@@ -702,6 +788,8 @@ static bool _spawn_swaybg(char **command) {
702 sway_log_errno(SWAY_ERROR, "fork failed"); 788 sway_log_errno(SWAY_ERROR, "fork failed");
703 return false; 789 return false;
704 } else if (pid == 0) { 790 } else if (pid == 0) {
791 restore_nofile_limit();
792
705 pid = fork(); 793 pid = fork();
706 if (pid < 0) { 794 if (pid < 0) {
707 sway_log_errno(SWAY_ERROR, "fork failed"); 795 sway_log_errno(SWAY_ERROR, "fork failed");
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..82353038 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -36,7 +36,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
36 36
37 inhibitor->manager = manager; 37 inhibitor->manager = manager;
38 inhibitor->mode = INHIBIT_IDLE_APPLICATION; 38 inhibitor->mode = INHIBIT_IDLE_APPLICATION;
39 inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); 39 inhibitor->wlr_inhibitor = wlr_inhibitor;
40 wl_list_insert(&manager->inhibitors, &inhibitor->link); 40 wl_list_insert(&manager->inhibitors, &inhibitor->link);
41 41
42 inhibitor->destroy.notify = handle_destroy; 42 inhibitor->destroy.notify = handle_destroy;
@@ -69,8 +69,8 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
69 struct sway_idle_inhibitor_v1 *inhibitor; 69 struct sway_idle_inhibitor_v1 *inhibitor;
70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors,
71 link) { 71 link) {
72 if (inhibitor->view == view && 72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION &&
73 inhibitor->mode != INHIBIT_IDLE_APPLICATION) { 73 inhibitor->view == view) {
74 return inhibitor; 74 return inhibitor;
75 } 75 }
76 } 76 }
@@ -82,8 +82,8 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi
82 struct sway_idle_inhibitor_v1 *inhibitor; 82 struct sway_idle_inhibitor_v1 *inhibitor;
83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors,
84 link) { 84 link) {
85 if (inhibitor->view == view && 85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION &&
86 inhibitor->mode == INHIBIT_IDLE_APPLICATION) { 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
87 return inhibitor; 87 return inhibitor;
88 } 88 }
89 } 89 }
@@ -104,10 +104,10 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy(
104 104
105bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { 105bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {
106 switch (inhibitor->mode) { 106 switch (inhibitor->mode) {
107 case INHIBIT_IDLE_APPLICATION: 107 case INHIBIT_IDLE_APPLICATION:;
108 // If there is no view associated with the inhibitor, assume visible 108 // If there is no view associated with the inhibitor, assume visible
109 return !inhibitor->view || !inhibitor->view->container || 109 struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface);
110 view_is_visible(inhibitor->view); 110 return !view || !view->container || view_is_visible(view);
111 case INHIBIT_IDLE_FOCUS:; 111 case INHIBIT_IDLE_FOCUS:;
112 struct sway_seat *seat = NULL; 112 struct sway_seat *seat = NULL;
113 wl_list_for_each(seat, &server.input->seats, link) { 113 wl_list_for_each(seat, &server.input->seats, link) {
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index d4ca4fb4..1250415e 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,10 +2,10 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_box.h>
6#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
7#include <wlr/types/wlr_output_damage.h> 6#include <wlr/types/wlr_output_damage.h>
8#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_subcompositor.h>
9#include "log.h" 9#include "log.h"
10#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
@@ -115,9 +115,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
115 // Horizontal axis 115 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT 116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; 117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if ((state->anchor & both_horiz) && box.width == 0) { 118 if (box.width == 0) {
119 box.x = bounds.x; 119 box.x = bounds.x;
120 box.width = bounds.width; 120 } else if ((state->anchor & both_horiz) == both_horiz) {
121 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
122 box.x = bounds.x; 123 box.x = bounds.x;
123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 124 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
@@ -128,9 +129,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
128 // Vertical axis 129 // Vertical axis
129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP 130 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; 131 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
131 if ((state->anchor & both_vert) && box.height == 0) { 132 if (box.height == 0) {
132 box.y = bounds.y; 133 box.y = bounds.y;
133 box.height = bounds.height; 134 } else if ((state->anchor & both_vert) == both_vert) {
135 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
134 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
135 box.y = bounds.y; 137 box.y = bounds.y;
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 138 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
@@ -139,25 +141,30 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list,
139 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); 141 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
140 } 142 }
141 // Margin 143 // Margin
142 if ((state->anchor & both_horiz) == both_horiz) { 144 if (box.width == 0) {
143 box.x += state->margin.left; 145 box.x += state->margin.left;
144 box.width -= state->margin.left + state->margin.right; 146 box.width = bounds.width -
147 (state->margin.left + state->margin.right);
148 } else if ((state->anchor & both_horiz) == both_horiz) {
149 // don't apply margins
145 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { 150 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
146 box.x += state->margin.left; 151 box.x += state->margin.left;
147 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { 152 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
148 box.x -= state->margin.right; 153 box.x -= state->margin.right;
149 } 154 }
150 if ((state->anchor & both_vert) == both_vert) { 155 if (box.height == 0) {
151 box.y += state->margin.top; 156 box.y += state->margin.top;
152 box.height -= state->margin.top + state->margin.bottom; 157 box.height = bounds.height -
158 (state->margin.top + state->margin.bottom);
159 } else if ((state->anchor & both_vert) == both_vert) {
160 // don't apply margins
153 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { 161 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
154 box.y += state->margin.top; 162 box.y += state->margin.top;
155 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { 163 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
156 box.y -= state->margin.bottom; 164 box.y -= state->margin.bottom;
157 } 165 }
158 if (box.width < 0 || box.height < 0) { 166 if (!sway_assert(box.width >= 0 && box.height >= 0,
159 // TODO: Bubble up a protocol error? 167 "Expected layer surface to have positive size")) {
160 wlr_layer_surface_v1_close(layer);
161 continue; 168 continue;
162 } 169 }
163 // Apply 170 // Apply
@@ -191,7 +198,7 @@ void arrange_layers(struct sway_output *output) {
191 arrange_output(output); 198 arrange_output(output);
192 } 199 }
193 200
194 // Arrange non-exlusive surfaces from top->bottom 201 // Arrange non-exclusive surfaces from top->bottom
195 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 202 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
196 &usable_area, false); 203 &usable_area, false);
197 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 204 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
@@ -277,7 +284,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
277 } 284 }
278 285
279 sway_layer->layer_surface->output = NULL; 286 sway_layer->layer_surface->output = NULL;
280 wlr_layer_surface_v1_close(sway_layer->layer_surface); 287 wlr_layer_surface_v1_destroy(sway_layer->layer_surface);
281} 288}
282 289
283static void handle_surface_commit(struct wl_listener *listener, void *data) { 290static void handle_surface_commit(struct wl_listener *listener, void *data) {
@@ -290,21 +297,30 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
290 } 297 }
291 298
292 struct sway_output *output = wlr_output->data; 299 struct sway_output *output = wlr_output->data;
293 struct wlr_box old_geo = layer->geo; 300 struct wlr_box old_extent = layer->extent;
294 arrange_layers(output); 301
295 302 bool layer_changed = false;
296 bool geo_changed = 303 if (layer_surface->current.committed != 0
297 memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; 304 || layer->mapped != layer_surface->mapped) {
298 bool layer_changed = layer->layer != layer_surface->current.layer; 305 layer->mapped = layer_surface->mapped;
299 if (layer_changed) { 306 layer_changed = layer->layer != layer_surface->current.layer;
300 wl_list_remove(&layer->link); 307 if (layer_changed) {
301 wl_list_insert(&output->layers[layer_surface->current.layer], 308 wl_list_remove(&layer->link);
302 &layer->link); 309 wl_list_insert(&output->layers[layer_surface->current.layer],
303 layer->layer = layer_surface->current.layer; 310 &layer->link);
311 layer->layer = layer_surface->current.layer;
312 }
313 arrange_layers(output);
304 } 314 }
305 if (geo_changed || layer_changed) { 315
306 output_damage_surface(output, old_geo.x, old_geo.y, 316 wlr_surface_get_extends(layer_surface->surface, &layer->extent);
307 layer_surface->surface, true); 317 layer->extent.x += layer->geo.x;
318 layer->extent.y += layer->geo.y;
319
320 bool extent_changed =
321 memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0;
322 if (extent_changed || layer_changed) {
323 output_damage_box(output, &old_extent);
308 output_damage_surface(output, layer->geo.x, layer->geo.y, 324 output_damage_surface(output, layer->geo.x, layer->geo.y,
309 layer_surface->surface, true); 325 layer_surface->surface, true);
310 } else { 326 } else {
@@ -337,6 +353,8 @@ static void unmap(struct sway_layer_surface *sway_layer) {
337 sway_layer->layer_surface->surface, true); 353 sway_layer->layer_surface->surface, true);
338} 354}
339 355
356static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface);
357
340static void handle_destroy(struct wl_listener *listener, void *data) { 358static void handle_destroy(struct wl_listener *listener, void *data) {
341 struct sway_layer_surface *sway_layer = 359 struct sway_layer_surface *sway_layer =
342 wl_container_of(listener, sway_layer, destroy); 360 wl_container_of(listener, sway_layer, destroy);
@@ -345,6 +363,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
345 if (sway_layer->layer_surface->mapped) { 363 if (sway_layer->layer_surface->mapped) {
346 unmap(sway_layer); 364 unmap(sway_layer);
347 } 365 }
366
367 struct sway_layer_subsurface *subsurface, *subsurface_tmp;
368 wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) {
369 layer_subsurface_destroy(subsurface);
370 }
371
348 wl_list_remove(&sway_layer->link); 372 wl_list_remove(&sway_layer->link);
349 wl_list_remove(&sway_layer->destroy.link); 373 wl_list_remove(&sway_layer->destroy.link);
350 wl_list_remove(&sway_layer->map.link); 374 wl_list_remove(&sway_layer->map.link);
@@ -413,11 +437,8 @@ static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
413 subsurface_damage(subsurface, false); 437 subsurface_damage(subsurface, false);
414} 438}
415 439
416static void subsurface_handle_destroy(struct wl_listener *listener, 440static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) {
417 void *data) { 441 wl_list_remove(&subsurface->link);
418 struct sway_layer_subsurface *subsurface =
419 wl_container_of(listener, subsurface, destroy);
420
421 wl_list_remove(&subsurface->map.link); 442 wl_list_remove(&subsurface->map.link);
422 wl_list_remove(&subsurface->unmap.link); 443 wl_list_remove(&subsurface->unmap.link);
423 wl_list_remove(&subsurface->destroy.link); 444 wl_list_remove(&subsurface->destroy.link);
@@ -425,17 +446,25 @@ static void subsurface_handle_destroy(struct wl_listener *listener,
425 free(subsurface); 446 free(subsurface);
426} 447}
427 448
449static void subsurface_handle_destroy(struct wl_listener *listener,
450 void *data) {
451 struct sway_layer_subsurface *subsurface =
452 wl_container_of(listener, subsurface, destroy);
453 layer_subsurface_destroy(subsurface);
454}
455
428static struct sway_layer_subsurface *create_subsurface( 456static struct sway_layer_subsurface *create_subsurface(
429 struct wlr_subsurface *wlr_subsurface, 457 struct wlr_subsurface *wlr_subsurface,
430 struct sway_layer_surface *layer_surface) { 458 struct sway_layer_surface *layer_surface) {
431 struct sway_layer_subsurface *subsurface = 459 struct sway_layer_subsurface *subsurface =
432 calloc(1, sizeof(struct sway_layer_surface)); 460 calloc(1, sizeof(struct sway_layer_subsurface));
433 if (subsurface == NULL) { 461 if (subsurface == NULL) {
434 return NULL; 462 return NULL;
435 } 463 }
436 464
437 subsurface->wlr_subsurface = wlr_subsurface; 465 subsurface->wlr_subsurface = wlr_subsurface;
438 subsurface->layer_surface = layer_surface; 466 subsurface->layer_surface = layer_surface;
467 wl_list_insert(&layer_surface->subsurfaces, &subsurface->link);
439 468
440 subsurface->map.notify = subsurface_handle_map; 469 subsurface->map.notify = subsurface_handle_map;
441 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 470 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
@@ -468,8 +497,8 @@ static struct sway_layer_surface *popup_get_layer(
468static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 497static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
469 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 498 struct wlr_xdg_popup *popup = layer_popup->wlr_popup;
470 struct wlr_surface *surface = popup->base->surface; 499 struct wlr_surface *surface = popup->base->surface;
471 int popup_sx = popup->geometry.x - popup->base->geometry.x; 500 int popup_sx = popup->geometry.x - popup->base->current.geometry.x;
472 int popup_sy = popup->geometry.y - popup->base->geometry.y; 501 int popup_sy = popup->geometry.y - popup->base->current.geometry.y;
473 int ox = popup_sx, oy = popup_sy; 502 int ox = popup_sx, oy = popup_sy;
474 struct sway_layer_surface *layer; 503 struct sway_layer_surface *layer;
475 while (true) { 504 while (true) {
@@ -590,14 +619,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
590 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 619 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32
591 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", 620 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",",
592 layer_surface->namespace, 621 layer_surface->namespace,
593 layer_surface->client_pending.layer, 622 layer_surface->pending.layer,
594 layer_surface->client_pending.anchor, 623 layer_surface->pending.anchor,
595 layer_surface->client_pending.desired_width, 624 layer_surface->pending.desired_width,
596 layer_surface->client_pending.desired_height, 625 layer_surface->pending.desired_height,
597 layer_surface->client_pending.margin.top, 626 layer_surface->pending.margin.top,
598 layer_surface->client_pending.margin.right, 627 layer_surface->pending.margin.right,
599 layer_surface->client_pending.margin.bottom, 628 layer_surface->pending.margin.bottom,
600 layer_surface->client_pending.margin.left); 629 layer_surface->pending.margin.left);
601 630
602 if (!layer_surface->output) { 631 if (!layer_surface->output) {
603 // Assign last active output 632 // Assign last active output
@@ -609,12 +638,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
609 output = ws->output; 638 output = ws->output;
610 } 639 }
611 } 640 }
612 if (!output || output == root->noop_output) { 641 if (!output || output == root->fallback_output) {
613 if (!root->outputs->length) { 642 if (!root->outputs->length) {
614 sway_log(SWAY_ERROR, 643 sway_log(SWAY_ERROR,
615 "no output to auto-assign layer surface '%s' to", 644 "no output to auto-assign layer surface '%s' to",
616 layer_surface->namespace); 645 layer_surface->namespace);
617 wlr_layer_surface_v1_close(layer_surface); 646 wlr_layer_surface_v1_destroy(layer_surface);
618 return; 647 return;
619 } 648 }
620 output = root->outputs->items[0]; 649 output = root->outputs->items[0];
@@ -628,6 +657,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
628 return; 657 return;
629 } 658 }
630 659
660 wl_list_init(&sway_layer->subsurfaces);
661
631 sway_layer->surface_commit.notify = handle_surface_commit; 662 sway_layer->surface_commit.notify = handle_surface_commit;
632 wl_signal_add(&layer_surface->surface->events.commit, 663 wl_signal_add(&layer_surface->surface->events.commit,
633 &sway_layer->surface_commit); 664 &sway_layer->surface_commit);
@@ -649,15 +680,15 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
649 680
650 struct sway_output *output = layer_surface->output->data; 681 struct sway_output *output = layer_surface->output->data;
651 sway_layer->output_destroy.notify = handle_output_destroy; 682 sway_layer->output_destroy.notify = handle_output_destroy;
652 wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); 683 wl_signal_add(&output->events.disable, &sway_layer->output_destroy);
653 684
654 wl_list_insert(&output->layers[layer_surface->client_pending.layer], 685 wl_list_insert(&output->layers[layer_surface->pending.layer],
655 &sway_layer->link); 686 &sway_layer->link);
656 687
657 // Temporarily set the layer's current state to client_pending 688 // Temporarily set the layer's current state to pending
658 // So that we can easily arrange it 689 // So that we can easily arrange it
659 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 690 struct wlr_layer_surface_v1_state old_state = layer_surface->current;
660 layer_surface->current = layer_surface->client_pending; 691 layer_surface->current = layer_surface->pending;
661 arrange_layers(output); 692 arrange_layers(output);
662 layer_surface->current = old_state; 693 layer_surface->current = old_state;
663} 694}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 5edc8f96..852671d2 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -4,15 +4,17 @@
4#include <strings.h> 4#include <strings.h>
5#include <time.h> 5#include <time.h>
6#include <wayland-server-core.h> 6#include <wayland-server-core.h>
7#include <wlr/backend/drm.h>
8#include <wlr/backend/headless.h>
7#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_drm_lease_v1.h>
10#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
11#include <wlr/types/wlr_output_damage.h> 13#include <wlr/types/wlr_output_damage.h>
12#include <wlr/types/wlr_output_layout.h> 14#include <wlr/types/wlr_output_layout.h>
13#include <wlr/types/wlr_output.h> 15#include <wlr/types/wlr_output.h>
14#include <wlr/types/wlr_presentation_time.h> 16#include <wlr/types/wlr_presentation_time.h>
15#include <wlr/types/wlr_surface.h> 17#include <wlr/types/wlr_compositor.h>
16#include <wlr/util/region.h> 18#include <wlr/util/region.h>
17#include "config.h" 19#include "config.h"
18#include "log.h" 20#include "log.h"
@@ -56,26 +58,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
56 return NULL; 58 return NULL;
57} 59}
58 60
59/**
60 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
61 * the child position is (*sx, *sy) and its size is (sw, sh).
62 */
63static void rotate_child_position(double *sx, double *sy, double sw, double sh,
64 double pw, double ph, float rotation) {
65 if (rotation == 0.0f) {
66 return;
67 }
68
69 // Coordinates relative to the center of the subsurface
70 double ox = *sx - pw/2 + sw/2,
71 oy = *sy - ph/2 + sh/2;
72 // Rotated coordinates
73 double rx = cos(-rotation)*ox - sin(-rotation)*oy,
74 ry = cos(-rotation)*oy + sin(-rotation)*ox;
75 *sx = rx + pw/2 - sw/2;
76 *sy = ry + ph/2 - sh/2;
77}
78
79struct surface_iterator_data { 61struct surface_iterator_data {
80 sway_surface_iterator_func_t user_iterator; 62 sway_surface_iterator_func_t user_iterator;
81 void *user_data; 63 void *user_data;
@@ -84,7 +66,6 @@ struct surface_iterator_data {
84 struct sway_view *view; 66 struct sway_view *view;
85 double ox, oy; 67 double ox, oy;
86 int width, height; 68 int width, height;
87 float rotation;
88}; 69};
89 70
90static bool get_surface_box(struct surface_iterator_data *data, 71static bool get_surface_box(struct surface_iterator_data *data,
@@ -99,14 +80,9 @@ static bool get_surface_box(struct surface_iterator_data *data,
99 int sw = surface->current.width; 80 int sw = surface->current.width;
100 int sh = surface->current.height; 81 int sh = surface->current.height;
101 82
102 double _sx = sx + surface->sx;
103 double _sy = sy + surface->sy;
104 rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height,
105 data->rotation);
106
107 struct wlr_box box = { 83 struct wlr_box box = {
108 .x = data->ox + _sx, 84 .x = floor(data->ox + sx),
109 .y = data->oy + _sy, 85 .y = floor(data->oy + sy),
110 .width = sw, 86 .width = sw,
111 .height = sh, 87 .height = sh,
112 }; 88 };
@@ -114,16 +90,13 @@ static bool get_surface_box(struct surface_iterator_data *data,
114 memcpy(surface_box, &box, sizeof(struct wlr_box)); 90 memcpy(surface_box, &box, sizeof(struct wlr_box));
115 } 91 }
116 92
117 struct wlr_box rotated_box;
118 wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
119
120 struct wlr_box output_box = { 93 struct wlr_box output_box = {
121 .width = output->width, 94 .width = output->width,
122 .height = output->height, 95 .height = output->height,
123 }; 96 };
124 97
125 struct wlr_box intersection; 98 struct wlr_box intersection;
126 return wlr_box_intersection(&intersection, &output_box, &rotated_box); 99 return wlr_box_intersection(&intersection, &output_box, &box);
127} 100}
128 101
129static void output_for_each_surface_iterator(struct wlr_surface *surface, 102static void output_for_each_surface_iterator(struct wlr_surface *surface,
@@ -136,7 +109,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface,
136 return; 109 return;
137 } 110 }
138 111
139 data->user_iterator(data->output, data->view, surface, &box, data->rotation, 112 data->user_iterator(data->output, data->view, surface, &box,
140 data->user_data); 113 data->user_data);
141} 114}
142 115
@@ -152,7 +125,6 @@ void output_surface_for_each_surface(struct sway_output *output,
152 .oy = oy, 125 .oy = oy,
153 .width = surface->current.width, 126 .width = surface->current.width,
154 .height = surface->current.height, 127 .height = surface->current.height,
155 .rotation = 0,
156 }; 128 };
157 129
158 wlr_surface_for_each_surface(surface, 130 wlr_surface_for_each_surface(surface,
@@ -173,7 +145,6 @@ void output_view_for_each_surface(struct sway_output *output,
173 - view->geometry.y, 145 - view->geometry.y,
174 .width = view->container->current.content_width, 146 .width = view->container->current.content_width,
175 .height = view->container->current.content_height, 147 .height = view->container->current.content_height,
176 .rotation = 0, // TODO
177 }; 148 };
178 149
179 view_for_each_surface(view, output_for_each_surface_iterator, &data); 150 view_for_each_surface(view, output_for_each_surface_iterator, &data);
@@ -193,7 +164,6 @@ void output_view_for_each_popup_surface(struct sway_output *output,
193 - view->geometry.y, 164 - view->geometry.y,
194 .width = view->container->current.content_width, 165 .width = view->container->current.content_width,
195 .height = view->container->current.content_height, 166 .height = view->container->current.content_height,
196 .rotation = 0, // TODO
197 }; 167 };
198 168
199 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); 169 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
@@ -206,40 +176,19 @@ void output_layer_for_each_surface(struct sway_output *output,
206 wl_list_for_each(layer_surface, layer_surfaces, link) { 176 wl_list_for_each(layer_surface, layer_surfaces, link) {
207 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 177 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
208 layer_surface->layer_surface; 178 layer_surface->layer_surface;
209 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, 179 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
210 layer_surface->geo.x, layer_surface->geo.y, iterator, 180 struct surface_iterator_data data = {
211 user_data); 181 .user_iterator = iterator,
212 182 .user_data = user_data,
213 struct wlr_xdg_popup *state; 183 .output = output,
214 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 184 .view = NULL,
215 struct wlr_xdg_surface *popup = state->base; 185 .ox = layer_surface->geo.x,
216 if (!popup->configured) { 186 .oy = layer_surface->geo.y,
217 continue; 187 .width = surface->current.width,
218 } 188 .height = surface->current.height,
219 189 };
220 double popup_sx, popup_sy; 190 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
221 popup_sx = layer_surface->geo.x + 191 output_for_each_surface_iterator, &data);
222 popup->popup->geometry.x - popup->geometry.x;
223 popup_sy = layer_surface->geo.y +
224 popup->popup->geometry.y - popup->geometry.y;
225
226 struct wlr_surface *surface = popup->surface;
227
228 struct surface_iterator_data data = {
229 .user_iterator = iterator,
230 .user_data = user_data,
231 .output = output,
232 .view = NULL,
233 .ox = popup_sx,
234 .oy = popup_sy,
235 .width = surface->current.width,
236 .height = surface->current.height,
237 .rotation = 0,
238 };
239
240 wlr_xdg_surface_for_each_surface(
241 popup, output_for_each_surface_iterator, &data);
242 }
243 } 192 }
244} 193}
245 194
@@ -264,37 +213,19 @@ void output_layer_for_each_popup_surface(struct sway_output *output,
264 wl_list_for_each(layer_surface, layer_surfaces, link) { 213 wl_list_for_each(layer_surface, layer_surfaces, link) {
265 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = 214 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
266 layer_surface->layer_surface; 215 layer_surface->layer_surface;
267 216 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
268 struct wlr_xdg_popup *state; 217 struct surface_iterator_data data = {
269 wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { 218 .user_iterator = iterator,
270 struct wlr_xdg_surface *popup = state->base; 219 .user_data = user_data,
271 if (!popup->configured) { 220 .output = output,
272 continue; 221 .view = NULL,
273 } 222 .ox = layer_surface->geo.x,
274 223 .oy = layer_surface->geo.y,
275 double popup_sx, popup_sy; 224 .width = surface->current.width,
276 popup_sx = layer_surface->geo.x + 225 .height = surface->current.height,
277 popup->popup->geometry.x - popup->geometry.x; 226 };
278 popup_sy = layer_surface->geo.y + 227 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
279 popup->popup->geometry.y - popup->geometry.y; 228 output_for_each_surface_iterator, &data);
280
281 struct wlr_surface *surface = popup->surface;
282
283 struct surface_iterator_data data = {
284 .user_iterator = iterator,
285 .user_data = user_data,
286 .output = output,
287 .view = NULL,
288 .ox = popup_sx,
289 .oy = popup_sy,
290 .width = surface->current.width,
291 .height = surface->current.height,
292 .rotation = 0,
293 };
294
295 wlr_xdg_surface_for_each_surface(
296 popup, output_for_each_surface_iterator, &data);
297 }
298 } 229 }
299} 230}
300 231
@@ -463,9 +394,9 @@ struct send_frame_done_data {
463 int msec_until_refresh; 394 int msec_until_refresh;
464}; 395};
465 396
466static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, 397static void send_frame_done_iterator(struct sway_output *output,
467 struct wlr_surface *surface, struct wlr_box *box, float rotation, 398 struct sway_view *view, struct wlr_surface *surface,
468 void *user_data) { 399 struct wlr_box *box, void *user_data) {
469 int view_max_render_time = 0; 400 int view_max_render_time = 0;
470 if (view != NULL) { 401 if (view != NULL) {
471 view_max_render_time = view->max_render_time; 402 view_max_render_time = view->max_render_time;
@@ -488,9 +419,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); 419 output_for_each_surface(output, send_frame_done_iterator, data);
489} 420}
490 421
491static void count_surface_iterator(struct sway_output *output, struct sway_view *view, 422static void count_surface_iterator(struct sway_output *output,
492 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 423 struct sway_view *view, struct wlr_surface *surface,
493 void *data) { 424 struct wlr_box *box, void *data) {
494 size_t *n = data; 425 size_t *n = data;
495 (*n)++; 426 (*n)++;
496} 427}
@@ -577,7 +508,7 @@ static int output_repaint_timer_handler(void *data) {
577 fullscreen_con = workspace->current.fullscreen; 508 fullscreen_con = workspace->current.fullscreen;
578 } 509 }
579 510
580 if (fullscreen_con && fullscreen_con->view) { 511 if (fullscreen_con && fullscreen_con->view && !debug.noscanout) {
581 // Try to scan-out the fullscreen view 512 // Try to scan-out the fullscreen view
582 static bool last_scanned_out = false; 513 static bool last_scanned_out = false;
583 bool scanned_out = 514 bool scanned_out =
@@ -590,6 +521,7 @@ static int output_repaint_timer_handler(void *data) {
590 if (last_scanned_out && !scanned_out) { 521 if (last_scanned_out && !scanned_out) {
591 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 522 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s",
592 output->wlr_output->name); 523 output->wlr_output->name);
524 output_damage_whole(output);
593 } 525 }
594 last_scanned_out = scanned_out; 526 last_scanned_out = scanned_out;
595 527
@@ -693,38 +625,30 @@ void output_damage_whole(struct sway_output *output) {
693 } 625 }
694} 626}
695 627
696static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, 628static void damage_surface_iterator(struct sway_output *output,
697 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 629 struct sway_view *view, struct wlr_surface *surface,
698 void *_data) { 630 struct wlr_box *_box, void *_data) {
699 bool *data = _data; 631 bool *data = _data;
700 bool whole = *data; 632 bool whole = *data;
701 633
702 struct wlr_box box = *_box; 634 struct wlr_box box = *_box;
703 scale_box(&box, output->wlr_output->scale); 635 scale_box(&box, output->wlr_output->scale);
704 636
705 int center_x = box.x + box.width/2; 637 pixman_region32_t damage;
706 int center_y = box.y + box.height/2; 638 pixman_region32_init(&damage);
707 639 wlr_surface_get_effective_damage(surface, &damage);
708 if (pixman_region32_not_empty(&surface->buffer_damage)) { 640 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
709 pixman_region32_t damage; 641 if (ceil(output->wlr_output->scale) > surface->current.scale) {
710 pixman_region32_init(&damage); 642 // When scaling up a surface, it'll become blurry so we need to
711 wlr_surface_get_effective_damage(surface, &damage); 643 // expand the damage region
712 wlr_region_scale(&damage, &damage, output->wlr_output->scale); 644 wlr_region_expand(&damage, &damage,
713 if (ceil(output->wlr_output->scale) > surface->current.scale) { 645 ceil(output->wlr_output->scale) - surface->current.scale);
714 // When scaling up a surface, it'll become blurry so we need to 646 }
715 // expand the damage region 647 pixman_region32_translate(&damage, box.x, box.y);
716 wlr_region_expand(&damage, &damage, 648 wlr_output_damage_add(output->damage, &damage);
717 ceil(output->wlr_output->scale) - surface->current.scale); 649 pixman_region32_fini(&damage);
718 }
719 pixman_region32_translate(&damage, box.x, box.y);
720 wlr_region_rotated_bounds(&damage, &damage, rotation,
721 center_x, center_y);
722 wlr_output_damage_add(output->damage, &damage);
723 pixman_region32_fini(&damage);
724 }
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
@@ -808,7 +732,7 @@ static void update_output_manager_config(struct sway_server *server) {
808 732
809 struct sway_output *output; 733 struct sway_output *output;
810 wl_list_for_each(output, &root->all_outputs, link) { 734 wl_list_for_each(output, &root->all_outputs, link) {
811 if (output == root->noop_output) { 735 if (output == root->fallback_output) {
812 continue; 736 continue;
813 } 737 }
814 struct wlr_output_configuration_head_v1 *config_head = 738 struct wlr_output_configuration_head_v1 *config_head =
@@ -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;
@@ -830,18 +754,22 @@ static void update_output_manager_config(struct sway_server *server) {
830static void handle_destroy(struct wl_listener *listener, void *data) { 754static void handle_destroy(struct wl_listener *listener, void *data) {
831 struct sway_output *output = wl_container_of(listener, output, destroy); 755 struct sway_output *output = wl_container_of(listener, output, destroy);
832 struct sway_server *server = output->server; 756 struct sway_server *server = output->server;
833 wl_signal_emit(&output->events.destroy, output); 757 output_begin_destroy(output);
834 758
835 if (output->enabled) { 759 if (output->enabled) {
836 output_disable(output); 760 output_disable(output);
837 } 761 }
838 output_begin_destroy(output); 762
763 wl_list_remove(&output->link);
839 764
840 wl_list_remove(&output->destroy.link); 765 wl_list_remove(&output->destroy.link);
841 wl_list_remove(&output->commit.link); 766 wl_list_remove(&output->commit.link);
842 wl_list_remove(&output->mode.link); 767 wl_list_remove(&output->mode.link);
843 wl_list_remove(&output->present.link); 768 wl_list_remove(&output->present.link);
844 769
770 output->wlr_output->data = NULL;
771 output->wlr_output = NULL;
772
845 transaction_commit_dirty(); 773 transaction_commit_dirty();
846 774
847 update_output_manager_config(server); 775 update_output_manager_config(server);
@@ -902,7 +830,7 @@ static void handle_present(struct wl_listener *listener, void *data) {
902 struct sway_output *output = wl_container_of(listener, output, present); 830 struct sway_output *output = wl_container_of(listener, output, present);
903 struct wlr_output_event_present *output_event = data; 831 struct wlr_output_event_present *output_event = data;
904 832
905 if (!output->enabled) { 833 if (!output->enabled || !output_event->presented) {
906 return; 834 return;
907 } 835 }
908 836
@@ -910,10 +838,39 @@ static void handle_present(struct wl_listener *listener, void *data) {
910 output->refresh_nsec = output_event->refresh; 838 output->refresh_nsec = output_event->refresh;
911} 839}
912 840
841static unsigned int last_headless_num = 0;
842
913void handle_new_output(struct wl_listener *listener, void *data) { 843void handle_new_output(struct wl_listener *listener, void *data) {
914 struct sway_server *server = wl_container_of(listener, server, new_output); 844 struct sway_server *server = wl_container_of(listener, server, new_output);
915 struct wlr_output *wlr_output = data; 845 struct wlr_output *wlr_output = data;
916 sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 846
847 if (wlr_output == root->fallback_output->wlr_output) {
848 return;
849 }
850
851 if (wlr_output_is_headless(wlr_output)) {
852 char name[64];
853 snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num);
854 wlr_output_set_name(wlr_output, name);
855 }
856
857 sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)",
858 wlr_output, wlr_output->name, wlr_output->non_desktop);
859
860 if (wlr_output->non_desktop) {
861 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
862 if (server->drm_lease_manager) {
863 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
864 wlr_output);
865 }
866 return;
867 }
868
869 if (!wlr_output_init_render(wlr_output, server->allocator,
870 server->renderer)) {
871 sway_log(SWAY_ERROR, "Failed to init output render");
872 return;
873 }
917 874
918 struct sway_output *output = output_create(wlr_output); 875 struct sway_output *output = output_create(wlr_output);
919 if (!output) { 876 if (!output) {
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index bd85282c..02397c05 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -7,13 +7,12 @@
7#include <wayland-server-core.h> 7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h> 8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_box.h>
11#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
12#include <wlr/types/wlr_matrix.h> 11#include <wlr/types/wlr_matrix.h>
13#include <wlr/types/wlr_output_damage.h> 12#include <wlr/types/wlr_output_damage.h>
14#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
15#include <wlr/types/wlr_output.h> 14#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_surface.h> 15#include <wlr/types/wlr_compositor.h>
17#include <wlr/util/region.h> 16#include <wlr/util/region.h>
18#include "log.h" 17#include "log.h"
19#include "config.h" 18#include "config.h"
@@ -32,6 +31,7 @@
32struct render_data { 31struct render_data {
33 pixman_region32_t *damage; 32 pixman_region32_t *damage;
34 float alpha; 33 float alpha;
34 struct wlr_box *clip_box;
35}; 35};
36 36
37/** 37/**
@@ -52,7 +52,7 @@ static int scale_length(int length, int offset, float scale) {
52 52
53static void scissor_output(struct wlr_output *wlr_output, 53static void scissor_output(struct wlr_output *wlr_output,
54 pixman_box32_t *rect) { 54 pixman_box32_t *rect) {
55 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); 55 struct wlr_renderer *renderer = wlr_output->renderer;
56 assert(renderer); 56 assert(renderer);
57 57
58 struct wlr_box box = { 58 struct wlr_box box = {
@@ -100,13 +100,9 @@ static void render_texture(struct wlr_output *wlr_output,
100 pixman_region32_t *output_damage, struct wlr_texture *texture, 100 pixman_region32_t *output_damage, struct wlr_texture *texture,
101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box, 101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
102 const float matrix[static 9], float alpha) { 102 const float matrix[static 9], float alpha) {
103 struct wlr_renderer *renderer = 103 struct wlr_renderer *renderer = wlr_output->renderer;
104 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data; 104 struct sway_output *output = wlr_output->data;
106 105
107 struct wlr_gles2_texture_attribs attribs;
108 wlr_gles2_texture_get_attribs(texture, &attribs);
109
110 pixman_region32_t damage; 106 pixman_region32_t damage;
111 pixman_region32_init(&damage); 107 pixman_region32_init(&damage);
112 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, 108 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
@@ -133,9 +129,9 @@ damage_finish:
133 pixman_region32_fini(&damage); 129 pixman_region32_fini(&damage);
134} 130}
135 131
136static void render_surface_iterator(struct sway_output *output, struct sway_view *view, 132static void render_surface_iterator(struct sway_output *output,
137 struct wlr_surface *surface, struct wlr_box *_box, float rotation, 133 struct sway_view *view, struct wlr_surface *surface,
138 void *_data) { 134 struct wlr_box *_box, void *_data) {
139 struct render_data *data = _data; 135 struct render_data *data = _data;
140 struct wlr_output *wlr_output = output->wlr_output; 136 struct wlr_output *wlr_output = output->wlr_output;
141 pixman_region32_t *output_damage = data->damage; 137 pixman_region32_t *output_damage = data->damage;
@@ -149,15 +145,23 @@ static void render_surface_iterator(struct sway_output *output, struct sway_view
149 struct wlr_fbox src_box; 145 struct wlr_fbox src_box;
150 wlr_surface_get_buffer_source_box(surface, &src_box); 146 wlr_surface_get_buffer_source_box(surface, &src_box);
151 147
152 struct wlr_box dst_box = *_box; 148 struct wlr_box proj_box = *_box;
153 scale_box(&dst_box, wlr_output->scale); 149 scale_box(&proj_box, wlr_output->scale);
154 150
155 float matrix[9]; 151 float matrix[9];
156 enum wl_output_transform transform = 152 enum wl_output_transform transform =
157 wlr_output_transform_invert(surface->current.transform); 153 wlr_output_transform_invert(surface->current.transform);
158 wlr_matrix_project_box(matrix, &dst_box, transform, rotation, 154 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
159 wlr_output->transform_matrix); 155 wlr_output->transform_matrix);
160 156
157 struct wlr_box dst_box = *_box;
158 struct wlr_box *clip_box = data->clip_box;
159 if (clip_box != NULL) {
160 dst_box.width = fmin(dst_box.width, clip_box->width);
161 dst_box.height = fmin(dst_box.height, clip_box->height);
162 }
163 scale_box(&dst_box, wlr_output->scale);
164
161 render_texture(wlr_output, output_damage, texture, 165 render_texture(wlr_output, output_damage, texture,
162 &src_box, &dst_box, matrix, alpha); 166 &src_box, &dst_box, matrix, alpha);
163 167
@@ -213,8 +217,7 @@ void render_rect(struct sway_output *output,
213 pixman_region32_t *output_damage, const struct wlr_box *_box, 217 pixman_region32_t *output_damage, const struct wlr_box *_box,
214 float color[static 4]) { 218 float color[static 4]) {
215 struct wlr_output *wlr_output = output->wlr_output; 219 struct wlr_output *wlr_output = output->wlr_output;
216 struct wlr_renderer *renderer = 220 struct wlr_renderer *renderer = wlr_output->renderer;
217 wlr_backend_get_renderer(wlr_output->backend);
218 221
219 struct wlr_box box; 222 struct wlr_box box;
220 memcpy(&box, _box, sizeof(struct wlr_box)); 223 memcpy(&box, _box, sizeof(struct wlr_box));
@@ -256,6 +259,14 @@ static void render_view_toplevels(struct sway_view *view,
256 .damage = damage, 259 .damage = damage,
257 .alpha = alpha, 260 .alpha = alpha,
258 }; 261 };
262 struct wlr_box clip_box;
263 if (!container_is_current_floating(view->container)) {
264 // As we pass the geometry offsets to the surface iterator, we will
265 // need to account for the offsets in the clip dimensions.
266 clip_box.width = view->container->current.content_width + view->geometry.x;
267 clip_box.height = view->container->current.content_height + view->geometry.y;
268 data.clip_box = &clip_box;
269 }
259 // Render all toplevels without descending into popups 270 // Render all toplevels without descending into popups
260 double ox = view->container->surface_x - 271 double ox = view->container->surface_x -
261 output->lx - view->geometry.x; 272 output->lx - view->geometry.x;
@@ -282,17 +293,18 @@ static void render_saved_view(struct sway_view *view,
282 if (wl_list_empty(&view->saved_buffers)) { 293 if (wl_list_empty(&view->saved_buffers)) {
283 return; 294 return;
284 } 295 }
296
297 bool floating = container_is_current_floating(view->container);
298
285 struct sway_saved_buffer *saved_buf; 299 struct sway_saved_buffer *saved_buf;
286 wl_list_for_each(saved_buf, &view->saved_buffers, link) { 300 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
287 if (!saved_buf->buffer->texture) { 301 if (!saved_buf->buffer->texture) {
288 continue; 302 continue;
289 } 303 }
290 304
291 struct wlr_box box = { 305 struct wlr_box proj_box = {
292 .x = view->container->surface_x - output->lx - 306 .x = saved_buf->x - view->saved_geometry.x - output->lx,
293 view->saved_geometry.x + saved_buf->x, 307 .y = saved_buf->y - view->saved_geometry.y - output->ly,
294 .y = view->container->surface_y - output->ly -
295 view->saved_geometry.y + saved_buf->y,
296 .width = saved_buf->width, 308 .width = saved_buf->width,
297 .height = saved_buf->height, 309 .height = saved_buf->height,
298 }; 310 };
@@ -303,20 +315,31 @@ static void render_saved_view(struct sway_view *view,
303 }; 315 };
304 316
305 struct wlr_box intersection; 317 struct wlr_box intersection;
306 bool intersects = wlr_box_intersection(&intersection, &output_box, &box); 318 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
307 if (!intersects) { 319 if (!intersects) {
308 continue; 320 continue;
309 } 321 }
310 322
311 scale_box(&box, wlr_output->scale); 323 struct wlr_box dst_box = proj_box;
324 scale_box(&proj_box, wlr_output->scale);
312 325
313 float matrix[9]; 326 float matrix[9];
314 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); 327 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
315 wlr_matrix_project_box(matrix, &box, transform, 0, 328 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
316 wlr_output->transform_matrix); 329 wlr_output->transform_matrix);
317 330
331 if (!floating) {
332 dst_box.width = fmin(dst_box.width,
333 view->container->current.content_width -
334 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
335 dst_box.height = fmin(dst_box.height,
336 view->container->current.content_height -
337 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
338 }
339 scale_box(&dst_box, wlr_output->scale);
340
318 render_texture(wlr_output, damage, saved_buf->buffer->texture, 341 render_texture(wlr_output, damage, saved_buf->buffer->texture,
319 &saved_buf->source_box, &box, matrix, alpha); 342 &saved_buf->source_box, &dst_box, matrix, alpha);
320 } 343 }
321 344
322 // FIXME: we should set the surface that this saved buffer originates from 345 // FIXME: we should set the surface that this saved buffer originates from
@@ -348,8 +371,8 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
348 if (state->border_left) { 371 if (state->border_left) {
349 memcpy(&color, colors->child_border, sizeof(float) * 4); 372 memcpy(&color, colors->child_border, sizeof(float) * 4);
350 premultiply_alpha(color, con->alpha); 373 premultiply_alpha(color, con->alpha);
351 box.x = state->x; 374 box.x = floor(state->x);
352 box.y = state->content_y; 375 box.y = floor(state->content_y);
353 box.width = state->border_thickness; 376 box.width = state->border_thickness;
354 box.height = state->content_height; 377 box.height = state->content_height;
355 scale_box(&box, output_scale); 378 scale_box(&box, output_scale);
@@ -361,14 +384,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
361 container_current_parent_layout(con); 384 container_current_parent_layout(con);
362 385
363 if (state->border_right) { 386 if (state->border_right) {
364 if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { 387 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
365 memcpy(&color, colors->indicator, sizeof(float) * 4); 388 memcpy(&color, colors->indicator, sizeof(float) * 4);
366 } else { 389 } else {
367 memcpy(&color, colors->child_border, sizeof(float) * 4); 390 memcpy(&color, colors->child_border, sizeof(float) * 4);
368 } 391 }
369 premultiply_alpha(color, con->alpha); 392 premultiply_alpha(color, con->alpha);
370 box.x = state->content_x + state->content_width; 393 box.x = floor(state->content_x + state->content_width);
371 box.y = state->content_y; 394 box.y = floor(state->content_y);
372 box.width = state->border_thickness; 395 box.width = state->border_thickness;
373 box.height = state->content_height; 396 box.height = state->content_height;
374 scale_box(&box, output_scale); 397 scale_box(&box, output_scale);
@@ -376,14 +399,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
376 } 399 }
377 400
378 if (state->border_bottom) { 401 if (state->border_bottom) {
379 if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { 402 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
380 memcpy(&color, colors->indicator, sizeof(float) * 4); 403 memcpy(&color, colors->indicator, sizeof(float) * 4);
381 } else { 404 } else {
382 memcpy(&color, colors->child_border, sizeof(float) * 4); 405 memcpy(&color, colors->child_border, sizeof(float) * 4);
383 } 406 }
384 premultiply_alpha(color, con->alpha); 407 premultiply_alpha(color, con->alpha);
385 box.x = state->x; 408 box.x = floor(state->x);
386 box.y = state->content_y + state->content_height; 409 box.y = floor(state->content_y + state->content_height);
387 box.width = state->width; 410 box.width = state->width;
388 box.height = state->border_thickness; 411 box.height = state->border_thickness;
389 scale_box(&box, output_scale); 412 scale_box(&box, output_scale);
@@ -464,9 +487,10 @@ static void render_titlebar(struct sway_output *output,
464 int ob_marks_x = 0; // output-buffer-local 487 int ob_marks_x = 0; // output-buffer-local
465 int ob_marks_width = 0; // output-buffer-local 488 int ob_marks_width = 0; // output-buffer-local
466 if (config->show_marks && marks_texture) { 489 if (config->show_marks && marks_texture) {
467 struct wlr_box texture_box; 490 struct wlr_box texture_box = {
468 wlr_texture_get_size(marks_texture, 491 .width = marks_texture->width,
469 &texture_box.width, &texture_box.height); 492 .height = marks_texture->height,
493 };
470 ob_marks_width = texture_box.width; 494 ob_marks_width = texture_box.width;
471 495
472 // The marks texture might be shorter than the config->font_height, in 496 // The marks texture might be shorter than the config->font_height, in
@@ -517,15 +541,23 @@ static void render_titlebar(struct sway_output *output,
517 int ob_title_x = 0; // output-buffer-local 541 int ob_title_x = 0; // output-buffer-local
518 int ob_title_width = 0; // output-buffer-local 542 int ob_title_width = 0; // output-buffer-local
519 if (title_texture) { 543 if (title_texture) {
520 struct wlr_box texture_box; 544 struct wlr_box texture_box = {
521 wlr_texture_get_size(title_texture, 545 .width = title_texture->width,
522 &texture_box.width, &texture_box.height); 546 .height = title_texture->height,
547 };
548
549 // The effective output may be NULL when con is not on any output.
550 // This can happen because we render all children of containers,
551 // even those that are out of the bounds of any output.
552 struct sway_output *effective = container_get_effective_output(con);
553 float title_scale = effective ? effective->wlr_output->scale : output_scale;
554 texture_box.width = texture_box.width * output_scale / title_scale;
555 texture_box.height = texture_box.height * output_scale / title_scale;
523 ob_title_width = texture_box.width; 556 ob_title_width = texture_box.width;
524 557
525 // The title texture might be shorter than the config->font_height, 558 // The title texture might be shorter than the config->font_height,
526 // in which case we need to pad it above and below. 559 // in which case we need to pad it above and below.
527 int ob_padding_above = round((config->font_baseline - 560 int ob_padding_above = round((titlebar_v_padding -
528 con->title_baseline + titlebar_v_padding -
529 titlebar_border_thickness) * output_scale); 561 titlebar_border_thickness) * output_scale);
530 int ob_padding_below = ob_bg_height - ob_padding_above - 562 int ob_padding_below = ob_bg_height - ob_padding_above -
531 texture_box.height; 563 texture_box.height;
@@ -660,8 +692,8 @@ static void render_top_border(struct sway_output *output,
660 // Child border - top edge 692 // Child border - top edge
661 memcpy(&color, colors->child_border, sizeof(float) * 4); 693 memcpy(&color, colors->child_border, sizeof(float) * 4);
662 premultiply_alpha(color, con->alpha); 694 premultiply_alpha(color, con->alpha);
663 box.x = state->x; 695 box.x = floor(state->x);
664 box.y = state->y; 696 box.y = floor(state->y);
665 box.width = state->width; 697 box.width = state->width;
666 box.height = state->border_thickness; 698 box.height = state->border_thickness;
667 scale_box(&box, output_scale); 699 scale_box(&box, output_scale);
@@ -716,8 +748,8 @@ static void render_containers_linear(struct sway_output *output,
716 } 748 }
717 749
718 if (state->border == B_NORMAL) { 750 if (state->border == B_NORMAL) {
719 render_titlebar(output, damage, child, state->x, 751 render_titlebar(output, damage, child, floor(state->x),
720 state->y, state->width, colors, 752 floor(state->y), state->width, colors,
721 title_texture, marks_texture); 753 title_texture, marks_texture);
722 } else if (state->border == B_PIXEL) { 754 } else if (state->border == B_PIXEL) {
723 render_top_border(output, damage, child, colors); 755 render_top_border(output, damage, child, colors);
@@ -730,6 +762,14 @@ static void render_containers_linear(struct sway_output *output,
730 } 762 }
731} 763}
732 764
765static bool container_is_focused(struct sway_container *con, void *data) {
766 return con->current.focused;
767}
768
769static bool container_has_focused_child(struct sway_container *con) {
770 return container_find_child(con, container_is_focused, NULL);
771}
772
733/** 773/**
734 * Render a container's children using the L_TABBED layout. 774 * Render a container's children using the L_TABBED layout.
735 */ 775 */
@@ -761,6 +801,10 @@ static void render_containers_tabbed(struct sway_output *output,
761 colors = &config->border_colors.focused; 801 colors = &config->border_colors.focused;
762 title_texture = child->title_focused; 802 title_texture = child->title_focused;
763 marks_texture = child->marks_focused; 803 marks_texture = child->marks_focused;
804 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
805 colors = &config->border_colors.focused_tab_title;
806 title_texture = child->title_focused_tab_title;
807 marks_texture = child->marks_focused_tab_title;
764 } else if (child == parent->active_child) { 808 } else if (child == parent->active_child) {
765 colors = &config->border_colors.focused_inactive; 809 colors = &config->border_colors.focused_inactive;
766 title_texture = child->title_focused_inactive; 810 title_texture = child->title_focused_inactive;
@@ -771,7 +815,7 @@ static void render_containers_tabbed(struct sway_output *output,
771 marks_texture = child->marks_unfocused; 815 marks_texture = child->marks_unfocused;
772 } 816 }
773 817
774 int x = cstate->x + tab_width * i; 818 int x = floor(cstate->x + tab_width * i);
775 819
776 // Make last tab use the remaining width of the parent 820 // Make last tab use the remaining width of the parent
777 if (i == parent->children->length - 1) { 821 if (i == parent->children->length - 1) {
@@ -826,7 +870,11 @@ static void render_containers_stacked(struct sway_output *output,
826 colors = &config->border_colors.focused; 870 colors = &config->border_colors.focused;
827 title_texture = child->title_focused; 871 title_texture = child->title_focused;
828 marks_texture = child->marks_focused; 872 marks_texture = child->marks_focused;
829 } else if (child == parent->active_child) { 873 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
874 colors = &config->border_colors.focused_tab_title;
875 title_texture = child->title_focused_tab_title;
876 marks_texture = child->marks_focused_tab_title;
877 } else if (child == parent->active_child) {
830 colors = &config->border_colors.focused_inactive; 878 colors = &config->border_colors.focused_inactive;
831 title_texture = child->title_focused_inactive; 879 title_texture = child->title_focused_inactive;
832 marks_texture = child->marks_focused_inactive; 880 marks_texture = child->marks_focused_inactive;
@@ -884,8 +932,8 @@ static void render_container(struct sway_output *output,
884 struct parent_data data = { 932 struct parent_data data = {
885 .layout = con->current.layout, 933 .layout = con->current.layout,
886 .box = { 934 .box = {
887 .x = con->current.x, 935 .x = floor(con->current.x),
888 .y = con->current.y, 936 .y = floor(con->current.y),
889 .width = con->current.width, 937 .width = con->current.width,
890 .height = con->current.height, 938 .height = con->current.height,
891 }, 939 },
@@ -901,8 +949,8 @@ static void render_workspace(struct sway_output *output,
901 struct parent_data data = { 949 struct parent_data data = {
902 .layout = ws->current.layout, 950 .layout = ws->current.layout,
903 .box = { 951 .box = {
904 .x = ws->current.x, 952 .x = floor(ws->current.x),
905 .y = ws->current.y, 953 .y = floor(ws->current.y),
906 .width = ws->current.width, 954 .width = ws->current.width,
907 .height = ws->current.height, 955 .height = ws->current.height,
908 }, 956 },
@@ -936,8 +984,8 @@ static void render_floating_container(struct sway_output *soutput,
936 } 984 }
937 985
938 if (con->current.border == B_NORMAL) { 986 if (con->current.border == B_NORMAL) {
939 render_titlebar(soutput, damage, con, con->current.x, 987 render_titlebar(soutput, damage, con, floor(con->current.x),
940 con->current.y, con->current.width, colors, 988 floor(con->current.y), con->current.width, colors,
941 title_texture, marks_texture); 989 title_texture, marks_texture);
942 } else if (con->current.border == B_PIXEL) { 990 } else if (con->current.border == B_PIXEL) {
943 render_top_border(soutput, damage, con, colors); 991 render_top_border(soutput, damage, con, colors);
@@ -959,7 +1007,7 @@ static void render_floating(struct sway_output *soutput,
959 } 1007 }
960 for (int k = 0; k < ws->current.floating->length; ++k) { 1008 for (int k = 0; k < ws->current.floating->length; ++k) {
961 struct sway_container *floater = ws->current.floating->items[k]; 1009 struct sway_container *floater = ws->current.floating->items[k];
962 if (floater->fullscreen_mode != FULLSCREEN_NONE) { 1010 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
963 continue; 1011 continue;
964 } 1012 }
965 render_floating_container(soutput, damage, floater); 1013 render_floating_container(soutput, damage, floater);
@@ -979,13 +1027,7 @@ static void render_seatops(struct sway_output *output,
979void output_render(struct sway_output *output, struct timespec *when, 1027void output_render(struct sway_output *output, struct timespec *when,
980 pixman_region32_t *damage) { 1028 pixman_region32_t *damage) {
981 struct wlr_output *wlr_output = output->wlr_output; 1029 struct wlr_output *wlr_output = output->wlr_output;
982 1030 struct wlr_renderer *renderer = output->server->renderer;
983 struct wlr_renderer *renderer =
984 wlr_backend_get_renderer(wlr_output->backend);
985 if (!sway_assert(renderer != NULL,
986 "expected the output backend to have a renderer")) {
987 return;
988 }
989 1031
990 struct sway_workspace *workspace = output->current.active_workspace; 1032 struct sway_workspace *workspace = output->current.active_workspace;
991 if (workspace == NULL) { 1033 if (workspace == NULL) {
@@ -999,6 +1041,12 @@ void output_render(struct sway_output *output, struct timespec *when,
999 1041
1000 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); 1042 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1001 1043
1044 if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1002 if (!pixman_region32_not_empty(damage)) { 1050 if (!pixman_region32_not_empty(damage)) {
1003 // Output isn't damaged but needs buffer swap 1051 // Output isn't damaged but needs buffer swap
1004 goto renderer_end; 1052 goto renderer_end;
@@ -1006,10 +1054,6 @@ void output_render(struct sway_output *output, struct timespec *when,
1006 1054
1007 if (debug.damage == DAMAGE_HIGHLIGHT) { 1055 if (debug.damage == DAMAGE_HIGHLIGHT) {
1008 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); 1056 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1009 } else if (debug.damage == DAMAGE_RERENDER) {
1010 int width, height;
1011 wlr_output_transformed_resolution(wlr_output, &width, &height);
1012 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1013 } 1057 }
1014 1058
1015 if (output_has_opaque_overlay_layer_surface(output)) { 1059 if (output_has_opaque_overlay_layer_surface(output)) {
@@ -1110,7 +1154,7 @@ renderer_end:
1110 wlr_region_transform(&frame_damage, &output->damage->current, 1154 wlr_region_transform(&frame_damage, &output->damage->current,
1111 transform, width, height); 1155 transform, width, height);
1112 1156
1113 if (debug.damage == DAMAGE_HIGHLIGHT) { 1157 if (debug.damage != DAMAGE_DEFAULT) {
1114 pixman_region32_union_rect(&frame_damage, &frame_damage, 1158 pixman_region32_union_rect(&frame_damage, &frame_damage,
1115 0, 0, wlr_output->width, wlr_output->height); 1159 0, 0, wlr_output->width, wlr_output->height);
1116 } 1160 }
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
index 767b2045..1d7b536d 100644
--- a/sway/desktop/surface.c
+++ b/sway/desktop/surface.c
@@ -1,7 +1,7 @@
1#define _POSIX_C_SOURCE 200112L 1#define _POSIX_C_SOURCE 200112L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <time.h> 3#include <time.h>
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_compositor.h>
5#include "sway/server.h" 5#include "sway/server.h"
6#include "sway/surface.h" 6#include "sway/surface.h"
7 7
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index eac38991..f5a3a053 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -35,6 +35,8 @@ struct sway_transaction_instruction {
35 struct sway_container_state container_state; 35 struct sway_container_state container_state;
36 }; 36 };
37 uint32_t serial; 37 uint32_t serial;
38 bool server_request;
39 bool waiting;
38}; 40};
39 41
40static struct sway_transaction *transaction_create(void) { 42static struct sway_transaction *transaction_create(void) {
@@ -86,7 +88,11 @@ static void transaction_destroy(struct sway_transaction *transaction) {
86static void copy_output_state(struct sway_output *output, 88static void copy_output_state(struct sway_output *output,
87 struct sway_transaction_instruction *instruction) { 89 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state = &instruction->output_state; 90 struct sway_output_state *state = &instruction->output_state;
89 state->workspaces = create_list(); 91 if (state->workspaces) {
92 state->workspaces->length = 0;
93 } else {
94 state->workspaces = create_list();
95 }
90 list_cat(state->workspaces, output->workspaces); 96 list_cat(state->workspaces, output->workspaces);
91 97
92 state->active_workspace = output_get_active_workspace(output); 98 state->active_workspace = output_get_active_workspace(output);
@@ -104,8 +110,16 @@ static void copy_workspace_state(struct sway_workspace *ws,
104 state->layout = ws->layout; 110 state->layout = ws->layout;
105 111
106 state->output = ws->output; 112 state->output = ws->output;
107 state->floating = create_list(); 113 if (state->floating) {
108 state->tiling = create_list(); 114 state->floating->length = 0;
115 } else {
116 state->floating = create_list();
117 }
118 if (state->tiling) {
119 state->tiling->length = 0;
120 } else {
121 state->tiling = create_list();
122 }
109 list_cat(state->floating, ws->floating); 123 list_cat(state->floating, ws->floating);
110 list_cat(state->tiling, ws->tiling); 124 list_cat(state->tiling, ws->tiling);
111 125
@@ -115,8 +129,8 @@ static void copy_workspace_state(struct sway_workspace *ws,
115 // Set focused_inactive_child to the direct tiling child 129 // Set focused_inactive_child to the direct tiling child
116 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); 130 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws);
117 if (focus) { 131 if (focus) {
118 while (focus->parent) { 132 while (focus->pending.parent) {
119 focus = focus->parent; 133 focus = focus->pending.parent;
120 } 134 }
121 } 135 }
122 state->focused_inactive_child = focus; 136 state->focused_inactive_child = focus;
@@ -126,28 +140,19 @@ static void copy_container_state(struct sway_container *container,
126 struct sway_transaction_instruction *instruction) { 140 struct sway_transaction_instruction *instruction) {
127 struct sway_container_state *state = &instruction->container_state; 141 struct sway_container_state *state = &instruction->container_state;
128 142
129 state->layout = container->layout; 143 if (state->children) {
130 state->x = container->x; 144 list_free(state->children);
131 state->y = container->y; 145 }
132 state->width = container->width; 146
133 state->height = container->height; 147 memcpy(state, &container->pending, sizeof(struct sway_container_state));
134 state->fullscreen_mode = container->fullscreen_mode;
135 state->parent = container->parent;
136 state->workspace = container->workspace;
137 state->border = container->border;
138 state->border_thickness = container->border_thickness;
139 state->border_top = container->border_top;
140 state->border_left = container->border_left;
141 state->border_right = container->border_right;
142 state->border_bottom = container->border_bottom;
143 state->content_x = container->content_x;
144 state->content_y = container->content_y;
145 state->content_width = container->content_width;
146 state->content_height = container->content_height;
147 148
148 if (!container->view) { 149 if (!container->view) {
150 // We store a copy of the child list to avoid having it mutated after
151 // we copy the state.
149 state->children = create_list(); 152 state->children = create_list();
150 list_cat(state->children, container->children); 153 list_cat(state->children, container->pending.children);
154 } else {
155 state->children = NULL;
151 } 156 }
152 157
153 struct sway_seat *seat = input_manager_current_seat(); 158 struct sway_seat *seat = input_manager_current_seat();
@@ -161,14 +166,36 @@ static void copy_container_state(struct sway_container *container,
161} 166}
162 167
163static void transaction_add_node(struct sway_transaction *transaction, 168static void transaction_add_node(struct sway_transaction *transaction,
164 struct sway_node *node) { 169 struct sway_node *node, bool server_request) {
165 struct sway_transaction_instruction *instruction = 170 struct sway_transaction_instruction *instruction = NULL;
166 calloc(1, sizeof(struct sway_transaction_instruction)); 171
167 if (!sway_assert(instruction, "Unable to allocate instruction")) { 172 // Check if we have an instruction for this node already, in which case we
168 return; 173 // update that instead of creating a new one.
174 if (node->ntxnrefs > 0) {
175 for (int idx = 0; idx < transaction->instructions->length; idx++) {
176 struct sway_transaction_instruction *other =
177 transaction->instructions->items[idx];
178 if (other->node == node) {
179 instruction = other;
180 break;
181 }
182 }
183 }
184
185 if (!instruction) {
186 instruction = calloc(1, sizeof(struct sway_transaction_instruction));
187 if (!sway_assert(instruction, "Unable to allocate instruction")) {
188 return;
189 }
190 instruction->transaction = transaction;
191 instruction->node = node;
192 instruction->server_request = server_request;
193
194 list_add(transaction->instructions, instruction);
195 node->ntxnrefs++;
196 } else if (server_request) {
197 instruction->server_request = true;
169 } 198 }
170 instruction->transaction = transaction;
171 instruction->node = node;
172 199
173 switch (node->type) { 200 switch (node->type) {
174 case N_ROOT: 201 case N_ROOT:
@@ -183,9 +210,6 @@ static void transaction_add_node(struct sway_transaction *transaction,
183 copy_container_state(node->sway_container, instruction); 210 copy_container_state(node->sway_container, instruction);
184 break; 211 break;
185 } 212 }
186
187 list_add(transaction->instructions, instruction);
188 node->ntxnrefs++;
189} 213}
190 214
191static void apply_output_state(struct sway_output *output, 215static void apply_output_state(struct sway_output *output,
@@ -214,8 +238,8 @@ static void apply_container_state(struct sway_container *container,
214 struct sway_saved_buffer *saved_buf; 238 struct sway_saved_buffer *saved_buf;
215 wl_list_for_each(saved_buf, &view->saved_buffers, link) { 239 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
216 struct wlr_box box = { 240 struct wlr_box box = {
217 .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, 241 .x = saved_buf->x - view->saved_geometry.x,
218 .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, 242 .y = saved_buf->y - view->saved_geometry.y,
219 .width = saved_buf->width, 243 .width = saved_buf->width,
220 .height = saved_buf->height, 244 .height = saved_buf->height,
221 }; 245 };
@@ -238,6 +262,13 @@ static void apply_container_state(struct sway_container *container,
238 } 262 }
239 } 263 }
240 264
265 // If the view hasn't responded to the configure, center it within
266 // the container. This is important for fullscreen views which
267 // refuse to resize to the size of the output.
268 if (view && view->surface) {
269 view_center_surface(view);
270 }
271
241 // Damage the new location 272 // Damage the new location
242 desktop_damage_whole_container(container); 273 desktop_damage_whole_container(container);
243 if (view && view->surface) { 274 if (view && view->surface) {
@@ -251,24 +282,6 @@ static void apply_container_state(struct sway_container *container,
251 desktop_damage_box(&box); 282 desktop_damage_box(&box);
252 } 283 }
253 284
254 // If the view hasn't responded to the configure, center it within
255 // the container. This is important for fullscreen views which
256 // refuse to resize to the size of the output.
257 if (view && view->surface) {
258 if (view->geometry.width < container->current.content_width) {
259 container->surface_x = container->current.content_x +
260 (container->current.content_width - view->geometry.width) / 2;
261 } else {
262 container->surface_x = container->current.content_x;
263 }
264 if (view->geometry.height < container->current.content_height) {
265 container->surface_y = container->current.content_y +
266 (container->current.content_height - view->geometry.height) / 2;
267 } else {
268 container->surface_y = container->current.content_y;
269 }
270 }
271
272 if (!container->node.destroying) { 285 if (!container->node.destroying) {
273 container_discover_outputs(container); 286 container_discover_outputs(container);
274 } 287 }
@@ -317,70 +330,25 @@ static void transaction_apply(struct sway_transaction *transaction) {
317 cursor_rebase_all(); 330 cursor_rebase_all();
318} 331}
319 332
320static void transaction_commit(struct sway_transaction *transaction); 333static void transaction_commit_pending(void);
321 334
322// Return true if both transactions operate on the same nodes 335static void transaction_progress(void) {
323static bool transaction_same_nodes(struct sway_transaction *a, 336 if (!server.queued_transaction) {
324 struct sway_transaction *b) {
325 if (a->instructions->length != b->instructions->length) {
326 return false;
327 }
328 for (int i = 0; i < a->instructions->length; ++i) {
329 struct sway_transaction_instruction *a_inst = a->instructions->items[i];
330 struct sway_transaction_instruction *b_inst = b->instructions->items[i];
331 if (a_inst->node != b_inst->node) {
332 return false;
333 }
334 }
335 return true;
336}
337
338static void transaction_progress_queue(void) {
339 if (!server.transactions->length) {
340 return; 337 return;
341 } 338 }
342 // Only the first transaction in the queue is committed, so that's the one 339 if (server.queued_transaction->num_waiting > 0) {
343 // we try to process.
344 struct sway_transaction *transaction = server.transactions->items[0];
345 if (transaction->num_waiting) {
346 return; 340 return;
347 } 341 }
348 transaction_apply(transaction); 342 transaction_apply(server.queued_transaction);
349 transaction_destroy(transaction); 343 transaction_destroy(server.queued_transaction);
350 list_del(server.transactions, 0); 344 server.queued_transaction = NULL;
351 345
352 if (server.transactions->length == 0) { 346 if (!server.pending_transaction) {
353 // The transaction queue is empty, so we're done.
354 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 347 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
355 return; 348 return;
356 } 349 }
357 350
358 // If there's a bunch of consecutive transactions which all apply to the 351 transaction_commit_pending();
359 // same views, skip all except the last one.
360 while (server.transactions->length >= 2) {
361 struct sway_transaction *txn = server.transactions->items[0];
362 struct sway_transaction *dup = NULL;
363
364 for (int i = 1; i < server.transactions->length; i++) {
365 struct sway_transaction *maybe_dup = server.transactions->items[i];
366 if (transaction_same_nodes(txn, maybe_dup)) {
367 dup = maybe_dup;
368 break;
369 }
370 }
371
372 if (dup) {
373 list_del(server.transactions, 0);
374 transaction_destroy(txn);
375 } else {
376 break;
377 }
378 }
379
380 // We again commit the first transaction in the queue to process it.
381 transaction = server.transactions->items[0];
382 transaction_commit(transaction);
383 transaction_progress_queue();
384} 352}
385 353
386static int handle_timeout(void *data) { 354static int handle_timeout(void *data) {
@@ -388,7 +356,7 @@ static int handle_timeout(void *data) {
388 sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", 356 sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)",
389 transaction, transaction->num_waiting); 357 transaction, transaction->num_waiting);
390 transaction->num_waiting = 0; 358 transaction->num_waiting = 0;
391 transaction_progress_queue(); 359 transaction_progress();
392 return 0; 360 return 0;
393} 361}
394 362
@@ -400,6 +368,9 @@ static bool should_configure(struct sway_node *node,
400 if (node->destroying) { 368 if (node->destroying) {
401 return false; 369 return false;
402 } 370 }
371 if (!instruction->server_request) {
372 return false;
373 }
403 struct sway_container_state *cstate = &node->sway_container->current; 374 struct sway_container_state *cstate = &node->sway_container->current;
404 struct sway_container_state *istate = &instruction->container_state; 375 struct sway_container_state *istate = &instruction->container_state;
405#if HAVE_XWAYLAND 376#if HAVE_XWAYLAND
@@ -431,13 +402,18 @@ static void transaction_commit(struct sway_transaction *transaction) {
431 struct sway_transaction_instruction *instruction = 402 struct sway_transaction_instruction *instruction =
432 transaction->instructions->items[i]; 403 transaction->instructions->items[i];
433 struct sway_node *node = instruction->node; 404 struct sway_node *node = instruction->node;
405 bool hidden = node_is_view(node) && !node->destroying &&
406 !view_is_visible(node->sway_container->view);
434 if (should_configure(node, instruction)) { 407 if (should_configure(node, instruction)) {
435 instruction->serial = view_configure(node->sway_container->view, 408 instruction->serial = view_configure(node->sway_container->view,
436 instruction->container_state.content_x, 409 instruction->container_state.content_x,
437 instruction->container_state.content_y, 410 instruction->container_state.content_y,
438 instruction->container_state.content_width, 411 instruction->container_state.content_width,
439 instruction->container_state.content_height); 412 instruction->container_state.content_height);
440 ++transaction->num_waiting; 413 if (!hidden) {
414 instruction->waiting = true;
415 ++transaction->num_waiting;
416 }
441 417
442 // From here on we are rendering a saved buffer of the view, which 418 // From here on we are rendering a saved buffer of the view, which
443 // means we can send a frame done event to make the client redraw it 419 // means we can send a frame done event to make the client redraw it
@@ -448,7 +424,8 @@ static void transaction_commit(struct sway_transaction *transaction) {
448 wlr_surface_send_frame_done( 424 wlr_surface_send_frame_done(
449 node->sway_container->view->surface, &now); 425 node->sway_container->view->surface, &now);
450 } 426 }
451 if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { 427 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) {
452 view_save_buffer(node->sway_container->view); 429 view_save_buffer(node->sway_container->view);
453 memcpy(&node->sway_container->view->saved_geometry, 430 memcpy(&node->sway_container->view->saved_geometry,
454 &node->sway_container->view->geometry, 431 &node->sway_container->view->geometry,
@@ -483,6 +460,17 @@ static void transaction_commit(struct sway_transaction *transaction) {
483 } 460 }
484} 461}
485 462
463static void transaction_commit_pending(void) {
464 if (server.queued_transaction) {
465 return;
466 }
467 struct sway_transaction *transaction = server.pending_transaction;
468 server.pending_transaction = NULL;
469 server.queued_transaction = transaction;
470 transaction_commit(transaction);
471 transaction_progress();
472}
473
486static void set_instruction_ready( 474static void set_instruction_ready(
487 struct sway_transaction_instruction *instruction) { 475 struct sway_transaction_instruction *instruction) {
488 struct sway_transaction *transaction = instruction->transaction; 476 struct sway_transaction *transaction = instruction->transaction;
@@ -501,13 +489,14 @@ static void set_instruction_ready(
501 } 489 }
502 490
503 // If the transaction has timed out then its num_waiting will be 0 already. 491 // If the transaction has timed out then its num_waiting will be 0 already.
504 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { 492 if (instruction->waiting && transaction->num_waiting > 0 &&
493 --transaction->num_waiting == 0) {
505 sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); 494 sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction);
506 wl_event_source_timer_update(transaction->timer, 0); 495 wl_event_source_timer_update(transaction->timer, 0);
507 } 496 }
508 497
509 instruction->node->instruction = NULL; 498 instruction->node->instruction = NULL;
510 transaction_progress_queue(); 499 transaction_progress();
511} 500}
512 501
513void transaction_notify_view_ready_by_serial(struct sway_view *view, 502void transaction_notify_view_ready_by_serial(struct sway_view *view,
@@ -532,36 +521,32 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view,
532 } 521 }
533} 522}
534 523
535void transaction_notify_view_ready_immediately(struct sway_view *view) { 524static void _transaction_commit_dirty(bool server_request) {
536 struct sway_transaction_instruction *instruction =
537 view->container->node.instruction;
538 if (instruction != NULL) {
539 set_instruction_ready(instruction);
540 }
541}
542
543void transaction_commit_dirty(void) {
544 if (!server.dirty_nodes->length) { 525 if (!server.dirty_nodes->length) {
545 return; 526 return;
546 } 527 }
547 struct sway_transaction *transaction = transaction_create(); 528
548 if (!transaction) { 529 if (!server.pending_transaction) {
549 return; 530 server.pending_transaction = transaction_create();
531 if (!server.pending_transaction) {
532 return;
533 }
550 } 534 }
535
551 for (int i = 0; i < server.dirty_nodes->length; ++i) { 536 for (int i = 0; i < server.dirty_nodes->length; ++i) {
552 struct sway_node *node = server.dirty_nodes->items[i]; 537 struct sway_node *node = server.dirty_nodes->items[i];
553 transaction_add_node(transaction, node); 538 transaction_add_node(server.pending_transaction, node, server_request);
554 node->dirty = false; 539 node->dirty = false;
555 } 540 }
556 server.dirty_nodes->length = 0; 541 server.dirty_nodes->length = 0;
557 542
558 list_add(server.transactions, transaction); 543 transaction_commit_pending();
544}
559 545
560 // We only commit the first transaction added to the queue. 546void transaction_commit_dirty(void) {
561 if (server.transactions->length == 1) { 547 _transaction_commit_dirty(true);
562 transaction_commit(transaction); 548}
563 // Attempting to progress the queue here is useful 549
564 // if the transaction has nothing to wait for. 550void transaction_commit_dirty_client(void) {
565 transaction_progress_queue(); 551 _transaction_commit_dirty(false);
566 }
567} 552}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 667fb9e5..5fae8296 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -21,18 +21,15 @@
21 21
22static const struct sway_view_child_impl popup_impl; 22static const struct sway_view_child_impl popup_impl;
23 23
24static void popup_get_root_coords(struct sway_view_child *child, 24static void popup_get_view_coords(struct sway_view_child *child,
25 int *root_sx, int *root_sy) { 25 int *sx, int *sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; 26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; 27 struct wlr_xdg_surface *surface = popup->wlr_xdg_surface;
28 28
29 int x_offset = -child->view->geometry.x - surface->geometry.x;
30 int y_offset = -child->view->geometry.y - surface->geometry.y;
31
32 wlr_xdg_popup_get_toplevel_coords(surface->popup, 29 wlr_xdg_popup_get_toplevel_coords(surface->popup,
33 x_offset + surface->popup->geometry.x, 30 surface->popup->geometry.x - surface->current.geometry.x,
34 y_offset + surface->popup->geometry.y, 31 surface->popup->geometry.y - surface->current.geometry.y,
35 root_sx, root_sy); 32 sx, sy);
36} 33}
37 34
38static void popup_destroy(struct sway_view_child *child) { 35static void popup_destroy(struct sway_view_child *child) {
@@ -47,7 +44,7 @@ static void popup_destroy(struct sway_view_child *child) {
47} 44}
48 45
49static const struct sway_view_child_impl popup_impl = { 46static const struct sway_view_child_impl popup_impl = {
50 .get_root_coords = popup_get_root_coords, 47 .get_view_coords = popup_get_view_coords,
51 .destroy = popup_destroy, 48 .destroy = popup_destroy,
52}; 49};
53 50
@@ -70,13 +67,13 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
70 struct sway_view *view = popup->child.view; 67 struct sway_view *view = popup->child.view;
71 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 68 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
72 69
73 struct sway_output *output = view->container->workspace->output; 70 struct sway_output *output = view->container->pending.workspace->output;
74 71
75 // the output box expressed in the coordinate system of the toplevel parent 72 // the output box expressed in the coordinate system of the toplevel parent
76 // of the popup 73 // of the popup
77 struct wlr_box output_toplevel_sx_box = { 74 struct wlr_box output_toplevel_sx_box = {
78 .x = output->lx - view->container->content_x, 75 .x = output->lx - view->container->pending.content_x + view->geometry.x,
79 .y = output->ly - view->container->content_y, 76 .y = output->ly - view->container->pending.content_y + view->geometry.y,
80 .width = output->width, 77 .width = output->width,
81 .height = output->height, 78 .height = output->height,
82 }; 79 };
@@ -293,19 +290,23 @@ static void handle_commit(struct wl_listener *listener, void *data) {
293 new_geo.y != view->geometry.y; 290 new_geo.y != view->geometry.y;
294 291
295 if (new_size) { 292 if (new_size) {
296 // The view has unexpectedly sent a new size 293 // The client changed its surface size in this commit. For floating
294 // containers, we resize the container to match. For tiling containers,
295 // we only recenter the surface.
297 desktop_damage_view(view); 296 desktop_damage_view(view);
298 view_update_size(view, new_geo.width, new_geo.height);
299 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 297 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
298 if (container_is_floating(view->container)) {
299 view_update_size(view);
300 transaction_commit_dirty_client();
301 } else {
302 view_center_surface(view);
303 }
300 desktop_damage_view(view); 304 desktop_damage_view(view);
301 transaction_commit_dirty();
302 } 305 }
303 306
304 if (view->container->node.instruction) { 307 if (view->container->node.instruction) {
305 transaction_notify_view_ready_by_serial(view, 308 transaction_notify_view_ready_by_serial(view,
306 xdg_surface->configure_serial); 309 xdg_surface->current.configure_serial);
307 } else if (new_size) {
308 transaction_notify_view_ready_immediately(view);
309 } 310 }
310 311
311 view_damage_from(view); 312 view_damage_from(view);
@@ -354,7 +355,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
354 if (e->fullscreen && e->output && e->output->data) { 355 if (e->fullscreen && e->output && e->output->data) {
355 struct sway_output *output = e->output->data; 356 struct sway_output *output = e->output->data;
356 struct sway_workspace *ws = output_get_active_workspace(output); 357 struct sway_workspace *ws = output_get_active_workspace(output);
357 if (ws && !container_is_scratchpad_hidden(container)) { 358 if (ws && !container_is_scratchpad_hidden(container) &&
359 container->pending.workspace != ws) {
358 if (container_is_floating(container)) { 360 if (container_is_floating(container)) {
359 workspace_add_floating(ws, container); 361 workspace_add_floating(ws, container);
360 } else { 362 } else {
@@ -369,11 +371,6 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
369 transaction_commit_dirty(); 371 transaction_commit_dirty();
370} 372}
371 373
372static void handle_request_maximize(struct wl_listener *listener, void *data) {
373 struct wlr_xdg_surface *surface = data;
374 wlr_xdg_surface_schedule_configure(surface);
375}
376
377static void handle_request_move(struct wl_listener *listener, void *data) { 374static void handle_request_move(struct wl_listener *listener, void *data) {
378 struct sway_xdg_shell_view *xdg_shell_view = 375 struct sway_xdg_shell_view *xdg_shell_view =
379 wl_container_of(listener, xdg_shell_view, request_move); 376 wl_container_of(listener, xdg_shell_view, request_move);
@@ -416,7 +413,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
416 wl_list_remove(&xdg_shell_view->commit.link); 413 wl_list_remove(&xdg_shell_view->commit.link);
417 wl_list_remove(&xdg_shell_view->new_popup.link); 414 wl_list_remove(&xdg_shell_view->new_popup.link);
418 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 415 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
419 wl_list_remove(&xdg_shell_view->request_maximize.link);
420 wl_list_remove(&xdg_shell_view->request_move.link); 416 wl_list_remove(&xdg_shell_view->request_move.link);
421 wl_list_remove(&xdg_shell_view->request_resize.link); 417 wl_list_remove(&xdg_shell_view->request_resize.link);
422 wl_list_remove(&xdg_shell_view->set_title.link); 418 wl_list_remove(&xdg_shell_view->set_title.link);
@@ -429,8 +425,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
429 struct sway_view *view = &xdg_shell_view->view; 425 struct sway_view *view = &xdg_shell_view->view;
430 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 426 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
431 427
432 view->natural_width = view->wlr_xdg_surface->geometry.width; 428 view->natural_width = view->wlr_xdg_surface->current.geometry.width;
433 view->natural_height = view->wlr_xdg_surface->geometry.height; 429 view->natural_height = view->wlr_xdg_surface->current.geometry.height;
434 if (!view->natural_width && !view->natural_height) { 430 if (!view->natural_width && !view->natural_height) {
435 view->natural_width = view->wlr_xdg_surface->surface->current.width; 431 view->natural_width = view->wlr_xdg_surface->surface->current.width;
436 view->natural_height = view->wlr_xdg_surface->surface->current.height; 432 view->natural_height = view->wlr_xdg_surface->surface->current.height;
@@ -438,17 +434,20 @@ static void handle_map(struct wl_listener *listener, void *data) {
438 434
439 bool csd = false; 435 bool csd = false;
440 436
441 if (!view->xdg_decoration) { 437 if (view->xdg_decoration) {
438 enum wlr_xdg_toplevel_decoration_v1_mode mode =
439 view->xdg_decoration->wlr_xdg_decoration->requested_mode;
440 csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
441 } else {
442 struct sway_server_decoration *deco = 442 struct sway_server_decoration *deco =
443 decoration_from_surface(xdg_surface->surface); 443 decoration_from_surface(xdg_surface->surface);
444 csd = !deco || deco->wlr_server_decoration->mode == 444 csd = !deco || deco->wlr_server_decoration->mode ==
445 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 445 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
446
447 } 446 }
448 447
449 view_map(view, view->wlr_xdg_surface->surface, 448 view_map(view, view->wlr_xdg_surface->surface,
450 xdg_surface->toplevel->client_pending.fullscreen, 449 xdg_surface->toplevel->requested.fullscreen,
451 xdg_surface->toplevel->client_pending.fullscreen_output, 450 xdg_surface->toplevel->requested.fullscreen_output,
452 csd); 451 csd);
453 452
454 transaction_commit_dirty(); 453 transaction_commit_dirty();
@@ -465,10 +464,6 @@ static void handle_map(struct wl_listener *listener, void *data) {
465 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, 464 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
466 &xdg_shell_view->request_fullscreen); 465 &xdg_shell_view->request_fullscreen);
467 466
468 xdg_shell_view->request_maximize.notify = handle_request_maximize;
469 wl_signal_add(&xdg_surface->toplevel->events.request_maximize,
470 &xdg_shell_view->request_maximize);
471
472 xdg_shell_view->request_move.notify = handle_request_move; 467 xdg_shell_view->request_move.notify = handle_request_move;
473 wl_signal_add(&xdg_surface->toplevel->events.request_move, 468 wl_signal_add(&xdg_surface->toplevel->events.request_move,
474 &xdg_shell_view->request_move); 469 &xdg_shell_view->request_move);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index e1a2e463..40288f97 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -105,14 +105,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
106 // This simply returns focus to the parent surface if there's one available. 106 // This simply returns focus to the parent surface if there's one available.
107 // This seems to handle JetBrains issues. 107 // This seems to handle JetBrains issues.
108 if (xsurface->parent && xsurface->parent->surface && 108 if (xsurface->parent && xsurface->parent->surface
109 wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { 109 && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) {
110 struct wlr_xwayland_surface *next_surface = 110 seat_set_focus_surface(seat, xsurface->parent->surface, false);
111 wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); 111 return;
112 if (wlr_xwayland_or_surface_wants_focus(next_surface)) {
113 seat_set_focus_surface(seat, xsurface->parent->surface, false);
114 return;
115 }
116 } 112 }
117 113
118 // Restore focus 114 // Restore focus
@@ -258,6 +254,7 @@ static void set_activated(struct sway_view *view, bool activated) {
258 } 254 }
259 255
260 wlr_xwayland_surface_activate(surface, activated); 256 wlr_xwayland_surface_activate(surface, activated);
257 wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE);
261} 258}
262 259
263static void set_tiled(struct sway_view *view, bool tiled) { 260static void set_tiled(struct sway_view *view, bool tiled) {
@@ -399,30 +396,31 @@ static void handle_commit(struct wl_listener *listener, void *data) {
399 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 396 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
400 struct wlr_surface_state *state = &xsurface->surface->current; 397 struct wlr_surface_state *state = &xsurface->surface->current;
401 398
399 struct wlr_box new_geo;
400 get_geometry(view, &new_geo);
401 bool new_size = new_geo.width != view->geometry.width ||
402 new_geo.height != view->geometry.height ||
403 new_geo.x != view->geometry.x ||
404 new_geo.y != view->geometry.y;
405
406 if (new_size) {
407 // The client changed its surface size in this commit. For floating
408 // containers, we resize the container to match. For tiling containers,
409 // we only recenter the surface.
410 desktop_damage_view(view);
411 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
412 if (container_is_floating(view->container)) {
413 view_update_size(view);
414 transaction_commit_dirty_client();
415 } else {
416 view_center_surface(view);
417 }
418 desktop_damage_view(view);
419 }
420
402 if (view->container->node.instruction) { 421 if (view->container->node.instruction) {
403 get_geometry(view, &view->geometry);
404 transaction_notify_view_ready_by_geometry(view, 422 transaction_notify_view_ready_by_geometry(view,
405 xsurface->x, xsurface->y, state->width, state->height); 423 xsurface->x, xsurface->y, state->width, state->height);
406 } else {
407 struct wlr_box new_geo;
408 get_geometry(view, &new_geo);
409
410 if ((new_geo.width != view->geometry.width ||
411 new_geo.height != view->geometry.height ||
412 new_geo.x != view->geometry.x ||
413 new_geo.y != view->geometry.y)) {
414 // The view has unexpectedly sent a new size
415 // eg. The Firefox "Save As" dialog when downloading a file
416 desktop_damage_view(view);
417 view_update_size(view, new_geo.width, new_geo.height);
418 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
419 desktop_damage_view(view);
420 transaction_commit_dirty();
421 transaction_notify_view_ready_by_geometry(view,
422 xsurface->x, xsurface->y, new_geo.width, new_geo.height);
423 } else {
424 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
425 }
426 } 424 }
427 425
428 view_damage_from(view); 426 view_damage_from(view);
@@ -438,6 +436,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
438 wl_list_remove(&xwayland_view->commit.link); 436 wl_list_remove(&xwayland_view->commit.link);
439 } 437 }
440 438
439 xwayland_view->view.wlr_xwayland_surface = NULL;
440
441 wl_list_remove(&xwayland_view->destroy.link); 441 wl_list_remove(&xwayland_view->destroy.link);
442 wl_list_remove(&xwayland_view->request_configure.link); 442 wl_list_remove(&xwayland_view->request_configure.link);
443 wl_list_remove(&xwayland_view->request_fullscreen.link); 443 wl_list_remove(&xwayland_view->request_fullscreen.link);
@@ -527,10 +527,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
527 view->natural_height = ev->height; 527 view->natural_height = ev->height;
528 container_floating_resize_and_center(view->container); 528 container_floating_resize_and_center(view->container);
529 529
530 configure(view, view->container->content_x, 530 configure(view, view->container->pending.content_x,
531 view->container->content_y, 531 view->container->pending.content_y,
532 view->container->content_width, 532 view->container->pending.content_width,
533 view->container->content_height); 533 view->container->pending.content_height);
534 node_set_dirty(&view->container->node); 534 node_set_dirty(&view->container->node);
535 } else { 535 } else {
536 configure(view, view->container->current.content_x, 536 configure(view, view->container->current.content_x,
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index fa604426..6fddee90 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -4,8 +4,8 @@
4#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
5#include <linux/input-event-codes.h> 5#include <linux/input-event-codes.h>
6#include <errno.h> 6#include <errno.h>
7#include <time.h>
7#include <strings.h> 8#include <strings.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_idle.h> 10#include <wlr/types/wlr_idle.h>
11#include <wlr/types/wlr_pointer.h> 11#include <wlr/types/wlr_pointer.h>
@@ -20,7 +20,6 @@
20#include "util.h" 20#include "util.h"
21#include "sway/commands.h" 21#include "sway/commands.h"
22#include "sway/desktop.h" 22#include "sway/desktop.h"
23#include "sway/desktop/transaction.h"
24#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
25#include "sway/input/keyboard.h" 24#include "sway/input/keyboard.h"
26#include "sway/input/tablet.h" 25#include "sway/input/tablet.h"
@@ -32,6 +31,12 @@
32#include "sway/tree/workspace.h" 31#include "sway/tree/workspace.h"
33#include "wlr-layer-shell-unstable-v1-protocol.h" 32#include "wlr-layer-shell-unstable-v1-protocol.h"
34 33
34static uint32_t get_current_time_msec(void) {
35 struct timespec now;
36 clock_gettime(CLOCK_MONOTONIC, &now);
37 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
38}
39
35static struct wlr_surface *layer_surface_at(struct sway_output *output, 40static struct wlr_surface *layer_surface_at(struct sway_output *output,
36 struct wl_list *layer, double ox, double oy, double *sx, double *sy) { 41 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
37 struct sway_layer_surface *sway_layer; 42 struct sway_layer_surface *sway_layer;
@@ -78,7 +83,28 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
78struct sway_node *node_at_coords( 83struct sway_node *node_at_coords(
79 struct sway_seat *seat, double lx, double ly, 84 struct sway_seat *seat, double lx, double ly,
80 struct wlr_surface **surface, double *sx, double *sy) { 85 struct wlr_surface **surface, double *sx, double *sy) {
81 // check for unmanaged views first 86 // find the output the cursor is on
87 struct wlr_output *wlr_output = wlr_output_layout_output_at(
88 root->output_layout, lx, ly);
89 if (wlr_output == NULL) {
90 return NULL;
91 }
92 struct sway_output *output = wlr_output->data;
93 if (!output || !output->enabled) {
94 // output is being destroyed or is being enabled
95 return NULL;
96 }
97 double ox = lx, oy = ly;
98 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
99
100 // layer surfaces on the overlay layer are rendered on top
101 if ((*surface = layer_surface_at(output,
102 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
103 ox, oy, sx, sy))) {
104 return NULL;
105 }
106
107 // check for unmanaged views
82#if HAVE_XWAYLAND 108#if HAVE_XWAYLAND
83 struct wl_list *unmanaged = &root->xwayland_unmanaged; 109 struct wl_list *unmanaged = &root->xwayland_unmanaged;
84 struct sway_xwayland_unmanaged *unmanaged_surface; 110 struct sway_xwayland_unmanaged *unmanaged_surface;
@@ -96,19 +122,6 @@ struct sway_node *node_at_coords(
96 } 122 }
97 } 123 }
98#endif 124#endif
99 // find the output the cursor is on
100 struct wlr_output *wlr_output = wlr_output_layout_output_at(
101 root->output_layout, lx, ly);
102 if (wlr_output == NULL) {
103 return NULL;
104 }
105 struct sway_output *output = wlr_output->data;
106 if (!output || !output->enabled) {
107 // output is being destroyed or is being enabled
108 return NULL;
109 }
110 double ox = lx, oy = ly;
111 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
112 125
113 if (root->fullscreen_global) { 126 if (root->fullscreen_global) {
114 // Try fullscreen container 127 // Try fullscreen container
@@ -126,11 +139,6 @@ struct sway_node *node_at_coords(
126 return NULL; 139 return NULL;
127 } 140 }
128 141
129 if ((*surface = layer_surface_at(output,
130 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
131 ox, oy, sx, sy))) {
132 return NULL;
133 }
134 if (ws->fullscreen) { 142 if (ws->fullscreen) {
135 // Try transient containers 143 // Try transient containers
136 for (int i = 0; i < ws->floating->length; ++i) { 144 for (int i = 0; i < ws->floating->length; ++i) {
@@ -383,7 +391,6 @@ static void handle_pointer_motion_relative(
383 391
384 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, 392 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y,
385 e->unaccel_dx, e->unaccel_dy); 393 e->unaccel_dx, e->unaccel_dy);
386 transaction_commit_dirty();
387} 394}
388 395
389static void handle_pointer_motion_absolute( 396static void handle_pointer_motion_absolute(
@@ -401,7 +408,6 @@ static void handle_pointer_motion_absolute(
401 double dy = ly - cursor->cursor->y; 408 double dy = ly - cursor->cursor->y;
402 409
403 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 410 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
404 transaction_commit_dirty();
405} 411}
406 412
407void dispatch_cursor_button(struct sway_cursor *cursor, 413void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -431,7 +437,6 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
431 cursor_handle_activity_from_device(cursor, event->device); 437 cursor_handle_activity_from_device(cursor, event->device);
432 dispatch_cursor_button(cursor, event->device, 438 dispatch_cursor_button(cursor, event->device,
433 event->time_msec, event->button, event->state); 439 event->time_msec, event->button, event->state);
434 transaction_commit_dirty();
435} 440}
436 441
437void dispatch_cursor_axis(struct sway_cursor *cursor, 442void dispatch_cursor_axis(struct sway_cursor *cursor,
@@ -444,7 +449,6 @@ static void handle_pointer_axis(struct wl_listener *listener, void *data) {
444 struct wlr_event_pointer_axis *event = data; 449 struct wlr_event_pointer_axis *event = data;
445 cursor_handle_activity_from_device(cursor, event->device); 450 cursor_handle_activity_from_device(cursor, event->device);
446 dispatch_cursor_axis(cursor, event); 451 dispatch_cursor_axis(cursor, event);
447 transaction_commit_dirty();
448} 452}
449 453
450static void handle_pointer_frame(struct wl_listener *listener, void *data) { 454static void handle_pointer_frame(struct wl_listener *listener, void *data) {
@@ -494,8 +498,6 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
494 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 498 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
495 dispatch_cursor_button(cursor, event->device, event->time_msec, 499 dispatch_cursor_button(cursor, event->device, event->time_msec,
496 BTN_LEFT, WLR_BUTTON_PRESSED); 500 BTN_LEFT, WLR_BUTTON_PRESSED);
497 wlr_seat_pointer_notify_frame(wlr_seat);
498 transaction_commit_dirty();
499 } 501 }
500} 502}
501 503
@@ -508,11 +510,9 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
508 510
509 if (cursor->simulating_pointer_from_touch) { 511 if (cursor->simulating_pointer_from_touch) {
510 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 512 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
511 cursor->simulating_pointer_from_touch = false; 513 cursor->pointer_touch_up = true;
512 dispatch_cursor_button(cursor, event->device, event->time_msec, 514 dispatch_cursor_button(cursor, event->device, event->time_msec,
513 BTN_LEFT, WLR_BUTTON_RELEASED); 515 BTN_LEFT, WLR_BUTTON_RELEASED);
514 wlr_seat_pointer_notify_frame(wlr_seat);
515 transaction_commit_dirty();
516 } 516 }
517 } else { 517 } else {
518 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 518 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id);
@@ -553,7 +553,6 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
553 dx = lx - cursor->cursor->x; 553 dx = lx - cursor->cursor->x;
554 dy = ly - cursor->cursor->y; 554 dy = ly - cursor->cursor->y;
555 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 555 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
556 transaction_commit_dirty();
557 } 556 }
558 } else if (surface) { 557 } else if (surface) {
559 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 558 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec,
@@ -561,6 +560,24 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
561 } 560 }
562} 561}
563 562
563static void handle_touch_frame(struct wl_listener *listener, void *data) {
564 struct sway_cursor *cursor =
565 wl_container_of(listener, cursor, touch_frame);
566
567 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
568
569 if (cursor->simulating_pointer_from_touch) {
570 wlr_seat_pointer_notify_frame(wlr_seat);
571
572 if (cursor->pointer_touch_up) {
573 cursor->pointer_touch_up = false;
574 cursor->simulating_pointer_from_touch = false;
575 }
576 } else {
577 wlr_seat_touch_notify_frame(wlr_seat);
578 }
579}
580
564static double apply_mapping_from_coord(double low, double high, double value) { 581static double apply_mapping_from_coord(double low, double high, double value) {
565 if (isnan(value)) { 582 if (isnan(value)) {
566 return value; 583 return value;
@@ -639,8 +656,6 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
639 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 656 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); 657 pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy);
641 } 658 }
642
643 transaction_commit_dirty();
644} 659}
645 660
646static void handle_tool_axis(struct wl_listener *listener, void *data) { 661static void handle_tool_axis(struct wl_listener *listener, void *data) {
@@ -720,7 +735,6 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
720 dispatch_cursor_button(cursor, event->device, event->time_msec, 735 dispatch_cursor_button(cursor, event->device, event->time_msec,
721 BTN_LEFT, WLR_BUTTON_RELEASED); 736 BTN_LEFT, WLR_BUTTON_RELEASED);
722 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 737 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)) { 738 } 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 739 // 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 740 // tablet v2, we should notify that surface if it gets released over a
@@ -733,7 +747,6 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
733 dispatch_cursor_button(cursor, event->device, event->time_msec, 747 dispatch_cursor_button(cursor, event->device, event->time_msec,
734 BTN_LEFT, WLR_BUTTON_PRESSED); 748 BTN_LEFT, WLR_BUTTON_PRESSED);
735 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 749 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
736 transaction_commit_dirty();
737 } 750 }
738 } else { 751 } else {
739 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); 752 seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state);
@@ -820,7 +833,6 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
820 break; 833 break;
821 } 834 }
822 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 835 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
823 transaction_commit_dirty();
824 return; 836 return;
825 } 837 }
826 838
@@ -837,8 +849,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
837 849
838 struct sway_container *con = view->container; 850 struct sway_container *con = view->container;
839 851
840 double sx = cursor->cursor->x - con->content_x + view->geometry.x; 852 double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x;
841 double sy = cursor->cursor->y - con->content_y + view->geometry.y; 853 double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y;
842 854
843 if (!pixman_region32_contains_point(region, 855 if (!pixman_region32_contains_point(region,
844 floor(sx), floor(sy), NULL)) { 856 floor(sx), floor(sy), NULL)) {
@@ -849,8 +861,8 @@ static void check_constraint_region(struct sway_cursor *cursor) {
849 double sy = (boxes[0].y1 + boxes[0].y2) / 2.; 861 double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
850 862
851 wlr_cursor_warp_closest(cursor->cursor, NULL, 863 wlr_cursor_warp_closest(cursor->cursor, NULL,
852 sx + con->content_x - view->geometry.x, 864 sx + con->pending.content_x - view->geometry.x,
853 sy + con->content_y - view->geometry.y); 865 sy + con->pending.content_y - view->geometry.y);
854 866
855 cursor_rebase(cursor); 867 cursor_rebase(cursor);
856 } 868 }
@@ -915,6 +927,7 @@ static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data)
915 struct sway_cursor *cursor = wl_container_of( 927 struct sway_cursor *cursor = wl_container_of(
916 listener, cursor, pinch_begin); 928 listener, cursor, pinch_begin);
917 struct wlr_event_pointer_pinch_begin *event = data; 929 struct wlr_event_pointer_pinch_begin *event = data;
930 cursor_handle_activity_from_device(cursor, event->device);
918 wlr_pointer_gestures_v1_send_pinch_begin( 931 wlr_pointer_gestures_v1_send_pinch_begin(
919 cursor->pointer_gestures, cursor->seat->wlr_seat, 932 cursor->pointer_gestures, cursor->seat->wlr_seat,
920 event->time_msec, event->fingers); 933 event->time_msec, event->fingers);
@@ -924,6 +937,7 @@ static void handle_pointer_pinch_update(struct wl_listener *listener, void *data
924 struct sway_cursor *cursor = wl_container_of( 937 struct sway_cursor *cursor = wl_container_of(
925 listener, cursor, pinch_update); 938 listener, cursor, pinch_update);
926 struct wlr_event_pointer_pinch_update *event = data; 939 struct wlr_event_pointer_pinch_update *event = data;
940 cursor_handle_activity_from_device(cursor, event->device);
927 wlr_pointer_gestures_v1_send_pinch_update( 941 wlr_pointer_gestures_v1_send_pinch_update(
928 cursor->pointer_gestures, cursor->seat->wlr_seat, 942 cursor->pointer_gestures, cursor->seat->wlr_seat,
929 event->time_msec, event->dx, event->dy, 943 event->time_msec, event->dx, event->dy,
@@ -934,6 +948,7 @@ static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
934 struct sway_cursor *cursor = wl_container_of( 948 struct sway_cursor *cursor = wl_container_of(
935 listener, cursor, pinch_end); 949 listener, cursor, pinch_end);
936 struct wlr_event_pointer_pinch_end *event = data; 950 struct wlr_event_pointer_pinch_end *event = data;
951 cursor_handle_activity_from_device(cursor, event->device);
937 wlr_pointer_gestures_v1_send_pinch_end( 952 wlr_pointer_gestures_v1_send_pinch_end(
938 cursor->pointer_gestures, cursor->seat->wlr_seat, 953 cursor->pointer_gestures, cursor->seat->wlr_seat,
939 event->time_msec, event->cancelled); 954 event->time_msec, event->cancelled);
@@ -943,6 +958,7 @@ static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data)
943 struct sway_cursor *cursor = wl_container_of( 958 struct sway_cursor *cursor = wl_container_of(
944 listener, cursor, swipe_begin); 959 listener, cursor, swipe_begin);
945 struct wlr_event_pointer_swipe_begin *event = data; 960 struct wlr_event_pointer_swipe_begin *event = data;
961 cursor_handle_activity_from_device(cursor, event->device);
946 wlr_pointer_gestures_v1_send_swipe_begin( 962 wlr_pointer_gestures_v1_send_swipe_begin(
947 cursor->pointer_gestures, cursor->seat->wlr_seat, 963 cursor->pointer_gestures, cursor->seat->wlr_seat,
948 event->time_msec, event->fingers); 964 event->time_msec, event->fingers);
@@ -952,6 +968,7 @@ static void handle_pointer_swipe_update(struct wl_listener *listener, void *data
952 struct sway_cursor *cursor = wl_container_of( 968 struct sway_cursor *cursor = wl_container_of(
953 listener, cursor, swipe_update); 969 listener, cursor, swipe_update);
954 struct wlr_event_pointer_swipe_update *event = data; 970 struct wlr_event_pointer_swipe_update *event = data;
971 cursor_handle_activity_from_device(cursor, event->device);
955 wlr_pointer_gestures_v1_send_swipe_update( 972 wlr_pointer_gestures_v1_send_swipe_update(
956 cursor->pointer_gestures, cursor->seat->wlr_seat, 973 cursor->pointer_gestures, cursor->seat->wlr_seat,
957 event->time_msec, event->dx, event->dy); 974 event->time_msec, event->dx, event->dy);
@@ -961,6 +978,7 @@ static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
961 struct sway_cursor *cursor = wl_container_of( 978 struct sway_cursor *cursor = wl_container_of(
962 listener, cursor, swipe_end); 979 listener, cursor, swipe_end);
963 struct wlr_event_pointer_swipe_end *event = data; 980 struct wlr_event_pointer_swipe_end *event = data;
981 cursor_handle_activity_from_device(cursor, event->device);
964 wlr_pointer_gestures_v1_send_swipe_end( 982 wlr_pointer_gestures_v1_send_swipe_end(
965 cursor->pointer_gestures, cursor->seat->wlr_seat, 983 cursor->pointer_gestures, cursor->seat->wlr_seat,
966 event->time_msec, event->cancelled); 984 event->time_msec, event->cancelled);
@@ -1051,6 +1069,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1051 wl_list_remove(&cursor->touch_down.link); 1069 wl_list_remove(&cursor->touch_down.link);
1052 wl_list_remove(&cursor->touch_up.link); 1070 wl_list_remove(&cursor->touch_up.link);
1053 wl_list_remove(&cursor->touch_motion.link); 1071 wl_list_remove(&cursor->touch_motion.link);
1072 wl_list_remove(&cursor->touch_frame.link);
1054 wl_list_remove(&cursor->tool_axis.link); 1073 wl_list_remove(&cursor->tool_axis.link);
1055 wl_list_remove(&cursor->tool_tip.link); 1074 wl_list_remove(&cursor->tool_tip.link);
1056 wl_list_remove(&cursor->tool_button.link); 1075 wl_list_remove(&cursor->tool_button.link);
@@ -1126,6 +1145,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1126 &cursor->touch_motion); 1145 &cursor->touch_motion);
1127 cursor->touch_motion.notify = handle_touch_motion; 1146 cursor->touch_motion.notify = handle_touch_motion;
1128 1147
1148 wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame);
1149 cursor->touch_frame.notify = handle_touch_frame;
1150
1129 wl_signal_add(&wlr_cursor->events.tablet_tool_axis, 1151 wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
1130 &cursor->tool_axis); 1152 &cursor->tool_axis);
1131 cursor->tool_axis.notify = handle_tool_axis; 1153 cursor->tool_axis.notify = handle_tool_axis;
@@ -1170,8 +1192,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
1170 return; 1192 return;
1171 } 1193 }
1172 1194
1173 double x = container->x + container->width / 2.0; 1195 double x = container->pending.x + container->pending.width / 2.0;
1174 double y = container->y + container->height / 2.0; 1196 double y = container->pending.y + container->pending.height / 2.0;
1175 1197
1176 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1198 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1177 cursor_unhide(cursor); 1199 cursor_unhide(cursor);
@@ -1284,8 +1306,8 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1284 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1306 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1285 struct sway_container *con = view->container; 1307 struct sway_container *con = view->container;
1286 1308
1287 double lx = sx + con->content_x - view->geometry.x; 1309 double lx = sx + con->pending.content_x - view->geometry.x;
1288 double ly = sy + con->content_y - view->geometry.y; 1310 double ly = sy + con->pending.content_y - view->geometry.y;
1289 1311
1290 wlr_cursor_warp(cursor->cursor, NULL, lx, ly); 1312 wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
1291 1313
@@ -1333,7 +1355,7 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1333 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1355 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1334 1356
1335 struct sway_node *focus = seat_get_focus(seat); 1357 struct sway_node *focus = seat_get_focus(seat);
1336 if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { 1358 if (focus && node_is_view(focus)) {
1337 struct wlr_surface *surface = focus->sway_container->view->surface; 1359 struct wlr_surface *surface = focus->sway_container->view->surface;
1338 if (surface == constraint->surface) { 1360 if (surface == constraint->surface) {
1339 sway_cursor_constrain(seat->cursor, constraint); 1361 sway_cursor_constrain(seat->cursor, constraint);
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..3c0f359d 100644
--- a/sway/input/libinput.c
+++ b/sway/input/libinput.c
@@ -1,5 +1,6 @@
1#include <float.h> 1#include <float.h>
2#include <libinput.h> 2#include <libinput.h>
3#include <libudev.h>
3#include <limits.h> 4#include <limits.h>
4#include <wlr/backend/libinput.h> 5#include <wlr/backend/libinput.h>
5#include "log.h" 6#include "log.h"
@@ -312,3 +313,32 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
312 ipc_event_input("libinput_config", input_device); 313 ipc_event_input("libinput_config", input_device);
313 } 314 }
314} 315}
316
317bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) {
318 if (!wlr_input_device_is_libinput(sway_device->wlr_device)) {
319 return false;
320 }
321
322 struct libinput_device *device =
323 wlr_libinput_get_device_handle(sway_device->wlr_device);
324 struct udev_device *udev_device =
325 libinput_device_get_udev_device(device);
326 if (!udev_device) {
327 return false;
328 }
329
330 const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH");
331 if (!id_path) {
332 return false;
333 }
334
335 const char prefix_platform[] = "platform-";
336 if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) {
337 return false;
338 }
339
340 const char prefix_pci[] = "pci-";
341 const char infix_platform[] = "-platform-";
342 return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) &&
343 strstr(id_path, infix_platform);
344}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1f5865ee..ce933b66 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"
@@ -50,6 +51,16 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
50static void seat_node_destroy(struct sway_seat_node *seat_node) { 51static void seat_node_destroy(struct sway_seat_node *seat_node) {
51 wl_list_remove(&seat_node->destroy.link); 52 wl_list_remove(&seat_node->destroy.link);
52 wl_list_remove(&seat_node->link); 53 wl_list_remove(&seat_node->link);
54
55 /*
56 * This is the only time we remove items from the focus stack without
57 * immediately re-adding them. If we just removed the last thing,
58 * mark that nothing has focus anymore.
59 */
60 if (wl_list_empty(&seat_node->seat->focus_stack)) {
61 seat_node->seat->has_focus = false;
62 }
63
53 free(seat_node); 64 free(seat_node);
54} 65}
55 66
@@ -209,14 +220,13 @@ void seat_for_each_node(struct sway_seat *seat,
209 220
210struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 221struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
211 struct sway_node *ancestor) { 222 struct sway_node *ancestor) {
212 if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { 223 if (node_is_view(ancestor)) {
213 return ancestor->sway_container; 224 return ancestor->sway_container;
214 } 225 }
215 struct sway_seat_node *current; 226 struct sway_seat_node *current;
216 wl_list_for_each(current, &seat->focus_stack, link) { 227 wl_list_for_each(current, &seat->focus_stack, link) {
217 struct sway_node *node = current->node; 228 struct sway_node *node = current->node;
218 if (node->type == N_CONTAINER && node->sway_container->view && 229 if (node_is_view(node) && node_has_ancestor(node, ancestor)) {
219 node_has_ancestor(node, ancestor)) {
220 return node->sway_container; 230 return node->sway_container;
221 } 231 }
222 } 232 }
@@ -309,8 +319,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
309 // Setting focus_inactive 319 // Setting focus_inactive
310 focus = seat_get_focus_inactive(seat, &root->node); 320 focus = seat_get_focus_inactive(seat, &root->node);
311 seat_set_raw_focus(seat, next_focus); 321 seat_set_raw_focus(seat, next_focus);
312 if (focus->type == N_CONTAINER && focus->sway_container->workspace) { 322 if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) {
313 seat_set_raw_focus(seat, &focus->sway_container->workspace->node); 323 seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node);
314 } 324 }
315 seat_set_raw_focus(seat, focus); 325 seat_set_raw_focus(seat, focus);
316 } 326 }
@@ -666,6 +676,40 @@ static void seat_reset_input_config(struct sway_seat *seat,
666 sway_device->input_device->wlr_device, NULL); 676 sway_device->input_device->wlr_device, NULL);
667} 677}
668 678
679static bool has_prefix(const char *str, const char *prefix) {
680 return strncmp(str, prefix, strlen(prefix)) == 0;
681}
682
683/**
684 * Get the name of the built-in output, if any. Returns NULL if there isn't
685 * exactly one built-in output.
686 */
687static const char *get_builtin_output_name(void) {
688 const char *match = NULL;
689 for (int i = 0; i < root->outputs->length; ++i) {
690 struct sway_output *output = root->outputs->items[i];
691 const char *name = output->wlr_output->name;
692 if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") ||
693 has_prefix(name, "DSI-")) {
694 if (match != NULL) {
695 return NULL;
696 }
697 match = name;
698 }
699 }
700 return match;
701}
702
703static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {
704 switch (seat_device->input_device->wlr_device->type) {
705 case WLR_INPUT_DEVICE_TOUCH:
706 case WLR_INPUT_DEVICE_TABLET_TOOL:
707 return true;
708 default:
709 return false;
710 }
711}
712
669static void seat_apply_input_config(struct sway_seat *seat, 713static void seat_apply_input_config(struct sway_seat *seat,
670 struct sway_seat_device *sway_device) { 714 struct sway_seat_device *sway_device) {
671 struct input_config *ic = 715 struct input_config *ic =
@@ -681,7 +725,21 @@ static void seat_apply_input_config(struct sway_seat *seat,
681 725
682 switch (mapped_to) { 726 switch (mapped_to) {
683 case MAPPED_TO_DEFAULT: 727 case MAPPED_TO_DEFAULT:
728 /*
729 * If the wlroots backend provides an output name, use that.
730 *
731 * Otherwise, try to map built-in touch and tablet tool devices to the
732 * built-in output.
733 */
684 mapped_to_output = sway_device->input_device->wlr_device->output_name; 734 mapped_to_output = sway_device->input_device->wlr_device->output_name;
735 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
736 sway_libinput_device_is_builtin(sway_device->input_device)) {
737 mapped_to_output = get_builtin_output_name();
738 if (mapped_to_output) {
739 sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'",
740 mapped_to_output, sway_device->input_device->identifier);
741 }
742 }
685 if (mapped_to_output == NULL) { 743 if (mapped_to_output == NULL) {
686 return; 744 return;
687 } 745 }
@@ -1086,30 +1144,19 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1086 } 1144 }
1087 1145
1088 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? 1146 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
1089 node->sway_workspace : node->sway_container->workspace; 1147 node->sway_workspace : node->sway_container->pending.workspace;
1090 struct sway_container *container = node->type == N_CONTAINER ? 1148 struct sway_container *container = node->type == N_CONTAINER ?
1091 node->sway_container : NULL; 1149 node->sway_container : NULL;
1092 1150
1093 // Deny setting focus to a view which is hidden by a fullscreen container 1151 // Deny setting focus to a view which is hidden by a fullscreen container or global
1094 if (new_workspace && new_workspace->fullscreen && container && 1152 if (container && container_obstructing_fullscreen_container(container)) {
1095 !container_is_fullscreen_or_child(container)) { 1153 return;
1096 // Unless it's a transient container
1097 if (!container_is_transient_for(container, new_workspace->fullscreen)) {
1098 return;
1099 }
1100 } 1154 }
1155
1101 // Deny setting focus to a workspace node when using fullscreen global 1156 // Deny setting focus to a workspace node when using fullscreen global
1102 if (root->fullscreen_global && !container && new_workspace) { 1157 if (root->fullscreen_global && !container && new_workspace) {
1103 return; 1158 return;
1104 } 1159 }
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 1160
1114 struct sway_output *new_output = 1161 struct sway_output *new_output =
1115 new_workspace ? new_workspace->output : NULL; 1162 new_workspace ? new_workspace->output : NULL;
@@ -1135,10 +1182,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 1182 // Put the container parents on the focus stack, then the workspace, then
1136 // the focused container. 1183 // the focused container.
1137 if (container) { 1184 if (container) {
1138 struct sway_container *parent = container->parent; 1185 struct sway_container *parent = container->pending.parent;
1139 while (parent) { 1186 while (parent) {
1140 seat_set_raw_focus(seat, &parent->node); 1187 seat_set_raw_focus(seat, &parent->node);
1141 parent = parent->parent; 1188 parent = parent->pending.parent;
1142 } 1189 }
1143 } 1190 }
1144 if (new_workspace) { 1191 if (new_workspace) {
@@ -1234,6 +1281,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1234 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); 1281 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
1235 } 1282 }
1236 1283
1284 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1237 seat_tablet_pads_notify_enter(seat, surface); 1285 seat_tablet_pads_notify_enter(seat, surface);
1238} 1286}
1239 1287
@@ -1326,7 +1374,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
1326 struct sway_node *node = current->node; 1374 struct sway_node *node = current->node;
1327 if (node->type == N_CONTAINER && 1375 if (node->type == N_CONTAINER &&
1328 !container_is_floating_or_child(node->sway_container) && 1376 !container_is_floating_or_child(node->sway_container) &&
1329 node->sway_container->workspace == workspace) { 1377 node->sway_container->pending.workspace == workspace) {
1330 return node->sway_container; 1378 return node->sway_container;
1331 } 1379 }
1332 } 1380 }
@@ -1343,7 +1391,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
1343 struct sway_node *node = current->node; 1391 struct sway_node *node = current->node;
1344 if (node->type == N_CONTAINER && 1392 if (node->type == N_CONTAINER &&
1345 container_is_floating_or_child(node->sway_container) && 1393 container_is_floating_or_child(node->sway_container) &&
1346 node->sway_container->workspace == workspace) { 1394 node->sway_container->pending.workspace == workspace) {
1347 return node->sway_container; 1395 return node->sway_container;
1348 } 1396 }
1349 } 1397 }
@@ -1377,9 +1425,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) {
1377 if (!seat->has_focus) { 1425 if (!seat->has_focus) {
1378 return NULL; 1426 return NULL;
1379 } 1427 }
1380 if (wl_list_empty(&seat->focus_stack)) { 1428 sway_assert(!wl_list_empty(&seat->focus_stack),
1381 return NULL; 1429 "focus_stack is empty, but has_focus is true");
1382 }
1383 struct sway_seat_node *current = 1430 struct sway_seat_node *current =
1384 wl_container_of(seat->focus_stack.next, current, link); 1431 wl_container_of(seat->focus_stack.next, current, link);
1385 return current->node; 1432 return current->node;
@@ -1391,7 +1438,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) {
1391 return NULL; 1438 return NULL;
1392 } 1439 }
1393 if (focus->type == N_CONTAINER) { 1440 if (focus->type == N_CONTAINER) {
1394 return focus->sway_container->workspace; 1441 return focus->sway_container->pending.workspace;
1395 } 1442 }
1396 if (focus->type == N_WORKSPACE) { 1443 if (focus->type == N_WORKSPACE) {
1397 return focus->sway_workspace; 1444 return focus->sway_workspace;
@@ -1404,8 +1451,8 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) {
1404 wl_list_for_each(current, &seat->focus_stack, link) { 1451 wl_list_for_each(current, &seat->focus_stack, link) {
1405 struct sway_node *node = current->node; 1452 struct sway_node *node = current->node;
1406 if (node->type == N_CONTAINER && 1453 if (node->type == N_CONTAINER &&
1407 node->sway_container->workspace) { 1454 node->sway_container->pending.workspace) {
1408 return node->sway_container->workspace; 1455 return node->sway_container->pending.workspace;
1409 } else if (node->type == N_WORKSPACE) { 1456 } else if (node->type == N_WORKSPACE) {
1410 return node->sway_workspace; 1457 return node->sway_workspace;
1411 } 1458 }
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index a583ed62..4320a3b4 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -4,6 +4,7 @@
4#include <wlr/types/wlr_cursor.h> 4#include <wlr/types/wlr_cursor.h>
5#include <wlr/types/wlr_tablet_v2.h> 5#include <wlr/types/wlr_tablet_v2.h>
6#include <wlr/types/wlr_xcursor_manager.h> 6#include <wlr/types/wlr_xcursor_manager.h>
7#include "sway/desktop/transaction.h"
7#include "sway/input/cursor.h" 8#include "sway/input/cursor.h"
8#include "sway/input/seat.h" 9#include "sway/input/seat.h"
9#include "sway/input/tablet.h" 10#include "sway/input/tablet.h"
@@ -59,7 +60,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
59 return false; 60 return false;
60 } 61 }
61 } 62 }
62 cont = cont->parent; 63 cont = cont->pending.parent;
63 } 64 }
64 return true; 65 return true;
65} 66}
@@ -69,25 +70,25 @@ static enum wlr_edges find_edge(struct sway_container *cont,
69 if (!cont->view || (surface && cont->view->surface != surface)) { 70 if (!cont->view || (surface && cont->view->surface != surface)) {
70 return WLR_EDGE_NONE; 71 return WLR_EDGE_NONE;
71 } 72 }
72 if (cont->border == B_NONE || !cont->border_thickness || 73 if (cont->pending.border == B_NONE || !cont->pending.border_thickness ||
73 cont->border == B_CSD) { 74 cont->pending.border == B_CSD) {
74 return WLR_EDGE_NONE; 75 return WLR_EDGE_NONE;
75 } 76 }
76 if (cont->fullscreen_mode) { 77 if (cont->pending.fullscreen_mode) {
77 return WLR_EDGE_NONE; 78 return WLR_EDGE_NONE;
78 } 79 }
79 80
80 enum wlr_edges edge = 0; 81 enum wlr_edges edge = 0;
81 if (cursor->cursor->x < cont->x + cont->border_thickness) { 82 if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) {
82 edge |= WLR_EDGE_LEFT; 83 edge |= WLR_EDGE_LEFT;
83 } 84 }
84 if (cursor->cursor->y < cont->y + cont->border_thickness) { 85 if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) {
85 edge |= WLR_EDGE_TOP; 86 edge |= WLR_EDGE_TOP;
86 } 87 }
87 if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { 88 if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) {
88 edge |= WLR_EDGE_RIGHT; 89 edge |= WLR_EDGE_RIGHT;
89 } 90 }
90 if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { 91 if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) {
91 edge |= WLR_EDGE_BOTTOM; 92 edge |= WLR_EDGE_BOTTOM;
92 } 93 }
93 94
@@ -231,6 +232,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
231 wlr_layer_surface_v1_from_wlr_surface(surface); 232 wlr_layer_surface_v1_from_wlr_surface(surface);
232 if (layer->current.keyboard_interactive) { 233 if (layer->current.keyboard_interactive) {
233 seat_set_focus_layer(seat, layer); 234 seat_set_focus_layer(seat, layer);
235 transaction_commit_dirty();
234 } 236 }
235 } else if (cont) { 237 } else if (cont) {
236 bool is_floating_or_child = container_is_floating_or_child(cont); 238 bool is_floating_or_child = container_is_floating_or_child(cont);
@@ -249,7 +251,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
249 251
250 // Handle moving a tiling container 252 // Handle moving a tiling container
251 if (config->tiling_drag && mod_pressed && !is_floating_or_child && 253 if (config->tiling_drag && mod_pressed && !is_floating_or_child &&
252 cont->fullscreen_mode == FULLSCREEN_NONE) { 254 cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
253 seatop_begin_move_tiling(seat, cont); 255 seatop_begin_move_tiling(seat, cont);
254 return; 256 return;
255 } 257 }
@@ -268,6 +270,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
268 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 270 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
269 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 271 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
270 seat_set_focus_surface(seat, xsurface->surface, false); 272 seat_set_focus_surface(seat, xsurface->surface, false);
273 transaction_commit_dirty();
271 } 274 }
272 } 275 }
273#endif 276#endif
@@ -356,6 +359,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
356 if (node && node->type == N_WORKSPACE) { 359 if (node && node->type == N_WORKSPACE) {
357 if (state == WLR_BUTTON_PRESSED) { 360 if (state == WLR_BUTTON_PRESSED) {
358 seat_set_focus(seat, node); 361 seat_set_focus(seat, node);
362 transaction_commit_dirty();
359 } 363 }
360 seat_pointer_notify_button(seat, time_msec, button, state); 364 seat_pointer_notify_button(seat, time_msec, button, state);
361 return; 365 return;
@@ -367,6 +371,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
367 wlr_layer_surface_v1_from_wlr_surface(surface); 371 wlr_layer_surface_v1_from_wlr_surface(surface);
368 if (layer->current.keyboard_interactive) { 372 if (layer->current.keyboard_interactive) {
369 seat_set_focus_layer(seat, layer); 373 seat_set_focus_layer(seat, layer);
374 transaction_commit_dirty();
375 }
376 if (state == WLR_BUTTON_PRESSED) {
377 seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy);
370 } 378 }
371 seat_pointer_notify_button(seat, time_msec, button, state); 379 seat_pointer_notify_button(seat, time_msec, button, state);
372 return; 380 return;
@@ -381,7 +389,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
381 struct sway_container *cont_to_focus = cont; 389 struct sway_container *cont_to_focus = cont;
382 enum sway_container_layout layout = container_parent_layout(cont); 390 enum sway_container_layout layout = container_parent_layout(cont);
383 if (layout == L_TABBED || layout == L_STACKED) { 391 if (layout == L_TABBED || layout == L_STACKED) {
384 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); 392 cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node);
385 } 393 }
386 394
387 seat_set_focus_container(seat, cont_to_focus); 395 seat_set_focus_container(seat, cont_to_focus);
@@ -397,9 +405,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
397 BTN_LEFT : BTN_RIGHT; 405 BTN_LEFT : BTN_RIGHT;
398 if (button == btn_resize) { 406 if (button == btn_resize) {
399 edge = 0; 407 edge = 0;
400 edge |= cursor->cursor->x > cont->x + cont->width / 2 ? 408 edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ?
401 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 409 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
402 edge |= cursor->cursor->y > cont->y + cont->height / 2 ? 410 edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ?
403 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 411 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
404 412
405 const char *image = NULL; 413 const char *image = NULL;
@@ -446,9 +454,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
446 if (mod_pressed && button == btn_resize) { 454 if (mod_pressed && button == btn_resize) {
447 struct sway_container *floater = container_toplevel_ancestor(cont); 455 struct sway_container *floater = container_toplevel_ancestor(cont);
448 edge = 0; 456 edge = 0;
449 edge |= cursor->cursor->x > floater->x + floater->width / 2 ? 457 edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ?
450 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 458 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
451 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 459 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
452 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 460 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
453 seatop_begin_resize_floating(seat, floater, edge); 461 seatop_begin_resize_floating(seat, floater, edge);
454 return; 462 return;
@@ -458,7 +466,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
458 // Handle moving a tiling container 466 // Handle moving a tiling container
459 if (config->tiling_drag && (mod_pressed || on_titlebar) && 467 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
460 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 468 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
461 cont && cont->fullscreen_mode == FULLSCREEN_NONE) { 469 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
462 struct sway_container *focus = seat_get_focused_container(seat); 470 struct sway_container *focus = seat_get_focused_container(seat);
463 bool focused = focus == cont || container_has_ancestor(focus, cont); 471 bool focused = focus == cont || container_has_ancestor(focus, cont);
464 if (on_titlebar && !focused) { 472 if (on_titlebar && !focused) {
@@ -487,6 +495,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
487 if (cont && state == WLR_BUTTON_PRESSED) { 495 if (cont && state == WLR_BUTTON_PRESSED) {
488 node = seat_get_focus_inactive(seat, &cont->node); 496 node = seat_get_focus_inactive(seat, &cont->node);
489 seat_set_focus(seat, node); 497 seat_set_focus(seat, node);
498 transaction_commit_dirty();
490 seat_pointer_notify_button(seat, time_msec, button, state); 499 seat_pointer_notify_button(seat, time_msec, button, state);
491 return; 500 return;
492 } 501 }
@@ -501,6 +510,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
501 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 510 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
502 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 511 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
503 seat_set_focus_surface(seat, xsurface->surface, false); 512 seat_set_focus_surface(seat, xsurface->surface, false);
513 transaction_commit_dirty();
504 seat_pointer_notify_button(seat, time_msec, button, state); 514 seat_pointer_notify_button(seat, time_msec, button, state);
505 return; 515 return;
506 } 516 }
@@ -530,6 +540,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
530 if (focus && hovered_output != node_get_output(focus)) { 540 if (focus && hovered_output != node_get_output(focus)) {
531 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 541 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
532 seat_set_focus(seat, &ws->node); 542 seat_set_focus(seat, &ws->node);
543 transaction_commit_dirty();
533 } 544 }
534 return; 545 return;
535 } 546 }
@@ -541,6 +552,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
541 struct sway_output *hovered_output = node_get_output(hovered_node); 552 struct sway_output *hovered_output = node_get_output(hovered_node);
542 if (hovered_output != focused_output) { 553 if (hovered_output != focused_output) {
543 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); 554 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node));
555 transaction_commit_dirty();
544 } 556 }
545 return; 557 return;
546 } 558 }
@@ -556,6 +568,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
556 if (hovered_node != e->previous_node || 568 if (hovered_node != e->previous_node ||
557 config->focus_follows_mouse == FOLLOWS_ALWAYS) { 569 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
558 seat_set_focus(seat, hovered_node); 570 seat_set_focus(seat, hovered_node);
571 transaction_commit_dirty();
559 } 572 }
560 } 573 }
561} 574}
@@ -664,7 +677,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
664 bool on_border = edge != WLR_EDGE_NONE; 677 bool on_border = edge != WLR_EDGE_NONE;
665 bool on_titlebar = cont && !on_border && !surface; 678 bool on_titlebar = cont && !on_border && !surface;
666 bool on_titlebar_border = cont && on_border && 679 bool on_titlebar_border = cont && on_border &&
667 cursor->cursor->y < cont->content_y; 680 cursor->cursor->y < cont->pending.content_y;
668 bool on_contents = cont && !on_border && surface; 681 bool on_contents = cont && !on_border && surface;
669 bool on_workspace = node && node->type == N_WORKSPACE; 682 bool on_workspace = node && node->type == N_WORKSPACE;
670 float scroll_factor = 683 float scroll_factor =
@@ -714,6 +727,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
714 // Use the focused child of the tabbed/stacked container, not the 727 // Use the focused child of the tabbed/stacked container, not the
715 // container the user scrolled on. 728 // container the user scrolled on.
716 seat_set_focus(seat, new_focus); 729 seat_set_focus(seat, new_focus);
730 transaction_commit_dirty();
717 handled = true; 731 handled = true;
718 } 732 }
719 } 733 }
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index 17f619e3..ecc34fea 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -5,10 +5,14 @@
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/tree/view.h" 7#include "sway/tree/view.h"
8#include "sway/desktop/transaction.h"
8#include "log.h" 9#include "log.h"
9 10
10struct seatop_down_event { 11struct seatop_down_event {
11 struct sway_container *con; 12 struct sway_container *con;
13 struct sway_seat *seat;
14 struct wl_listener surface_destroy;
15 struct wlr_surface *surface;
12 double ref_lx, ref_ly; // cursor's x/y at start of op 16 double ref_lx, ref_ly; // cursor's x/y at start of op
13 double ref_con_lx, ref_con_ly; // container's x/y at start of op 17 double ref_con_lx, ref_con_ly; // container's x/y at start of op
14}; 18};
@@ -39,8 +43,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
39 43
40static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 44static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
41 struct seatop_down_event *e = seat->seatop_data; 45 struct seatop_down_event *e = seat->seatop_data;
42 struct sway_container *con = e->con; 46 if (seat_is_input_allowed(seat, e->surface)) {
43 if (seat_is_input_allowed(seat, con->view->surface)) {
44 double moved_x = seat->cursor->cursor->x - e->ref_lx; 47 double moved_x = seat->cursor->cursor->x - e->ref_lx;
45 double moved_y = seat->cursor->cursor->y - e->ref_ly; 48 double moved_y = seat->cursor->cursor->y - e->ref_ly;
46 double sx = e->ref_con_lx + moved_x; 49 double sx = e->ref_con_lx + moved_x;
@@ -61,8 +64,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
61static void handle_tablet_tool_motion(struct sway_seat *seat, 64static void handle_tablet_tool_motion(struct sway_seat *seat,
62 struct sway_tablet_tool *tool, uint32_t time_msec) { 65 struct sway_tablet_tool *tool, uint32_t time_msec) {
63 struct seatop_down_event *e = seat->seatop_data; 66 struct seatop_down_event *e = seat->seatop_data;
64 struct sway_container *con = e->con; 67 if (seat_is_input_allowed(seat, e->surface)) {
65 if (seat_is_input_allowed(seat, con->view->surface)) {
66 double moved_x = seat->cursor->cursor->x - e->ref_lx; 68 double moved_x = seat->cursor->cursor->x - e->ref_lx;
67 double moved_y = seat->cursor->cursor->y - e->ref_ly; 69 double moved_y = seat->cursor->cursor->y - e->ref_ly;
68 double sx = e->ref_con_lx + moved_x; 70 double sx = e->ref_con_lx + moved_x;
@@ -71,6 +73,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
71 } 73 }
72} 74}
73 75
76static void handle_destroy(struct wl_listener *listener, void *data) {
77 struct seatop_down_event *e =
78 wl_container_of(listener, e, surface_destroy);
79 if (e) {
80 seatop_begin_default(e->seat);
81 }
82}
83
74static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 84static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
75 struct seatop_down_event *e = seat->seatop_data; 85 struct seatop_down_event *e = seat->seatop_data;
76 if (e->con == con) { 86 if (e->con == con) {
@@ -78,6 +88,11 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
78 } 88 }
79} 89}
80 90
91static void handle_end(struct sway_seat *seat) {
92 struct seatop_down_event *e = seat->seatop_data;
93 wl_list_remove(&e->surface_destroy.link);
94}
95
81static const struct sway_seatop_impl seatop_impl = { 96static const struct sway_seatop_impl seatop_impl = {
82 .button = handle_button, 97 .button = handle_button,
83 .pointer_motion = handle_pointer_motion, 98 .pointer_motion = handle_pointer_motion,
@@ -85,11 +100,22 @@ static const struct sway_seatop_impl seatop_impl = {
85 .tablet_tool_tip = handle_tablet_tool_tip, 100 .tablet_tool_tip = handle_tablet_tool_tip,
86 .tablet_tool_motion = handle_tablet_tool_motion, 101 .tablet_tool_motion = handle_tablet_tool_motion,
87 .unref = handle_unref, 102 .unref = handle_unref,
103 .end = handle_end,
88 .allow_set_cursor = true, 104 .allow_set_cursor = true,
89}; 105};
90 106
91void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 107void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
92 uint32_t time_msec, int sx, int sy) { 108 uint32_t time_msec, double sx, double sy) {
109 seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy);
110 struct seatop_down_event *e = seat->seatop_data;
111 e->con = con;
112
113 container_raise_floating(con);
114 transaction_commit_dirty();
115}
116
117void seatop_begin_down_on_surface(struct sway_seat *seat,
118 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) {
93 seatop_end(seat); 119 seatop_end(seat);
94 120
95 struct seatop_down_event *e = 121 struct seatop_down_event *e =
@@ -97,7 +123,11 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
97 if (!e) { 123 if (!e) {
98 return; 124 return;
99 } 125 }
100 e->con = con; 126 e->con = NULL;
127 e->seat = seat;
128 e->surface = surface;
129 wl_signal_add(&e->surface->events.destroy, &e->surface_destroy);
130 e->surface_destroy.notify = handle_destroy;
101 e->ref_lx = seat->cursor->cursor->x; 131 e->ref_lx = seat->cursor->cursor->x;
102 e->ref_ly = seat->cursor->cursor->y; 132 e->ref_ly = seat->cursor->cursor->y;
103 e->ref_con_lx = sx; 133 e->ref_con_lx = sx;
@@ -105,6 +135,4 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
105 135
106 seat->seatop_impl = &seatop_impl; 136 seat->seatop_impl = &seatop_impl;
107 seat->seatop_data = e; 137 seat->seatop_data = e;
108
109 container_raise_floating(con);
110} 138}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
index 7f501fc9..ddcd4c53 100644
--- a/sway/input/seatop_move_floating.c
+++ b/sway/input/seatop_move_floating.c
@@ -1,6 +1,7 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h" 3#include "sway/desktop.h"
4#include "sway/desktop/transaction.h"
4#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
5#include "sway/input/seat.h" 6#include "sway/input/seat.h"
6 7
@@ -14,7 +15,8 @@ static void finalize_move(struct sway_seat *seat) {
14 15
15 // We "move" the container to its own location 16 // We "move" the container to its own location
16 // so it discovers its output again. 17 // so it discovers its output again.
17 container_floating_move_to(e->con, e->con->x, e->con->y); 18 container_floating_move_to(e->con, e->con->pending.x, e->con->pending.y);
19 transaction_commit_dirty();
18 20
19 seatop_begin_default(seat); 21 seatop_begin_default(seat);
20} 22}
@@ -40,6 +42,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
40 desktop_damage_whole_container(e->con); 42 desktop_damage_whole_container(e->con);
41 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); 43 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy);
42 desktop_damage_whole_container(e->con); 44 desktop_damage_whole_container(e->con);
45 transaction_commit_dirty();
43} 46}
44 47
45static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 48static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -67,13 +70,14 @@ void seatop_begin_move_floating(struct sway_seat *seat,
67 return; 70 return;
68 } 71 }
69 e->con = con; 72 e->con = con;
70 e->dx = cursor->cursor->x - con->x; 73 e->dx = cursor->cursor->x - con->pending.x;
71 e->dy = cursor->cursor->y - con->y; 74 e->dy = cursor->cursor->y - con->pending.y;
72 75
73 seat->seatop_impl = &seatop_impl; 76 seat->seatop_impl = &seatop_impl;
74 seat->seatop_data = e; 77 seat->seatop_data = e;
75 78
76 container_raise_floating(con); 79 container_raise_floating(con);
80 transaction_commit_dirty();
77 81
78 cursor_set_image(cursor, "grab", NULL); 82 cursor_set_image(cursor, "grab", NULL);
79 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 83 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 704e7270..223c6c08 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -3,6 +3,7 @@
3#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h> 4#include <wlr/util/edges.h>
5#include "sway/desktop.h" 5#include "sway/desktop.h"
6#include "sway/desktop/transaction.h"
6#include "sway/input/cursor.h" 7#include "sway/input/cursor.h"
7#include "sway/input/seat.h" 8#include "sway/input/seat.h"
8#include "sway/ipc-server.h" 9#include "sway/ipc-server.h"
@@ -15,6 +16,10 @@
15// Thickness of the dropzone when dragging to the edge of a layout container 16// Thickness of the dropzone when dragging to the edge of a layout container
16#define DROP_LAYOUT_BORDER 30 17#define DROP_LAYOUT_BORDER 30
17 18
19// Thickness of indicator when dropping onto a titlebar. This should be a
20// multiple of 2.
21#define DROP_SPLIT_INDICATOR 10
22
18struct seatop_move_tiling_event { 23struct seatop_move_tiling_event {
19 struct sway_container *con; 24 struct sway_container *con;
20 struct sway_node *target_node; 25 struct sway_node *target_node;
@@ -22,6 +27,8 @@ struct seatop_move_tiling_event {
22 struct wlr_box drop_box; 27 struct wlr_box drop_box;
23 double ref_lx, ref_ly; // cursor's x/y at start of op 28 double ref_lx, ref_ly; // cursor's x/y at start of op
24 bool threshold_reached; 29 bool threshold_reached;
30 bool split_target;
31 bool insert_after_target;
25}; 32};
26 33
27static void handle_render(struct sway_seat *seat, 34static void handle_render(struct sway_seat *seat,
@@ -91,8 +98,76 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge,
91 } 98 }
92} 99}
93 100
101static void split_border(double pos, int offset, int len, int n_children,
102 int avoid, int *out_pos, bool *out_after) {
103 int region = 2 * n_children * (pos - offset) / len;
104 // If the cursor is over the right side of a left-adjacent titlebar, or the
105 // left side of a right-adjacent titlebar, it's position when dropped will
106 // be the same. To avoid this, shift the region for adjacent containers.
107 if (avoid >= 0) {
108 if (region == 2 * avoid - 1 || region == 2 * avoid) {
109 region--;
110 } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) {
111 region++;
112 }
113 }
114
115 int child_index = (region + 1) / 2;
116 *out_after = region % 2;
117 // When dropping at the beginning or end of a container, show the drop
118 // region within the container boundary, otherwise show it on top of the
119 // border between two titlebars.
120 if (child_index == 0) {
121 *out_pos = offset;
122 } else if (child_index == n_children) {
123 *out_pos = offset + len - DROP_SPLIT_INDICATOR;
124 } else {
125 *out_pos = offset + child_index * len / n_children -
126 DROP_SPLIT_INDICATOR / 2;
127 }
128}
129
130static bool split_titlebar(struct sway_node *node, struct sway_container *avoid,
131 struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) {
132 struct sway_container *con = node->sway_container;
133 struct sway_node *parent = &con->pending.parent->node;
134 int title_height = container_titlebar_height();
135 struct wlr_box box;
136 int n_children, avoid_index;
137 enum sway_container_layout layout =
138 parent ? node_get_layout(parent) : L_NONE;
139 if (layout == L_TABBED || layout == L_STACKED) {
140 node_get_box(parent, &box);
141 n_children = node_get_children(parent)->length;
142 avoid_index = list_find(node_get_children(parent), avoid);
143 } else {
144 node_get_box(node, &box);
145 n_children = 1;
146 avoid_index = -1;
147 }
148 if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) {
149 // Drop into stacked titlebars.
150 title_box->width = box.width;
151 title_box->height = DROP_SPLIT_INDICATOR;
152 title_box->x = box.x;
153 split_border(cursor->y, box.y, title_height * n_children,
154 n_children, avoid_index, &title_box->y, after);
155 return true;
156 } else if (layout != L_STACKED && cursor->y < box.y + title_height) {
157 // Drop into side-by-side titlebars.
158 title_box->width = DROP_SPLIT_INDICATOR;
159 title_box->height = title_height;
160 title_box->y = box.y;
161 split_border(cursor->x, box.x, box.width, n_children,
162 avoid_index, &title_box->x, after);
163 return true;
164 }
165 return false;
166}
167
94static void handle_motion_postthreshold(struct sway_seat *seat) { 168static void handle_motion_postthreshold(struct sway_seat *seat) {
95 struct seatop_move_tiling_event *e = seat->seatop_data; 169 struct seatop_move_tiling_event *e = seat->seatop_data;
170 e->split_target = false;
96 struct wlr_surface *surface = NULL; 171 struct wlr_surface *surface = NULL;
97 double sx, sy; 172 double sx, sy;
98 struct sway_cursor *cursor = seat->cursor; 173 struct sway_cursor *cursor = seat->cursor;
@@ -119,34 +194,60 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
119 194
120 // Deny moving within own workspace if this is the only child 195 // Deny moving within own workspace if this is the only child
121 struct sway_container *con = node->sway_container; 196 struct sway_container *con = node->sway_container;
122 if (workspace_num_tiling_views(e->con->workspace) == 1 && 197 if (workspace_num_tiling_views(e->con->pending.workspace) == 1 &&
123 con->workspace == e->con->workspace) { 198 con->pending.workspace == e->con->pending.workspace) {
124 e->target_node = NULL; 199 e->target_node = NULL;
125 e->target_edge = WLR_EDGE_NONE; 200 e->target_edge = WLR_EDGE_NONE;
126 return; 201 return;
127 } 202 }
128 203
204 // Check if the cursor is over a tilebar only if the destination
205 // container is not a descendant of the source container.
206 if (!surface && !container_has_ancestor(con, e->con) &&
207 split_titlebar(node, e->con, cursor->cursor,
208 &e->drop_box, &e->insert_after_target)) {
209 // Don't allow dropping over the source container's titlebar
210 // to give users a chance to cancel a drag operation.
211 if (con == e->con) {
212 e->target_node = NULL;
213 } else {
214 e->target_node = node;
215 e->split_target = true;
216 }
217 e->target_edge = WLR_EDGE_NONE;
218 return;
219 }
220
129 // Traverse the ancestors, trying to find a layout container perpendicular 221 // Traverse the ancestors, trying to find a layout container perpendicular
130 // to the edge. Eg. close to the top or bottom of a horiz layout. 222 // to the edge. Eg. close to the top or bottom of a horiz layout.
223 int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER;
224 int thresh_bottom = con->pending.content_y +
225 con->pending.content_height - DROP_LAYOUT_BORDER;
226 int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER;
227 int thresh_right = con->pending.content_x +
228 con->pending.content_width - DROP_LAYOUT_BORDER;
131 while (con) { 229 while (con) {
132 enum wlr_edges edge = WLR_EDGE_NONE; 230 enum wlr_edges edge = WLR_EDGE_NONE;
133 enum sway_container_layout layout = container_parent_layout(con); 231 enum sway_container_layout layout = container_parent_layout(con);
134 struct wlr_box parent; 232 struct wlr_box box;
135 con->parent ? container_get_box(con->parent, &parent) : 233 node_get_box(node_get_parent(&con->node), &box);
136 workspace_get_box(con->workspace, &parent);
137 if (layout == L_HORIZ || layout == L_TABBED) { 234 if (layout == L_HORIZ || layout == L_TABBED) {
138 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { 235 if (cursor->cursor->y < thresh_top) {
139 edge = WLR_EDGE_TOP; 236 edge = WLR_EDGE_TOP;
140 } else if (cursor->cursor->y > parent.y + parent.height 237 box.height = thresh_top - box.y;
141 - DROP_LAYOUT_BORDER) { 238 } else if (cursor->cursor->y > thresh_bottom) {
142 edge = WLR_EDGE_BOTTOM; 239 edge = WLR_EDGE_BOTTOM;
240 box.height = box.y + box.height - thresh_bottom;
241 box.y = thresh_bottom;
143 } 242 }
144 } else if (layout == L_VERT || layout == L_STACKED) { 243 } else if (layout == L_VERT || layout == L_STACKED) {
145 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { 244 if (cursor->cursor->x < thresh_left) {
146 edge = WLR_EDGE_LEFT; 245 edge = WLR_EDGE_LEFT;
147 } else if (cursor->cursor->x > parent.x + parent.width 246 box.width = thresh_left - box.x;
148 - DROP_LAYOUT_BORDER) { 247 } else if (cursor->cursor->x > thresh_right) {
149 edge = WLR_EDGE_RIGHT; 248 edge = WLR_EDGE_RIGHT;
249 box.width = box.x + box.width - thresh_right;
250 box.x = thresh_right;
150 } 251 }
151 } 252 }
152 if (edge) { 253 if (edge) {
@@ -155,12 +256,11 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
155 e->target_node = node_get_parent(e->target_node); 256 e->target_node = node_get_parent(e->target_node);
156 } 257 }
157 e->target_edge = edge; 258 e->target_edge = edge;
158 node_get_box(e->target_node, &e->drop_box); 259 e->drop_box = box;
159 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
160 desktop_damage_box(&e->drop_box); 260 desktop_damage_box(&e->drop_box);
161 return; 261 return;
162 } 262 }
163 con = con->parent; 263 con = con->pending.parent;
164 } 264 }
165 265
166 // Use the hovered view - but we must be over the actual surface 266 // Use the hovered view - but we must be over the actual surface
@@ -173,23 +273,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
173 } 273 }
174 274
175 // Find the closest edge 275 // Find the closest edge
176 size_t thickness = fmin(con->content_width, con->content_height) * 0.3; 276 size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3;
177 size_t closest_dist = INT_MAX; 277 size_t closest_dist = INT_MAX;
178 size_t dist; 278 size_t dist;
179 e->target_edge = WLR_EDGE_NONE; 279 e->target_edge = WLR_EDGE_NONE;
180 if ((dist = cursor->cursor->y - con->y) < closest_dist) { 280 if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) {
181 closest_dist = dist; 281 closest_dist = dist;
182 e->target_edge = WLR_EDGE_TOP; 282 e->target_edge = WLR_EDGE_TOP;
183 } 283 }
184 if ((dist = cursor->cursor->x - con->x) < closest_dist) { 284 if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) {
185 closest_dist = dist; 285 closest_dist = dist;
186 e->target_edge = WLR_EDGE_LEFT; 286 e->target_edge = WLR_EDGE_LEFT;
187 } 287 }
188 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { 288 if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) {
189 closest_dist = dist; 289 closest_dist = dist;
190 e->target_edge = WLR_EDGE_RIGHT; 290 e->target_edge = WLR_EDGE_RIGHT;
191 } 291 }
192 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { 292 if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) {
193 closest_dist = dist; 293 closest_dist = dist;
194 e->target_edge = WLR_EDGE_BOTTOM; 294 e->target_edge = WLR_EDGE_BOTTOM;
195 } 295 }
@@ -199,10 +299,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
199 } 299 }
200 300
201 e->target_node = node; 301 e->target_node = node;
202 e->drop_box.x = con->content_x; 302 e->drop_box.x = con->pending.content_x;
203 e->drop_box.y = con->content_y; 303 e->drop_box.y = con->pending.content_y;
204 e->drop_box.width = con->content_width; 304 e->drop_box.width = con->pending.content_width;
205 e->drop_box.height = con->content_height; 305 e->drop_box.height = con->pending.content_height;
206 resize_box(&e->drop_box, e->target_edge, thickness); 306 resize_box(&e->drop_box, e->target_edge, thickness);
207 desktop_damage_box(&e->drop_box); 307 desktop_damage_box(&e->drop_box);
208} 308}
@@ -214,6 +314,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
214 } else { 314 } else {
215 handle_motion_prethreshold(seat); 315 handle_motion_prethreshold(seat);
216 } 316 }
317 transaction_commit_dirty();
217} 318}
218 319
219static bool is_parallel(enum sway_container_layout layout, 320static bool is_parallel(enum sway_container_layout layout,
@@ -232,14 +333,15 @@ static void finalize_move(struct sway_seat *seat) {
232 } 333 }
233 334
234 struct sway_container *con = e->con; 335 struct sway_container *con = e->con;
235 struct sway_container *old_parent = con->parent; 336 struct sway_container *old_parent = con->pending.parent;
236 struct sway_workspace *old_ws = con->workspace; 337 struct sway_workspace *old_ws = con->pending.workspace;
237 struct sway_node *target_node = e->target_node; 338 struct sway_node *target_node = e->target_node;
238 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? 339 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
239 target_node->sway_workspace : target_node->sway_container->workspace; 340 target_node->sway_workspace : target_node->sway_container->pending.workspace;
240 enum wlr_edges edge = e->target_edge; 341 enum wlr_edges edge = e->target_edge;
241 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; 342 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
242 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; 343 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER &&
344 !e->split_target;
243 345
244 if (!swap) { 346 if (!swap) {
245 container_detach(con); 347 container_detach(con);
@@ -248,6 +350,14 @@ static void finalize_move(struct sway_seat *seat) {
248 // Moving container into empty workspace 350 // Moving container into empty workspace
249 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { 351 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
250 con = workspace_add_tiling(new_ws, con); 352 con = workspace_add_tiling(new_ws, con);
353 } else if (e->split_target) {
354 struct sway_container *target = target_node->sway_container;
355 enum sway_container_layout layout = container_parent_layout(target);
356 if (layout != L_TABBED && layout != L_STACKED) {
357 container_split(target, L_TABBED);
358 }
359 container_add_sibling(target, con, e->insert_after_target);
360 ipc_event_window(con, "move");
251 } else if (target_node->type == N_CONTAINER) { 361 } else if (target_node->type == N_CONTAINER) {
252 // Moving container before/after another 362 // Moving container before/after another
253 struct sway_container *target = target_node->sway_container; 363 struct sway_container *target = target_node->sway_container;
@@ -283,8 +393,8 @@ static void finalize_move(struct sway_seat *seat) {
283 int index = list_find(siblings, con); 393 int index = list_find(siblings, con);
284 struct sway_container *sibling = index == 0 ? 394 struct sway_container *sibling = index == 0 ?
285 siblings->items[1] : siblings->items[index - 1]; 395 siblings->items[1] : siblings->items[index - 1];
286 con->width = sibling->width; 396 con->pending.width = sibling->pending.width;
287 con->height = sibling->height; 397 con->pending.height = sibling->pending.height;
288 con->width_fraction = sibling->width_fraction; 398 con->width_fraction = sibling->width_fraction;
289 con->height_fraction = sibling->height_fraction; 399 con->height_fraction = sibling->height_fraction;
290 } 400 }
@@ -294,6 +404,7 @@ static void finalize_move(struct sway_seat *seat) {
294 arrange_workspace(new_ws); 404 arrange_workspace(new_ws);
295 } 405 }
296 406
407 transaction_commit_dirty();
297 seatop_begin_default(seat); 408 seatop_begin_default(seat);
298} 409}
299 410
@@ -348,6 +459,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
348 seat->seatop_data = e; 459 seat->seatop_data = e;
349 460
350 container_raise_floating(con); 461 container_raise_floating(con);
462 transaction_commit_dirty();
351 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 463 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
352} 464}
353 465
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
index 5da22e47..df683026 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -2,6 +2,7 @@
2#include <limits.h> 2#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h> 4#include <wlr/types/wlr_xcursor_manager.h>
5#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 6#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 7#include "sway/input/seat.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
@@ -27,6 +28,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
27 if (seat->cursor->pressed_button_count == 0) { 28 if (seat->cursor->pressed_button_count == 0) {
28 container_set_resizing(con, false); 29 container_set_resizing(con, false);
29 arrange_container(con); // Send configure w/o resizing hint 30 arrange_container(con); // Send configure w/o resizing hint
31 transaction_commit_dirty();
30 seatop_begin_default(seat); 32 seatop_begin_default(seat);
31 } 33 }
32} 34}
@@ -78,17 +80,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
78 double height = e->ref_height + grow_height; 80 double height = e->ref_height + grow_height;
79 int min_width, max_width, min_height, max_height; 81 int min_width, max_width, min_height, max_height;
80 floating_calculate_constraints(&min_width, &max_width, 82 floating_calculate_constraints(&min_width, &max_width,
81 &min_height, &max_height); 83 &min_height, &max_height);
82 width = fmax(min_width + border_width, fmin(width, max_width)); 84 width = fmin(width, max_width - border_width);
83 height = fmax(min_height + border_height, fmin(height, max_height)); 85 width = fmax(width, min_width + border_width);
86 width = fmax(width, 1);
87 height = fmin(height, max_height - border_height);
88 height = fmax(height, min_height + border_height);
89 height = fmax(height, 1);
84 90
85 // Apply the view's min/max size 91 // Apply the view's min/max size
86 if (con->view) { 92 if (con->view) {
87 double view_min_width, view_max_width, view_min_height, view_max_height; 93 double view_min_width, view_max_width, view_min_height, view_max_height;
88 view_get_constraints(con->view, &view_min_width, &view_max_width, 94 view_get_constraints(con->view, &view_min_width, &view_max_width,
89 &view_min_height, &view_max_height); 95 &view_min_height, &view_max_height);
90 width = fmax(view_min_width + border_width, fmin(width, view_max_width)); 96 width = fmin(width, view_max_width - border_width);
91 height = fmax(view_min_height + border_height, fmin(height, view_max_height)); 97 width = fmax(width, view_min_width + border_width);
98 width = fmax(width, 1);
99 height = fmin(height, view_max_height - border_height);
100 height = fmax(height, view_min_height + border_height);
101 height = fmax(height, 1);
92 102
93 } 103 }
94 104
@@ -116,23 +126,24 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
116 126
117 // Determine the amounts we need to bump everything relative to the current 127 // Determine the amounts we need to bump everything relative to the current
118 // size. 128 // size.
119 int relative_grow_width = width - con->width; 129 int relative_grow_width = width - con->pending.width;
120 int relative_grow_height = height - con->height; 130 int relative_grow_height = height - con->pending.height;
121 int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; 131 int relative_grow_x = (e->ref_con_lx + grow_x) - con->pending.x;
122 int relative_grow_y = (e->ref_con_ly + grow_y) - con->y; 132 int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y;
123 133
124 // Actually resize stuff 134 // Actually resize stuff
125 con->x += relative_grow_x; 135 con->pending.x += relative_grow_x;
126 con->y += relative_grow_y; 136 con->pending.y += relative_grow_y;
127 con->width += relative_grow_width; 137 con->pending.width += relative_grow_width;
128 con->height += relative_grow_height; 138 con->pending.height += relative_grow_height;
129 139
130 con->content_x += relative_grow_x; 140 con->pending.content_x += relative_grow_x;
131 con->content_y += relative_grow_y; 141 con->pending.content_y += relative_grow_y;
132 con->content_width += relative_grow_width; 142 con->pending.content_width += relative_grow_width;
133 con->content_height += relative_grow_height; 143 con->pending.content_height += relative_grow_height;
134 144
135 arrange_container(con); 145 arrange_container(con);
146 transaction_commit_dirty();
136} 147}
137 148
138static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 149static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -166,16 +177,17 @@ void seatop_begin_resize_floating(struct sway_seat *seat,
166 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; 177 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
167 e->ref_lx = seat->cursor->cursor->x; 178 e->ref_lx = seat->cursor->cursor->x;
168 e->ref_ly = seat->cursor->cursor->y; 179 e->ref_ly = seat->cursor->cursor->y;
169 e->ref_con_lx = con->x; 180 e->ref_con_lx = con->pending.x;
170 e->ref_con_ly = con->y; 181 e->ref_con_ly = con->pending.y;
171 e->ref_width = con->width; 182 e->ref_width = con->pending.width;
172 e->ref_height = con->height; 183 e->ref_height = con->pending.height;
173 184
174 seat->seatop_impl = &seatop_impl; 185 seat->seatop_impl = &seatop_impl;
175 seat->seatop_data = e; 186 seat->seatop_data = e;
176 187
177 container_set_resizing(con, true); 188 container_set_resizing(con, true);
178 container_raise_floating(con); 189 container_raise_floating(con);
190 transaction_commit_dirty();
179 191
180 const char *image = edge == WLR_EDGE_NONE ? 192 const char *image = edge == WLR_EDGE_NONE ?
181 "se-resize" : wlr_xcursor_get_resize_name(edge); 193 "se-resize" : wlr_xcursor_get_resize_name(edge);
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
index 2cca805d..869d11b5 100644
--- a/sway/input/seatop_resize_tiling.c
+++ b/sway/input/seatop_resize_tiling.c
@@ -2,6 +2,7 @@
2#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
3#include <wlr/util/edges.h> 3#include <wlr/util/edges.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 6#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 7#include "sway/input/seat.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
@@ -52,21 +53,22 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
52 if (e->h_con) { 53 if (e->h_con) {
53 container_set_resizing(e->h_con, false); 54 container_set_resizing(e->h_con, false);
54 container_set_resizing(e->h_sib, false); 55 container_set_resizing(e->h_sib, false);
55 if (e->h_con->parent) { 56 if (e->h_con->pending.parent) {
56 arrange_container(e->h_con->parent); 57 arrange_container(e->h_con->pending.parent);
57 } else { 58 } else {
58 arrange_workspace(e->h_con->workspace); 59 arrange_workspace(e->h_con->pending.workspace);
59 } 60 }
60 } 61 }
61 if (e->v_con) { 62 if (e->v_con) {
62 container_set_resizing(e->v_con, false); 63 container_set_resizing(e->v_con, false);
63 container_set_resizing(e->v_sib, false); 64 container_set_resizing(e->v_sib, false);
64 if (e->v_con->parent) { 65 if (e->v_con->pending.parent) {
65 arrange_container(e->v_con->parent); 66 arrange_container(e->v_con->pending.parent);
66 } else { 67 } else {
67 arrange_workspace(e->v_con->workspace); 68 arrange_workspace(e->v_con->pending.workspace);
68 } 69 }
69 } 70 }
71 transaction_commit_dirty();
70 seatop_begin_default(seat); 72 seatop_begin_default(seat);
71 } 73 }
72} 74}
@@ -80,16 +82,16 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
80 82
81 if (e->h_con) { 83 if (e->h_con) {
82 if (e->edge & WLR_EDGE_LEFT) { 84 if (e->edge & WLR_EDGE_LEFT) {
83 amount_x = (e->h_con_orig_width - moved_x) - e->h_con->width; 85 amount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width;
84 } else if (e->edge & WLR_EDGE_RIGHT) { 86 } else if (e->edge & WLR_EDGE_RIGHT) {
85 amount_x = (e->h_con_orig_width + moved_x) - e->h_con->width; 87 amount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width;
86 } 88 }
87 } 89 }
88 if (e->v_con) { 90 if (e->v_con) {
89 if (e->edge & WLR_EDGE_TOP) { 91 if (e->edge & WLR_EDGE_TOP) {
90 amount_y = (e->v_con_orig_height - moved_y) - e->v_con->height; 92 amount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height;
91 } else if (e->edge & WLR_EDGE_BOTTOM) { 93 } else if (e->edge & WLR_EDGE_BOTTOM) {
92 amount_y = (e->v_con_orig_height + moved_y) - e->v_con->height; 94 amount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height;
93 } 95 }
94 } 96 }
95 97
@@ -99,6 +101,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
99 if (amount_y != 0) { 101 if (amount_y != 0) {
100 container_resize_tiled(e->v_con, e->edge_y, amount_y); 102 container_resize_tiled(e->v_con, e->edge_y, amount_y);
101 } 103 }
104 transaction_commit_dirty();
102} 105}
103 106
104static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 107static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
@@ -140,7 +143,7 @@ void seatop_begin_resize_tiling(struct sway_seat *seat,
140 if (e->h_con) { 143 if (e->h_con) {
141 container_set_resizing(e->h_con, true); 144 container_set_resizing(e->h_con, true);
142 container_set_resizing(e->h_sib, true); 145 container_set_resizing(e->h_sib, true);
143 e->h_con_orig_width = e->h_con->width; 146 e->h_con_orig_width = e->h_con->pending.width;
144 } 147 }
145 } 148 }
146 if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { 149 if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) {
@@ -151,12 +154,13 @@ void seatop_begin_resize_tiling(struct sway_seat *seat,
151 if (e->v_con) { 154 if (e->v_con) {
152 container_set_resizing(e->v_con, true); 155 container_set_resizing(e->v_con, true);
153 container_set_resizing(e->v_sib, true); 156 container_set_resizing(e->v_sib, true);
154 e->v_con_orig_height = e->v_con->height; 157 e->v_con_orig_height = e->v_con->pending.height;
155 } 158 }
156 } 159 }
157 160
158 seat->seatop_impl = &seatop_impl; 161 seat->seatop_impl = &seatop_impl;
159 seat->seatop_data = e; 162 seat->seatop_data = e;
160 163
164 transaction_commit_dirty();
161 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 165 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
162} 166}
diff --git a/sway/input/switch.c b/sway/input/switch.c
index b7c28df1..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..8357ae04 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -1,7 +1,11 @@
1#include <ctype.h>
2#include <float.h>
1#include <json.h> 3#include <json.h>
2#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
3#include <stdio.h> 5#include <stdio.h>
4#include <ctype.h> 6#include <wlr/backend/libinput.h>
7#include <wlr/types/wlr_output.h>
8#include <xkbcommon/xkbcommon.h>
5#include "config.h" 9#include "config.h"
6#include "log.h" 10#include "log.h"
7#include "sway/config.h" 11#include "sway/config.h"
@@ -13,16 +17,26 @@
13#include "sway/input/input-manager.h" 17#include "sway/input/input-manager.h"
14#include "sway/input/cursor.h" 18#include "sway/input/cursor.h"
15#include "sway/input/seat.h" 19#include "sway/input/seat.h"
16#include <wlr/backend/libinput.h>
17#include <wlr/types/wlr_box.h>
18#include <wlr/types/wlr_output.h>
19#include <xkbcommon/xkbcommon.h>
20#include "wlr-layer-shell-unstable-v1-protocol.h" 20#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22 22
23static const int i3_output_id = INT32_MAX; 23static const int i3_output_id = INT32_MAX;
24static const int i3_scratch_id = INT32_MAX - 1; 24static const int i3_scratch_id = INT32_MAX - 1;
25 25
26static const char *ipc_json_node_type_description(enum sway_node_type node_type) {
27 switch (node_type) {
28 case N_ROOT:
29 return "root";
30 case N_OUTPUT:
31 return "output";
32 case N_WORKSPACE:
33 return "workspace";
34 case N_CONTAINER:
35 return "con";
36 }
37 return "none";
38}
39
26static const char *ipc_json_layout_description(enum sway_container_layout l) { 40static const char *ipc_json_layout_description(enum sway_container_layout l) {
27 switch (l) { 41 switch (l) {
28 case L_VERT: 42 case L_VERT:
@@ -189,16 +203,22 @@ static json_object *ipc_json_create_empty_rect(void) {
189 return ipc_json_create_rect(&empty); 203 return ipc_json_create_rect(&empty);
190} 204}
191 205
192static json_object *ipc_json_create_node(int id, char *name, 206static json_object *ipc_json_create_node(int id, const char* type, char *name,
193 bool focused, json_object *focus, struct wlr_box *box) { 207 bool focused, json_object *focus, struct wlr_box *box) {
194 json_object *object = json_object_new_object(); 208 json_object *object = json_object_new_object();
195 209
196 json_object_object_add(object, "id", json_object_new_int(id)); 210 json_object_object_add(object, "id", json_object_new_int(id));
197 json_object_object_add(object, "name", 211 json_object_object_add(object, "type", json_object_new_string(type));
198 name ? json_object_new_string(name) : NULL); 212 json_object_object_add(object, "orientation",
199 json_object_object_add(object, "rect", ipc_json_create_rect(box)); 213 json_object_new_string(
214 ipc_json_orientation_description(L_HORIZ)));
215 json_object_object_add(object, "percent", NULL);
216 json_object_object_add(object, "urgent", json_object_new_boolean(false));
217 json_object_object_add(object, "marks", json_object_new_array());
200 json_object_object_add(object, "focused", json_object_new_boolean(focused)); 218 json_object_object_add(object, "focused", json_object_new_boolean(focused));
201 json_object_object_add(object, "focus", focus); 219 json_object_object_add(object, "layout",
220 json_object_new_string(
221 ipc_json_layout_description(L_HORIZ)));
202 222
203 // set default values to be compatible with i3 223 // set default values to be compatible with i3
204 json_object_object_add(object, "border", 224 json_object_object_add(object, "border",
@@ -206,35 +226,25 @@ static json_object *ipc_json_create_node(int id, char *name,
206 ipc_json_border_description(B_NONE))); 226 ipc_json_border_description(B_NONE)));
207 json_object_object_add(object, "current_border_width", 227 json_object_object_add(object, "current_border_width",
208 json_object_new_int(0)); 228 json_object_new_int(0));
209 json_object_object_add(object, "layout", 229 json_object_object_add(object, "rect", ipc_json_create_rect(box));
210 json_object_new_string(
211 ipc_json_layout_description(L_HORIZ)));
212 json_object_object_add(object, "orientation",
213 json_object_new_string(
214 ipc_json_orientation_description(L_HORIZ)));
215 json_object_object_add(object, "percent", NULL);
216 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
217 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); 230 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
231 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
218 json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); 232 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
233 json_object_object_add(object, "name",
234 name ? json_object_new_string(name) : NULL);
219 json_object_object_add(object, "window", NULL); 235 json_object_object_add(object, "window", NULL);
220 json_object_object_add(object, "urgent", json_object_new_boolean(false));
221 json_object_object_add(object, "marks", json_object_new_array());
222 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
223 json_object_object_add(object, "nodes", json_object_new_array()); 236 json_object_object_add(object, "nodes", json_object_new_array());
224 json_object_object_add(object, "floating_nodes", json_object_new_array()); 237 json_object_object_add(object, "floating_nodes", json_object_new_array());
238 json_object_object_add(object, "focus", focus);
239 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
225 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 240 json_object_object_add(object, "sticky", json_object_new_boolean(false));
226 241
227 return object; 242 return object;
228} 243}
229 244
230static void ipc_json_describe_root(struct sway_root *root, json_object *object) {
231 json_object_object_add(object, "type", json_object_new_string("root"));
232}
233
234static void ipc_json_describe_output(struct sway_output *output, 245static void ipc_json_describe_output(struct sway_output *output,
235 json_object *object) { 246 json_object *object) {
236 struct wlr_output *wlr_output = output->wlr_output; 247 struct wlr_output *wlr_output = output->wlr_output;
237 json_object_object_add(object, "type", json_object_new_string("output"));
238 json_object_object_add(object, "active", json_object_new_boolean(true)); 248 json_object_object_add(object, "active", json_object_new_boolean(true));
239 json_object_object_add(object, "dpms", 249 json_object_object_add(object, "dpms",
240 json_object_new_boolean(wlr_output->enabled)); 250 json_object_new_boolean(wlr_output->enabled));
@@ -369,11 +379,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
369 json_object_new_int(container->node.id)); 379 json_object_new_int(container->node.id));
370 } 380 }
371 381
372 json_object *workspace = ipc_json_create_node(i3_scratch_id, 382 json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace",
373 "__i3_scratch", false, workspace_focus, &box); 383 "__i3_scratch", false, workspace_focus, &box);
374 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); 384 json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1));
375 json_object_object_add(workspace, "type",
376 json_object_new_string("workspace"));
377 385
378 // List all hidden scratchpad containers as floating nodes 386 // List all hidden scratchpad containers as floating nodes
379 json_object *floating_array = json_object_new_array(); 387 json_object *floating_array = json_object_new_array();
@@ -390,10 +398,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) {
390 json_object *output_focus = json_object_new_array(); 398 json_object *output_focus = json_object_new_array();
391 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); 399 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
392 400
393 json_object *output = ipc_json_create_node(i3_output_id, 401 json_object *output = ipc_json_create_node(i3_output_id, "output",
394 "__i3", false, output_focus, &box); 402 "__i3", false, output_focus, &box);
395 json_object_object_add(output, "type",
396 json_object_new_string("output"));
397 json_object_object_add(output, "layout", 403 json_object_object_add(output, "layout",
398 json_object_new_string("output")); 404 json_object_new_string("output"));
399 405
@@ -423,7 +429,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
423 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); 429 json_object_object_add(object, "fullscreen_mode", json_object_new_int(1));
424 json_object_object_add(object, "output", workspace->output ? 430 json_object_object_add(object, "output", workspace->output ?
425 json_object_new_string(workspace->output->wlr_output->name) : NULL); 431 json_object_new_string(workspace->output->wlr_output->name) : NULL);
426 json_object_object_add(object, "type", json_object_new_string("workspace"));
427 json_object_object_add(object, "urgent", 432 json_object_object_add(object, "urgent",
428 json_object_new_boolean(workspace->urgent)); 433 json_object_new_boolean(workspace->urgent));
429 json_object_object_add(object, "representation", workspace->representation ? 434 json_object_object_add(object, "representation", workspace->representation ?
@@ -451,27 +456,27 @@ static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
451 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; 456 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED;
452 if (((!tab_or_stack || container_is_floating(c)) && 457 if (((!tab_or_stack || container_is_floating(c)) &&
453 c->current.border != B_NORMAL) || 458 c->current.border != B_NORMAL) ||
454 c->fullscreen_mode != FULLSCREEN_NONE || 459 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
455 c->workspace == NULL) { 460 c->pending.workspace == NULL) {
456 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; 461 deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0;
457 return; 462 return;
458 } 463 }
459 464
460 if (c->parent) { 465 if (c->pending.parent) {
461 deco_rect->x = c->x - c->parent->x; 466 deco_rect->x = c->pending.x - c->pending.parent->pending.x;
462 deco_rect->y = c->y - c->parent->y; 467 deco_rect->y = c->pending.y - c->pending.parent->pending.y;
463 } else { 468 } else {
464 deco_rect->x = c->x - c->workspace->x; 469 deco_rect->x = c->pending.x - c->pending.workspace->x;
465 deco_rect->y = c->y - c->workspace->y; 470 deco_rect->y = c->pending.y - c->pending.workspace->y;
466 } 471 }
467 deco_rect->width = c->width; 472 deco_rect->width = c->pending.width;
468 deco_rect->height = container_titlebar_height(); 473 deco_rect->height = container_titlebar_height();
469 474
470 if (!container_is_floating(c)) { 475 if (!container_is_floating(c)) {
471 if (parent_layout == L_TABBED) { 476 if (parent_layout == L_TABBED) {
472 deco_rect->width = c->parent 477 deco_rect->width = c->pending.parent
473 ? c->parent->width / c->parent->children->length 478 ? c->pending.parent->pending.width / c->pending.parent->pending.children->length
474 : c->workspace->width / c->workspace->tiling->length; 479 : c->pending.workspace->width / c->pending.workspace->tiling->length;
475 deco_rect->x += deco_rect->width * container_sibling_index(c); 480 deco_rect->x += deco_rect->width * container_sibling_index(c);
476 } else if (parent_layout == L_STACKED) { 481 } else if (parent_layout == L_STACKED) {
477 if (!c->view) { 482 if (!c->view) {
@@ -494,10 +499,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
494 json_object_object_add(object, "visible", json_object_new_boolean(visible)); 499 json_object_object_add(object, "visible", json_object_new_boolean(visible));
495 500
496 struct wlr_box window_box = { 501 struct wlr_box window_box = {
497 c->content_x - c->x, 502 c->pending.content_x - c->pending.x,
498 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 503 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0,
499 c->content_width, 504 c->pending.content_width,
500 c->content_height 505 c->pending.content_height
501 }; 506 };
502 507
503 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); 508 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box));
@@ -583,16 +588,18 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
583static void ipc_json_describe_container(struct sway_container *c, json_object *object) { 588static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
584 json_object_object_add(object, "name", 589 json_object_object_add(object, "name",
585 c->title ? json_object_new_string(c->title) : NULL); 590 c->title ? json_object_new_string(c->title) : NULL);
586 json_object_object_add(object, "type", 591 if (container_is_floating(c)) {
587 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 592 json_object_object_add(object, "type",
593 json_object_new_string("floating_con"));
594 }
588 595
589 json_object_object_add(object, "layout", 596 json_object_object_add(object, "layout",
590 json_object_new_string( 597 json_object_new_string(
591 ipc_json_layout_description(c->layout))); 598 ipc_json_layout_description(c->pending.layout)));
592 599
593 json_object_object_add(object, "orientation", 600 json_object_object_add(object, "orientation",
594 json_object_new_string( 601 json_object_new_string(
595 ipc_json_orientation_description(c->layout))); 602 ipc_json_orientation_description(c->pending.layout)));
596 603
597 bool urgent = c->view ? 604 bool urgent = c->view ?
598 view_is_urgent(c->view) : container_has_urgent_child(c); 605 view_is_urgent(c->view) : container_has_urgent_child(c);
@@ -600,7 +607,7 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
600 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 607 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
601 608
602 json_object_object_add(object, "fullscreen_mode", 609 json_object_object_add(object, "fullscreen_mode",
603 json_object_new_int(c->fullscreen_mode)); 610 json_object_new_int(c->pending.fullscreen_mode));
604 611
605 struct sway_node *parent = node_get_parent(&c->node); 612 struct sway_node *parent = node_get_parent(&c->node);
606 struct wlr_box parent_box = {0, 0, 0, 0}; 613 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -610,8 +617,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
610 } 617 }
611 618
612 if (parent_box.width != 0 && parent_box.height != 0) { 619 if (parent_box.width != 0 && parent_box.height != 0) {
613 double percent = ((double)c->width / parent_box.width) 620 double percent = ((double)c->pending.width / parent_box.width)
614 * ((double)c->height / parent_box.height); 621 * ((double)c->pending.height / parent_box.height);
615 json_object_object_add(object, "percent", json_object_new_double(percent)); 622 json_object_object_add(object, "percent", json_object_new_double(percent));
616 } 623 }
617 624
@@ -692,12 +699,11 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
692 }; 699 };
693 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 700 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
694 701
695 json_object *object = ipc_json_create_node( 702 json_object *object = ipc_json_create_node((int)node->id,
696 (int)node->id, name, focused, focus, &box); 703 ipc_json_node_type_description(node->type), name, focused, focus, &box);
697 704
698 switch (node->type) { 705 switch (node->type) {
699 case N_ROOT: 706 case N_ROOT:
700 ipc_json_describe_root(root, object);
701 break; 707 break;
702 case N_OUTPUT: 708 case N_OUTPUT:
703 ipc_json_describe_output(node->sway_output, object); 709 ipc_json_describe_output(node->sway_output, object);
@@ -743,10 +749,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
743 } 749 }
744 break; 750 break;
745 case N_CONTAINER: 751 case N_CONTAINER:
746 if (node->sway_container->children) { 752 if (node->sway_container->pending.children) {
747 for (i = 0; i < node->sway_container->children->length; ++i) { 753 for (i = 0; i < node->sway_container->pending.children->length; ++i) {
748 struct sway_container *child = 754 struct sway_container *child =
749 node->sway_container->children->items[i]; 755 node->sway_container->pending.children->items[i];
750 json_object_array_add(children, 756 json_object_array_add(children,
751 ipc_json_describe_node_recursive(&child->node)); 757 ipc_json_describe_node_recursive(&child->node));
752 } 758 }
@@ -974,6 +980,11 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
974 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 980 struct wlr_keyboard *keyboard = device->wlr_device->keyboard;
975 struct xkb_keymap *keymap = keyboard->keymap; 981 struct xkb_keymap *keymap = keyboard->keymap;
976 struct xkb_state *state = keyboard->xkb_state; 982 struct xkb_state *state = keyboard->xkb_state;
983
984 json_object_object_add(object, "repeat_delay",
985 json_object_new_int(keyboard->repeat_info.delay));
986 json_object_object_add(object, "repeat_rate",
987 json_object_new_int(keyboard->repeat_info.rate));
977 988
978 json_object *layouts_arr = json_object_new_array(); 989 json_object *layouts_arr = json_object_new_array();
979 json_object_object_add(object, "xkb_layout_names", layouts_arr); 990 json_object_object_add(object, "xkb_layout_names", layouts_arr);
@@ -996,6 +1007,17 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
996 } 1007 }
997 } 1008 }
998 1009
1010 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1011 struct input_config *ic = input_device_get_config(device);
1012 float scroll_factor = 1.0f;
1013 if (ic != NULL && !isnan(ic->scroll_factor) &&
1014 ic->scroll_factor != FLT_MIN) {
1015 scroll_factor = ic->scroll_factor;
1016 }
1017 json_object_object_add(object, "scroll_factor",
1018 json_object_new_double(scroll_factor));
1019 }
1020
999 if (wlr_input_device_is_libinput(device->wlr_device)) { 1021 if (wlr_input_device_is_libinput(device->wlr_device)) {
1000 struct libinput_device *libinput_dev; 1022 struct libinput_device *libinput_dev;
1001 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1023 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
@@ -1109,7 +1131,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
1109 json_object_object_add(json, "verbose", 1131 json_object_object_add(json, "verbose",
1110 json_object_new_boolean(bar->verbose)); 1132 json_object_new_boolean(bar->verbose));
1111 json_object_object_add(json, "pango_markup", 1133 json_object_object_add(json, "pango_markup",
1112 json_object_new_boolean(bar->pango_markup)); 1134 json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT
1135 ? config->pango_markup
1136 : bar->pango_markup));
1113 1137
1114 json_object *colors = json_object_new_object(); 1138 json_object *colors = json_object_new_object();
1115 json_object_object_add(colors, "background", 1139 json_object_object_add(colors, "background",
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index aad9a7b5..1bf5a05f 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -687,7 +687,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
687 } 687 }
688 struct sway_output *output; 688 struct sway_output *output;
689 wl_list_for_each(output, &root->all_outputs, link) { 689 wl_list_for_each(output, &root->all_outputs, link) {
690 if (!output->enabled && output != root->noop_output) { 690 if (!output->enabled && output != root->fallback_output) {
691 json_object_array_add(outputs, 691 json_object_array_add(outputs,
692 ipc_json_describe_disabled_output(output)); 692 ipc_json_describe_disabled_output(output));
693 } 693 }
diff --git a/sway/main.c b/sway/main.c
index 0c219fb3..b6f8a8bf 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -6,12 +6,14 @@
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include <sys/resource.h>
9#include <sys/stat.h> 10#include <sys/stat.h>
10#include <sys/types.h> 11#include <sys/types.h>
11#include <sys/wait.h> 12#include <sys/wait.h>
12#include <sys/un.h> 13#include <sys/un.h>
13#include <unistd.h> 14#include <unistd.h>
14#include <wlr/util/log.h> 15#include <wlr/util/log.h>
16#include <wlr/version.h>
15#include "sway/commands.h" 17#include "sway/commands.h"
16#include "sway/config.h" 18#include "sway/config.h"
17#include "sway/server.h" 19#include "sway/server.h"
@@ -26,6 +28,7 @@
26 28
27static bool terminate_request = false; 29static bool terminate_request = false;
28static int exit_value = 0; 30static int exit_value = 0;
31static struct rlimit original_nofile_rlimit = {0};
29struct sway_server server = {0}; 32struct sway_server server = {0};
30struct sway_debug debug = {0}; 33struct sway_debug debug = {0};
31 34
@@ -46,43 +49,6 @@ void sig_handler(int signal) {
46 sway_terminate(EXIT_SUCCESS); 49 sway_terminate(EXIT_SUCCESS);
47} 50}
48 51
49void detect_raspi(void) {
50 bool raspi = false;
51 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
52 if (!f) {
53 return;
54 }
55 char *line = NULL;
56 size_t line_size = 0;
57 while (getline(&line, &line_size, f) != -1) {
58 if (strstr(line, "Raspberry Pi")) {
59 raspi = true;
60 break;
61 }
62 }
63 fclose(f);
64 FILE *g = fopen("/proc/modules", "r");
65 if (!g) {
66 free(line);
67 return;
68 }
69 bool vc4 = false;
70 while (getline(&line, &line_size, g) != -1) {
71 if (strstr(line, "vc4")) {
72 vc4 = true;
73 break;
74 }
75 }
76 free(line);
77 fclose(g);
78 if (!vc4 && raspi) {
79 fprintf(stderr, "\x1B[1;31mWarning: You have a "
80 "Raspberry Pi, but the vc4 Module is "
81 "not loaded! Set 'dtoverlay=vc4-kms-v3d'"
82 "in /boot/config.txt and reboot.\x1B[0m\n");
83 }
84}
85
86void detect_proprietary(int allow_unsupported_gpu) { 52void detect_proprietary(int allow_unsupported_gpu) {
87 FILE *f = fopen("/proc/modules", "r"); 53 FILE *f = fopen("/proc/modules", "r");
88 if (!f) { 54 if (!f) {
@@ -99,7 +65,7 @@ void detect_proprietary(int allow_unsupported_gpu) {
99 sway_log(SWAY_ERROR, 65 sway_log(SWAY_ERROR,
100 "Proprietary Nvidia drivers are NOT supported. " 66 "Proprietary Nvidia drivers are NOT supported. "
101 "Use Nouveau. To launch sway anyway, launch with " 67 "Use Nouveau. To launch sway anyway, launch with "
102 "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); 68 "--unsupported-gpu and DO NOT report issues.");
103 exit(EXIT_FAILURE); 69 exit(EXIT_FAILURE);
104 } 70 }
105 break; 71 break;
@@ -187,6 +153,9 @@ static void log_kernel(void) {
187 153
188static bool drop_permissions(void) { 154static bool drop_permissions(void) {
189 if (getuid() != geteuid() || getgid() != getegid()) { 155 if (getuid() != geteuid() || getgid() != getegid()) {
156 sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: "
157 "SUID privilege drop will be removed in a future release, please migrate to seatd-launch");
158
190 // Set the gid and uid in the correct order. 159 // Set the gid and uid in the correct order.
191 if (setgid(getgid()) != 0) { 160 if (setgid(getgid()) != 0) {
192 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); 161 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
@@ -205,6 +174,33 @@ static bool drop_permissions(void) {
205 return true; 174 return true;
206} 175}
207 176
177static void increase_nofile_limit(void) {
178 if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
179 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
180 "getrlimit(NOFILE) failed");
181 return;
182 }
183
184 struct rlimit new_rlimit = original_nofile_rlimit;
185 new_rlimit.rlim_cur = new_rlimit.rlim_max;
186 if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {
187 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
188 "setrlimit(NOFILE) failed");
189 sway_log(SWAY_INFO, "Running with %d max open files",
190 (int)original_nofile_rlimit.rlim_cur);
191 }
192}
193
194void restore_nofile_limit(void) {
195 if (original_nofile_rlimit.rlim_cur == 0) {
196 return;
197 }
198 if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
199 sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: "
200 "setrlimit(NOFILE) failed");
201 }
202}
203
208void enable_debug_flag(const char *flag) { 204void enable_debug_flag(const char *flag) {
209 if (strcmp(flag, "damage=highlight") == 0) { 205 if (strcmp(flag, "damage=highlight") == 0) {
210 debug.damage = DAMAGE_HIGHLIGHT; 206 debug.damage = DAMAGE_HIGHLIGHT;
@@ -218,6 +214,8 @@ void enable_debug_flag(const char *flag) {
218 debug.txn_timings = true; 214 debug.txn_timings = true;
219 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 215 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
220 server.txn_timeout_ms = atoi(&flag[12]); 216 server.txn_timeout_ms = atoi(&flag[12]);
217 } else if (strcmp(flag, "noscanout") == 0) {
218 debug.noscanout = true;
221 } else { 219 } else {
222 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 220 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
223 } 221 }
@@ -245,7 +243,7 @@ static void handle_wlr_log(enum wlr_log_importance importance,
245int main(int argc, char **argv) { 243int main(int argc, char **argv) {
246 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 244 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0;
247 245
248 static struct option long_options[] = { 246 static const struct option long_options[] = {
249 {"help", no_argument, NULL, 'h'}, 247 {"help", no_argument, NULL, 'h'},
250 {"config", required_argument, NULL, 'c'}, 248 {"config", required_argument, NULL, 'c'},
251 {"validate", no_argument, NULL, 'C'}, 249 {"validate", no_argument, NULL, 'C'},
@@ -254,7 +252,6 @@ int main(int argc, char **argv) {
254 {"verbose", no_argument, NULL, 'V'}, 252 {"verbose", no_argument, NULL, 'V'},
255 {"get-socketpath", no_argument, NULL, 'p'}, 253 {"get-socketpath", no_argument, NULL, 'p'},
256 {"unsupported-gpu", no_argument, NULL, 'u'}, 254 {"unsupported-gpu", no_argument, NULL, 'u'},
257 {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'},
258 {0, 0, 0, 0} 255 {0, 0, 0, 0}
259 }; 256 };
260 257
@@ -344,11 +341,10 @@ int main(int argc, char **argv) {
344 } 341 }
345 342
346 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); 343 sway_log(SWAY_INFO, "Sway version " SWAY_VERSION);
344 sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR);
347 log_kernel(); 345 log_kernel();
348 log_distro(); 346 log_distro();
349 log_env(); 347 log_env();
350 detect_proprietary(allow_unsupported_gpu);
351 detect_raspi();
352 348
353 if (optind < argc) { // Behave as IPC client 349 if (optind < argc) { // Behave as IPC client
354 if (optind != 1) { 350 if (optind != 1) {
@@ -375,6 +371,8 @@ int main(int argc, char **argv) {
375 return 0; 371 return 0;
376 } 372 }
377 373
374 detect_proprietary(allow_unsupported_gpu);
375
378 if (!server_privileged_prepare(&server)) { 376 if (!server_privileged_prepare(&server)) {
379 return 1; 377 return 1;
380 } 378 }
@@ -384,6 +382,8 @@ int main(int argc, char **argv) {
384 exit(EXIT_FAILURE); 382 exit(EXIT_FAILURE);
385 } 383 }
386 384
385 increase_nofile_limit();
386
387 // handle SIGTERM signals 387 // handle SIGTERM signals
388 signal(SIGTERM, sig_handler); 388 signal(SIGTERM, sig_handler);
389 signal(SIGINT, sig_handler); 389 signal(SIGINT, sig_handler);
diff --git a/sway/meson.build b/sway/meson.build
index 6e138101..8eab31a2 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',
@@ -187,6 +188,7 @@ sway_sources = files(
187 'commands/output/max_render_time.c', 188 'commands/output/max_render_time.c',
188 'commands/output/mode.c', 189 'commands/output/mode.c',
189 'commands/output/position.c', 190 'commands/output/position.c',
191 'commands/output/render_bit_depth.c',
190 'commands/output/scale.c', 192 'commands/output/scale.c',
191 'commands/output/scale_filter.c', 193 'commands/output/scale_filter.c',
192 'commands/output/subpixel.c', 194 'commands/output/subpixel.c',
@@ -204,9 +206,11 @@ sway_sources = files(
204 206
205sway_deps = [ 207sway_deps = [
206 cairo, 208 cairo,
209 drm,
207 jsonc, 210 jsonc,
208 libevdev, 211 libevdev,
209 libinput, 212 libinput,
213 libudev,
210 math, 214 math,
211 pango, 215 pango,
212 pcre, 216 pcre,
diff --git a/sway/server.c b/sway/server.c
index f51fcfe2..567e6ae4 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -7,24 +7,32 @@
7#include <wlr/backend.h> 7#include <wlr/backend.h>
8#include <wlr/backend/headless.h> 8#include <wlr/backend/headless.h>
9#include <wlr/backend/multi.h> 9#include <wlr/backend/multi.h>
10#include <wlr/backend/noop.h>
11#include <wlr/backend/session.h> 10#include <wlr/backend/session.h>
11#include <wlr/config.h>
12#include <wlr/render/wlr_renderer.h> 12#include <wlr/render/wlr_renderer.h>
13#include <wlr/types/wlr_compositor.h> 13#include <wlr/types/wlr_compositor.h>
14#include <wlr/types/wlr_data_control_v1.h> 14#include <wlr/types/wlr_data_control_v1.h>
15#include <wlr/types/wlr_drm_lease_v1.h>
16#include <wlr/types/wlr_drm.h>
15#include <wlr/types/wlr_export_dmabuf_v1.h> 17#include <wlr/types/wlr_export_dmabuf_v1.h>
16#include <wlr/types/wlr_gamma_control_v1.h> 18#include <wlr/types/wlr_gamma_control_v1.h>
17#include <wlr/types/wlr_idle.h> 19#include <wlr/types/wlr_idle.h>
18#include <wlr/types/wlr_layer_shell_v1.h> 20#include <wlr/types/wlr_layer_shell_v1.h>
21#include <wlr/types/wlr_linux_dmabuf_v1.h>
19#include <wlr/types/wlr_pointer_constraints_v1.h> 22#include <wlr/types/wlr_pointer_constraints_v1.h>
20#include <wlr/types/wlr_primary_selection_v1.h> 23#include <wlr/types/wlr_primary_selection_v1.h>
21#include <wlr/types/wlr_relative_pointer_v1.h> 24#include <wlr/types/wlr_relative_pointer_v1.h>
22#include <wlr/types/wlr_screencopy_v1.h> 25#include <wlr/types/wlr_screencopy_v1.h>
23#include <wlr/types/wlr_server_decoration.h> 26#include <wlr/types/wlr_server_decoration.h>
27#include <wlr/types/wlr_subcompositor.h>
24#include <wlr/types/wlr_tablet_v2.h> 28#include <wlr/types/wlr_tablet_v2.h>
25#include <wlr/types/wlr_viewporter.h> 29#include <wlr/types/wlr_viewporter.h>
26#include <wlr/types/wlr_xcursor_manager.h> 30#include <wlr/types/wlr_xcursor_manager.h>
31#include <wlr/types/wlr_xdg_activation_v1.h>
27#include <wlr/types/wlr_xdg_decoration_v1.h> 32#include <wlr/types/wlr_xdg_decoration_v1.h>
33#include <wlr/types/wlr_xdg_foreign_registry.h>
34#include <wlr/types/wlr_xdg_foreign_v1.h>
35#include <wlr/types/wlr_xdg_foreign_v2.h>
28#include <wlr/types/wlr_xdg_output_v1.h> 36#include <wlr/types/wlr_xdg_output_v1.h>
29#include "config.h" 37#include "config.h"
30#include "list.h" 38#include "list.h"
@@ -52,19 +60,50 @@ bool server_privileged_prepare(struct sway_server *server) {
52 return true; 60 return true;
53} 61}
54 62
63static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
64 /* We only offer non-desktop outputs, but in the future we might want to do
65 * more logic here. */
66
67 struct wlr_drm_lease_request_v1 *req = data;
68 struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
69 if (!lease) {
70 sway_log(SWAY_ERROR, "Failed to grant lease request");
71 wlr_drm_lease_request_v1_reject(req);
72 }
73}
74
55bool server_init(struct sway_server *server) { 75bool server_init(struct sway_server *server) {
56 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 76 sway_log(SWAY_DEBUG, "Initializing Wayland server");
57 77
58 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); 78 server->renderer = wlr_renderer_autocreate(server->backend);
59 assert(renderer); 79 if (!server->renderer) {
80 sway_log(SWAY_ERROR, "Failed to create renderer");
81 return false;
82 }
83
84 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
85
86 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) {
87 wlr_drm_create(server->wl_display, server->renderer);
88 server->linux_dmabuf_v1 =
89 wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer);
90 }
60 91
61 wlr_renderer_init_wl_display(renderer, server->wl_display); 92 server->allocator = wlr_allocator_autocreate(server->backend,
93 server->renderer);
94 if (!server->allocator) {
95 sway_log(SWAY_ERROR, "Failed to create allocator");
96 return false;
97 }
62 98
63 server->compositor = wlr_compositor_create(server->wl_display, renderer); 99 server->compositor = wlr_compositor_create(server->wl_display,
100 server->renderer);
64 server->compositor_new_surface.notify = handle_compositor_new_surface; 101 server->compositor_new_surface.notify = handle_compositor_new_surface;
65 wl_signal_add(&server->compositor->events.new_surface, 102 wl_signal_add(&server->compositor->events.new_surface,
66 &server->compositor_new_surface); 103 &server->compositor_new_surface);
67 104
105 wlr_subcompositor_create(server->wl_display);
106
68 server->data_device_manager = 107 server->data_device_manager =
69 wlr_data_device_manager_create(server->wl_display); 108 wlr_data_device_manager_create(server->wl_display);
70 109
@@ -144,12 +183,34 @@ bool server_init(struct sway_server *server) {
144 server->foreign_toplevel_manager = 183 server->foreign_toplevel_manager =
145 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 184 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
146 185
186 server->drm_lease_manager=
187 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
188 if (server->drm_lease_manager) {
189 server->drm_lease_request.notify = handle_drm_lease_request;
190 wl_signal_add(&server->drm_lease_manager->events.request,
191 &server->drm_lease_request);
192 } else {
193 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
194 sway_log(SWAY_INFO, "VR will not be available");
195 }
196
147 wlr_export_dmabuf_manager_v1_create(server->wl_display); 197 wlr_export_dmabuf_manager_v1_create(server->wl_display);
148 wlr_screencopy_manager_v1_create(server->wl_display); 198 wlr_screencopy_manager_v1_create(server->wl_display);
149 wlr_data_control_manager_v1_create(server->wl_display); 199 wlr_data_control_manager_v1_create(server->wl_display);
150 wlr_primary_selection_v1_device_manager_create(server->wl_display); 200 wlr_primary_selection_v1_device_manager_create(server->wl_display);
151 wlr_viewporter_create(server->wl_display); 201 wlr_viewporter_create(server->wl_display);
152 202
203 struct wlr_xdg_foreign_registry *foreign_registry =
204 wlr_xdg_foreign_registry_create(server->wl_display);
205 wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);
206 wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry);
207
208 server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display);
209 server->xdg_activation_v1_request_activate.notify =
210 xdg_activation_v1_handle_request_activate;
211 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
212 &server->xdg_activation_v1_request_activate);
213
153 // Avoid using "wayland-0" as display socket 214 // Avoid using "wayland-0" as display socket
154 char name_candidate[16]; 215 char name_candidate[16];
155 for (int i = 1; i <= 32; ++i) { 216 for (int i = 1; i <= 32; ++i) {
@@ -166,27 +227,26 @@ bool server_init(struct sway_server *server) {
166 return false; 227 return false;
167 } 228 }
168 229
169 server->noop_backend = wlr_noop_backend_create(server->wl_display); 230 server->headless_backend = wlr_headless_backend_create(server->wl_display);
170
171 struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend);
172 root->noop_output = output_create(wlr_output);
173
174 server->headless_backend =
175 wlr_headless_backend_create_with_renderer(server->wl_display, renderer);
176 if (!server->headless_backend) { 231 if (!server->headless_backend) {
177 sway_log(SWAY_INFO, "Failed to create secondary headless backend, " 232 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
178 "starting without it"); 233 wlr_backend_destroy(server->backend);
234 return false;
179 } else { 235 } else {
180 wlr_multi_backend_add(server->backend, server->headless_backend); 236 wlr_multi_backend_add(server->backend, server->headless_backend);
181 } 237 }
182 238
239 struct wlr_output *wlr_output =
240 wlr_headless_add_output(server->headless_backend, 800, 600);
241 wlr_output_set_name(wlr_output, "FALLBACK");
242 root->fallback_output = output_create(wlr_output);
243
183 // This may have been set already via -Dtxn-timeout 244 // This may have been set already via -Dtxn-timeout
184 if (!server->txn_timeout_ms) { 245 if (!server->txn_timeout_ms) {
185 server->txn_timeout_ms = 200; 246 server->txn_timeout_ms = 200;
186 } 247 }
187 248
188 server->dirty_nodes = create_list(); 249 server->dirty_nodes = create_list();
189 server->transactions = create_list();
190 250
191 server->input = input_manager_create(server); 251 server->input = input_manager_create(server);
192 input_manager_get_default_seat(); // create seat0 252 input_manager_get_default_seat(); // create seat0
@@ -202,7 +262,6 @@ void server_fini(struct sway_server *server) {
202 wl_display_destroy_clients(server->wl_display); 262 wl_display_destroy_clients(server->wl_display);
203 wl_display_destroy(server->wl_display); 263 wl_display_destroy(server->wl_display);
204 list_free(server->dirty_nodes); 264 list_free(server->dirty_nodes);
205 list_free(server->transactions);
206} 265}
207 266
208bool server_start(struct sway_server *server) { 267bool server_start(struct sway_server *server) {
@@ -238,6 +297,7 @@ bool server_start(struct sway_server *server) {
238 wlr_backend_destroy(server->backend); 297 wlr_backend_destroy(server->backend);
239 return false; 298 return false;
240 } 299 }
300
241 return true; 301 return true;
242} 302}
243 303
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 80d08449..42e59d57 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -40,7 +40,7 @@ runtime.
40*font* <font> 40*font* <font>
41 Specifies the font to be used in the bar. _font_ should be specified as a 41 Specifies the font to be used in the bar. _font_ should be specified as a
42 pango font description. For more information on pango font descriptions, 42 pango font description. For more information on pango font descriptions,
43 see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 43 see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
44 44
45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> 45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
46 Sets the gaps from the edge of the screen for the bar. Gaps can either be 46 Sets the gaps from the edge of the screen for the bar. Gaps can either be
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index dbf21d93..8b702b77 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -111,6 +111,9 @@ The following commands may only be used in the configuration file.
111 Maps inputs from this device to the specified output. Only meaningful if the 111 Maps inputs from this device to the specified output. Only meaningful if the
112 device is a pointer, touch, or drawing tablet device. 112 device is a pointer, touch, or drawing tablet device.
113 113
114 The wildcard _\*_ can be used to map the input device to the whole desktop
115 layout.
116
114*input* <identifier> map_to_region <X> <Y> <width> <height> 117*input* <identifier> map_to_region <X> <Y> <width> <height>
115 Maps inputs from this device to the specified region of the global output 118 Maps inputs from this device to the specified region of the global output
116 layout. Only meaningful if the device is a pointer, touch, or drawing tablet 119 layout. Only meaningful if the device is a pointer, touch, or drawing tablet
@@ -230,7 +233,7 @@ correct seat.
230 not explicitly attached to another seat (similar to a "default" seat). 233 not explicitly attached to another seat (similar to a "default" seat).
231 234
232*seat* <name> hide_cursor <timeout>|when-typing [enable|disable] 235*seat* <name> hide_cursor <timeout>|when-typing [enable|disable]
233 Hides the cursor image after the specified event occured. 236 Hides the cursor image after the specified event occurred.
234 237
235 If _timeout_ is specified, then the cursor will be hidden after _timeout_ 238 If _timeout_ is specified, then the cursor will be hidden after _timeout_
236 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 239 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0
@@ -274,7 +277,7 @@ correct seat.
274 whether future inhibitors are honoured by default, i.e. activated 277 whether future inhibitors are honoured by default, i.e. activated
275 automatically, the default being _enable_. When used at runtime, 278 automatically, the default being _enable_. When used at runtime,
276 _disable_ also disables any currently active inhibitors. _activate_, 279 _disable_ also disables any currently active inhibitors. _activate_,
277 _deactivate_ and _toggle_ are only useable at runtime and change the 280 _deactivate_ and _toggle_ are only usable at runtime and change the
278 state of a potentially existing inhibitor on the currently focused 281 state of a potentially existing inhibitor on the currently focused
279 window. This can be used with the current seat alias (_-_) to affect 282 window. This can be used with the current seat alias (_-_) to affect
280 only the currently focused window of the current seat. Subcommand 283 only the currently focused window of the current seat. Subcommand
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 1b855959..d90fe97a 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -294,7 +294,7 @@ following properties:
294Retrieve a JSON representation of the tree 294Retrieve a JSON representation of the tree
295 295
296*REPLY*++ 296*REPLY*++
297An array of object the represent the current tree. Each object represents one 297An array of objects that represent the current tree. Each object represents one
298node and will have the following properties: 298node and will have the following properties:
299 299
300[- *PROPERTY* 300[- *PROPERTY*
@@ -1131,6 +1131,9 @@ following properties:
1131|- xkb_active_layout_index 1131|- xkb_active_layout_index
1132: integer 1132: integer
1133: (Only keyboards) The index of the active keyboard layout in use 1133: (Only keyboards) The index of the active keyboard layout in use
1134|- scroll_factor
1135: floating
1136: (Only pointers) Multiplier applied on scroll event values.
1134|- libinput 1137|- libinput
1135: object 1138: object
1136: (Only libinput devices) An object describing the current device settings. 1139: (Only libinput devices) An object describing the current device settings.
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 69f529fe..4159a851 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -40,6 +40,16 @@ must be separated by one space. For example:
40 40
41 output HDMI-A-1 mode 1920x1080@60Hz 41 output HDMI-A-1 mode 1920x1080@60Hz
42 42
43*output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync>
44 Configures the specified output to use the given modeline. It can be
45 generated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5).
46 Only supported on DRM backend.
47
48 Example:
49
50 output HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync
51
52
43*output* <name> position|pos <X> <Y> 53*output* <name> position|pos <X> <Y>
44 Places the specified output at the specific position in the global 54 Places the specified output at the specific position in the global
45 coordinate space. The cursor may only be moved between immediately 55 coordinate space. The cursor may only be moved between immediately
@@ -112,7 +122,7 @@ must be separated by one space. For example:
112*output* <name> toggle 122*output* <name> toggle
113 Toggle the specified output. 123 Toggle the specified output.
114 124
115*output* <name> dpms on|off 125*output* <name> dpms on|off|toggle
116 Enables or disables the specified output via DPMS. To turn an output off 126 Enables or disables the specified output via DPMS. To turn an output off
117 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. 127 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
118 128
@@ -142,11 +152,26 @@ must be separated by one space. For example:
142 Enables or disables adaptive synchronization (often referred to as Variable 152 Enables or disables adaptive synchronization (often referred to as Variable
143 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). 153 Refresh Rate, or by the vendor-specific names FreeSync/G-Sync).
144 154
145 Adaptive sync allows clients to submit frames a little to late without 155 Adaptive sync allows clients to submit frames a little too late without
146 having to wait a whole refresh period to display it on screen. Enabling 156 having to wait a whole refresh period to display it on screen. Enabling
147 adaptive sync can improve latency, but can cause flickering on some 157 adaptive sync can improve latency, but can cause flickering on some
148 hardware. 158 hardware.
149 159
160*output* <name> render_bit_depth 8|10
161 Controls the color channel bit depth at which frames are rendered; the
162 default is currently 8 bits per channel.
163
164 Setting higher values will not have an effect if hardware and software lack
165 support for such bit depths. Successfully increasing the render bit depth
166 will not necessarily increase the bit depth of the frames sent to a display.
167 An increased render bit depth may provide smoother rendering of gradients,
168 and screenshots which can more precisely store the colors of programs
169 which display high bit depth colors.
170
171 Warnings: this can break screenshot/screencast programs which have not been
172 updated to work with different bit depths. This command is experimental,
173 and may be removed or changed in the future.
174
150# SEE ALSO 175# SEE ALSO
151 176
152*sway*(5) *sway-input*(5) 177*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 02592b5f..641d0925 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -155,7 +155,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
155 is specified, the view will be fullscreen across all outputs. 155 is specified, the view will be fullscreen across all outputs.
156 156
157*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current 157*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current
158set|plus|minus <amount> 158set|plus|minus|toggle <amount>
159 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the 159 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the
160 _current_ workspace. _outer_ gaps can be altered per side with _top_, 160 _current_ workspace. _outer_ gaps can be altered per side with _top_,
161 _right_, _bottom_, and _left_ or per direction with _horizontal_ and 161 _right_, _bottom_, and _left_ or per direction with _horizontal_ and
@@ -215,7 +215,7 @@ set|plus|minus <amount>
215 If unspecified, the default is 10 pixels. Pixels are ignored when moving 215 If unspecified, the default is 10 pixels. Pixels are ignored when moving
216 tiled containers. 216 tiled containers.
217 217
218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] 218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
219 Moves the focused container to the specified position in the workspace. 219 Moves the focused container to the specified position in the workspace.
220 The position can be specified in pixels or percentage points, omitting 220 The position can be specified in pixels or percentage points, omitting
221 the unit defaults to pixels. If _absolute_ is used, the position is 221 the unit defaults to pixels. If _absolute_ is used, the position is
@@ -319,8 +319,10 @@ set|plus|minus <amount>
319 established by the *seat* subcommand of the same name. See 319 established by the *seat* subcommand of the same name. See
320 *sway-input*(5) for more ways to affect inhibitors. 320 *sway-input*(5) for more ways to affect inhibitors.
321 321
322*split* vertical|v|horizontal|h|toggle|t 322*split* vertical|v|horizontal|h|none|n|toggle|t
323 Splits the current container, vertically or horizontally. When _toggle_ is 323 Splits the current container, vertically or horizontally. When _none_ is
324 specified, the effect of a previous split is undone if the current
325 container is the only child of a split parent. When _toggle_ is
324 specified, the current container is split opposite to the parent 326 specified, the current container is split opposite to the parent
325 container's layout. 327 container's layout.
326 328
@@ -404,7 +406,7 @@ runtime.
404 a keyboard shortcuts inhibitor is active for the currently focused 406 a keyboard shortcuts inhibitor is active for the currently focused
405 window. Such inhibitors are usually requested by remote desktop and 407 window. Such inhibitors are usually requested by remote desktop and
406 virtualization software to enable the user to send keyboard shortcuts 408 virtualization software to enable the user to send keyboard shortcuts
407 to the remote or virtual session. The _--inhibited_ flag allows to 409 to the remote or virtual session. The _--inhibited_ flag allows one to
408 define bindings which will be exempt from pass-through to such 410 define bindings which will be exempt from pass-through to such
409 software. The same preference logic as for _--locked_ applies. 411 software. The same preference logic as for _--locked_ applies.
410 412
@@ -497,6 +499,12 @@ runtime.
497 *client.focused_inactive* 499 *client.focused_inactive*
498 The most recently focused view within a container which is not focused. 500 The most recently focused view within a container which is not focused.
499 501
502 *client.focused_tab_title*
503 A view that has focused descendant container.
504 Tab or stack container title that is the parent of the focused container
505 but is not directly focused. Defaults to focused_inactive if not
506 specified and does not use the indicator and child_border colors.
507
500 *client.placeholder* 508 *client.placeholder*
501 Ignored (present for i3 compatibility). 509 Ignored (present for i3 compatibility).
502 510
@@ -552,6 +560,12 @@ The default colors are:
552: #ffffff 560: #ffffff
553: #484e50 561: #484e50
554: #5f676a 562: #5f676a
563| *focused_tab_title*
564: #333333
565: #5f676a
566: #ffffff
567: n/a
568: n/a
555| *unfocused* 569| *unfocused*
556: #333333 570: #333333
557: #222222 571: #222222
@@ -630,14 +644,14 @@ The default colors are:
630 should be used instead. Regardless of whether pango markup is enabled, 644 should be used instead. Regardless of whether pango markup is enabled,
631 _font_ should be specified as a pango font description. For more 645 _font_ should be specified as a pango font description. For more
632 information on pango font descriptions, see 646 information on pango font descriptions, see
633 https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 647 https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
634 648
635*force_display_urgency_hint* <timeout> [ms] 649*force_display_urgency_hint* <timeout> [ms]
636 If an application on another workspace sets an urgency hint, switching to this 650 If an application on another workspace sets an urgency hint, switching to this
637 workspace may lead to immediate focus of the application, which also means the 651 workspace may lead to immediate focus of the application, which also means the
638 window decoration color would be immediately resetted to *client.focused*. This 652 window decoration color would be immediately reset to *client.focused*. This
639 may make it unnecessarily hard to tell which window originally raised the 653 may make it unnecessarily hard to tell which window originally raised the
640 event. This option allows to set a _timeout_ in ms to delay the urgency hint reset. 654 event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset.
641 655
642*titlebar_border_thickness* <thickness> 656*titlebar_border_thickness* <thickness>
643 Thickness of the titlebar border in pixels 657 Thickness of the titlebar border in pixels
@@ -690,9 +704,10 @@ The default colors are:
690 borders will only be enabled if the workspace has more than one visible 704 borders will only be enabled if the workspace has more than one visible
691 child and gaps equal to zero. 705 child and gaps equal to zero.
692 706
693*smart_gaps* on|off 707*smart_gaps* on|off|toggle|inverse_outer
694 If smart_gaps are _on_ gaps will only be enabled if a workspace has more 708 If smart_gaps are _on_ gaps will only be enabled if a workspace has more
695 than one child. 709 than one child. If smart_gaps are _inverse_outer_ outer gaps will only
710 be enabled if a workspace has exactly one child.
696 711
697*mark* --add|--replace [--toggle] <identifier> 712*mark* --add|--replace [--toggle] <identifier>
698 Marks are arbitrary labels that can be used to identify certain windows and 713 Marks are arbitrary labels that can be used to identify certain windows and
diff --git a/sway/swaynag.c b/sway/swaynag.c
index db5a919a..4a0a6d30 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -64,6 +64,8 @@ bool swaynag_spawn(const char *swaynag_command,
64 sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); 64 sway_log(SWAY_ERROR, "Failed to create fork for swaynag");
65 goto failed; 65 goto failed;
66 } else if (pid == 0) { 66 } else if (pid == 0) {
67 restore_nofile_limit();
68
67 pid = fork(); 69 pid = fork();
68 if (pid < 0) { 70 if (pid < 0) {
69 sway_log_errno(SWAY_ERROR, "fork failed"); 71 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -87,8 +89,8 @@ bool swaynag_spawn(const char *swaynag_command,
87 size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; 89 size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2;
88 char *cmd = malloc(length); 90 char *cmd = malloc(length);
89 snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); 91 snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args);
90 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 92 execlp("sh", "sh", "-c", cmd, NULL);
91 sway_log_errno(SWAY_ERROR, "execl failed"); 93 sway_log_errno(SWAY_ERROR, "execlp failed");
92 _exit(EXIT_FAILURE); 94 _exit(EXIT_FAILURE);
93 } 95 }
94 _exit(EXIT_SUCCESS); 96 _exit(EXIT_SUCCESS);
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index bac9f2fa..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..4756028c 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,12 +1,18 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <drm_fourcc.h>
3#include <stdint.h> 4#include <stdint.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h> 6#include <string.h>
6#include <strings.h> 7#include <strings.h>
8#include <sys/stat.h>
7#include <wayland-server-core.h> 9#include <wayland-server-core.h>
10#include <wlr/types/wlr_linux_dmabuf_v1.h>
8#include <wlr/types/wlr_output_layout.h> 11#include <wlr/types/wlr_output_layout.h>
9#include "cairo.h" 12#include <wlr/types/wlr_subcompositor.h>
13#include <wlr/render/drm_format_set.h>
14#include "linux-dmabuf-unstable-v1-protocol.h"
15#include "cairo_util.h"
10#include "pango.h" 16#include "pango.h"
11#include "sway/config.h" 17#include "sway/config.h"
12#include "sway/desktop.h" 18#include "sway/desktop.h"
@@ -19,6 +25,7 @@
19#include "sway/tree/arrange.h" 25#include "sway/tree/arrange.h"
20#include "sway/tree/view.h" 26#include "sway/tree/view.h"
21#include "sway/tree/workspace.h" 27#include "sway/tree/workspace.h"
28#include "sway/xdg_decoration.h"
22#include "list.h" 29#include "list.h"
23#include "log.h" 30#include "log.h"
24#include "stringop.h" 31#include "stringop.h"
@@ -30,12 +37,12 @@ struct sway_container *container_create(struct sway_view *view) {
30 return NULL; 37 return NULL;
31 } 38 }
32 node_init(&c->node, N_CONTAINER, c); 39 node_init(&c->node, N_CONTAINER, c);
33 c->layout = L_NONE; 40 c->pending.layout = L_NONE;
34 c->view = view; 41 c->view = view;
35 c->alpha = 1.0f; 42 c->alpha = 1.0f;
36 43
37 if (!view) { 44 if (!view) {
38 c->children = create_list(); 45 c->pending.children = create_list();
39 c->current.children = create_list(); 46 c->current.children = create_list();
40 } 47 }
41 c->marks = create_list(); 48 c->marks = create_list();
@@ -62,7 +69,8 @@ void container_destroy(struct sway_container *con) {
62 wlr_texture_destroy(con->title_focused_inactive); 69 wlr_texture_destroy(con->title_focused_inactive);
63 wlr_texture_destroy(con->title_unfocused); 70 wlr_texture_destroy(con->title_unfocused);
64 wlr_texture_destroy(con->title_urgent); 71 wlr_texture_destroy(con->title_urgent);
65 list_free(con->children); 72 wlr_texture_destroy(con->title_focused_tab_title);
73 list_free(con->pending.children);
66 list_free(con->current.children); 74 list_free(con->current.children);
67 list_free(con->outputs); 75 list_free(con->outputs);
68 76
@@ -71,11 +79,10 @@ void container_destroy(struct sway_container *con) {
71 wlr_texture_destroy(con->marks_focused_inactive); 79 wlr_texture_destroy(con->marks_focused_inactive);
72 wlr_texture_destroy(con->marks_unfocused); 80 wlr_texture_destroy(con->marks_unfocused);
73 wlr_texture_destroy(con->marks_urgent); 81 wlr_texture_destroy(con->marks_urgent);
82 wlr_texture_destroy(con->marks_focused_tab_title);
74 83
75 if (con->view) { 84 if (con->view && con->view->container == con) {
76 if (con->view->container == con) { 85 con->view->container = NULL;
77 con->view->container = NULL;
78 }
79 if (con->view->destroying) { 86 if (con->view->destroying) {
80 view_destroy(con->view); 87 view_destroy(con->view);
81 } 88 }
@@ -90,10 +97,10 @@ void container_begin_destroy(struct sway_container *con) {
90 } 97 }
91 // The workspace must have the fullscreen pointer cleared so that the 98 // The workspace must have the fullscreen pointer cleared so that the
92 // seat code can find an appropriate new focus. 99 // seat code can find an appropriate new focus.
93 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { 100 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) {
94 con->workspace->fullscreen = NULL; 101 con->pending.workspace->fullscreen = NULL;
95 } 102 }
96 if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { 103 if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
97 container_fullscreen_disable(con); 104 container_fullscreen_disable(con);
98 } 105 }
99 106
@@ -108,11 +115,11 @@ void container_begin_destroy(struct sway_container *con) {
108 root_scratchpad_remove_container(con); 115 root_scratchpad_remove_container(con);
109 } 116 }
110 117
111 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 118 if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
112 container_fullscreen_disable(con); 119 container_fullscreen_disable(con);
113 } 120 }
114 121
115 if (con->parent || con->workspace) { 122 if (con->pending.parent || con->pending.workspace) {
116 container_detach(con); 123 container_detach(con);
117 } 124 }
118} 125}
@@ -121,12 +128,12 @@ void container_reap_empty(struct sway_container *con) {
121 if (con->view) { 128 if (con->view) {
122 return; 129 return;
123 } 130 }
124 struct sway_workspace *ws = con->workspace; 131 struct sway_workspace *ws = con->pending.workspace;
125 while (con) { 132 while (con) {
126 if (con->children->length) { 133 if (con->pending.children->length) {
127 return; 134 return;
128 } 135 }
129 struct sway_container *parent = con->parent; 136 struct sway_container *parent = con->pending.parent;
130 container_begin_destroy(con); 137 container_begin_destroy(con);
131 con = parent; 138 con = parent;
132 } 139 }
@@ -139,9 +146,9 @@ struct sway_container *container_flatten(struct sway_container *container) {
139 if (container->view) { 146 if (container->view) {
140 return NULL; 147 return NULL;
141 } 148 }
142 while (container && container->children->length == 1) { 149 while (container && container->pending.children->length == 1) {
143 struct sway_container *child = container->children->items[0]; 150 struct sway_container *child = container->pending.children->items[0];
144 struct sway_container *parent = container->parent; 151 struct sway_container *parent = container->pending.parent;
145 container_replace(container, child); 152 container_replace(container, child);
146 container_begin_destroy(container); 153 container_begin_destroy(container);
147 container = parent; 154 container = parent;
@@ -151,11 +158,11 @@ struct sway_container *container_flatten(struct sway_container *container) {
151 158
152struct sway_container *container_find_child(struct sway_container *container, 159struct sway_container *container_find_child(struct sway_container *container,
153 bool (*test)(struct sway_container *con, void *data), void *data) { 160 bool (*test)(struct sway_container *con, void *data), void *data) {
154 if (!container->children) { 161 if (!container->pending.children) {
155 return NULL; 162 return NULL;
156 } 163 }
157 for (int i = 0; i < container->children->length; ++i) { 164 for (int i = 0; i < container->pending.children->length; ++i) {
158 struct sway_container *child = container->children->items[i]; 165 struct sway_container *child = container->pending.children->items[i];
159 if (test(child, data)) { 166 if (test(child, data)) {
160 return child; 167 return child;
161 } 168 }
@@ -310,7 +317,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
310 return NULL; 317 return NULL;
311} 318}
312 319
313struct sway_container *view_container_at(struct sway_node *parent, 320static struct sway_container *view_container_content_at(struct sway_node *parent,
314 double lx, double ly, 321 double lx, double ly,
315 struct wlr_surface **surface, double *sx, double *sy) { 322 struct wlr_surface **surface, double *sx, double *sy) {
316 if (!sway_assert(node_is_view(parent), "Expected a view")) { 323 if (!sway_assert(node_is_view(parent), "Expected a view")) {
@@ -319,10 +326,33 @@ struct sway_container *view_container_at(struct sway_node *parent,
319 326
320 struct sway_container *container = parent->sway_container; 327 struct sway_container *container = parent->sway_container;
321 struct wlr_box box = { 328 struct wlr_box box = {
322 .x = container->x, 329 .x = container->pending.content_x,
323 .y = container->y, 330 .y = container->pending.content_y,
324 .width = container->width, 331 .width = container->pending.content_width,
325 .height = container->height, 332 .height = container->pending.content_height,
333 };
334
335 if (wlr_box_contains_point(&box, lx, ly)) {
336 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
337 return container;
338 }
339
340 return NULL;
341}
342
343static struct sway_container *view_container_at(struct sway_node *parent,
344 double lx, double ly,
345 struct wlr_surface **surface, double *sx, double *sy) {
346 if (!sway_assert(node_is_view(parent), "Expected a view")) {
347 return NULL;
348 }
349
350 struct sway_container *container = parent->sway_container;
351 struct wlr_box box = {
352 .x = container->pending.x,
353 .y = container->pending.y,
354 .width = container->pending.width,
355 .height = container->pending.height,
326 }; 356 };
327 357
328 if (wlr_box_contains_point(&box, lx, ly)) { 358 if (wlr_box_contains_point(&box, lx, ly)) {
@@ -357,19 +387,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent,
357} 387}
358 388
359static bool surface_is_popup(struct wlr_surface *surface) { 389static bool surface_is_popup(struct wlr_surface *surface) {
360 if (wlr_surface_is_xdg_surface(surface)) { 390 while (!wlr_surface_is_xdg_surface(surface)) {
361 struct wlr_xdg_surface *xdg_surface = 391 if (!wlr_surface_is_subsurface(surface)) {
362 wlr_xdg_surface_from_wlr_surface(surface); 392 return false;
363 while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
364 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
365 return true;
366 }
367 xdg_surface = xdg_surface->toplevel->parent;
368 } 393 }
369 return false; 394 struct wlr_subsurface *subsurface =
395 wlr_subsurface_from_wlr_surface(surface);
396 surface = subsurface->parent;
370 } 397 }
371 398 struct wlr_xdg_surface *xdg_surface =
372 return false; 399 wlr_xdg_surface_from_wlr_surface(surface);
400 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
373} 401}
374 402
375struct sway_container *container_at(struct sway_workspace *workspace, 403struct sway_container *container_at(struct sway_workspace *workspace,
@@ -394,7 +422,7 @@ struct sway_container *container_at(struct sway_workspace *workspace,
394 } 422 }
395 // Tiling (focused) 423 // Tiling (focused)
396 if (focus && focus->view && !is_floating) { 424 if (focus && focus->view && !is_floating) {
397 if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { 425 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
398 return c; 426 return c;
399 } 427 }
400 } 428 }
@@ -408,19 +436,41 @@ struct sway_container *container_at(struct sway_workspace *workspace,
408void container_for_each_child(struct sway_container *container, 436void container_for_each_child(struct sway_container *container,
409 void (*f)(struct sway_container *container, void *data), 437 void (*f)(struct sway_container *container, void *data),
410 void *data) { 438 void *data) {
411 if (container->children) { 439 if (container->pending.children) {
412 for (int i = 0; i < container->children->length; ++i) { 440 for (int i = 0; i < container->pending.children->length; ++i) {
413 struct sway_container *child = container->children->items[i]; 441 struct sway_container *child = container->pending.children->items[i];
414 f(child, data); 442 f(child, data);
415 container_for_each_child(child, f, data); 443 container_for_each_child(child, f, data);
416 } 444 }
417 } 445 }
418} 446}
419 447
448struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container)
449{
450 struct sway_workspace *workspace = container->pending.workspace;
451
452 if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) {
453 if (container_is_transient_for(container, workspace->fullscreen)) {
454 return NULL;
455 }
456 return workspace->fullscreen;
457 }
458
459 struct sway_container *fullscreen_global = root->fullscreen_global;
460 if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) {
461 if (container_is_transient_for(container, fullscreen_global)) {
462 return NULL;
463 }
464 return fullscreen_global;
465 }
466
467 return NULL;
468}
469
420bool container_has_ancestor(struct sway_container *descendant, 470bool container_has_ancestor(struct sway_container *descendant,
421 struct sway_container *ancestor) { 471 struct sway_container *ancestor) {
422 while (descendant) { 472 while (descendant) {
423 descendant = descendant->parent; 473 descendant = descendant->pending.parent;
424 if (descendant == ancestor) { 474 if (descendant == ancestor) {
425 return true; 475 return true;
426 } 476 }
@@ -446,23 +496,13 @@ struct sway_output *container_get_effective_output(struct sway_container *con) {
446 return con->outputs->items[con->outputs->length - 1]; 496 return con->outputs->items[con->outputs->length - 1];
447} 497}
448 498
449static void update_title_texture(struct sway_container *con, 499static void render_titlebar_text_texture(struct sway_output *output,
450 struct wlr_texture **texture, struct border_colors *class) { 500 struct sway_container *con, struct wlr_texture **texture,
451 struct sway_output *output = container_get_effective_output(con); 501 struct border_colors *class, bool pango_markup, char *text) {
452 if (!output) {
453 return;
454 }
455 if (*texture) {
456 wlr_texture_destroy(*texture);
457 *texture = NULL;
458 }
459 if (!con->formatted_title) {
460 return;
461 }
462
463 double scale = output->wlr_output->scale; 502 double scale = output->wlr_output->scale;
464 int width = 0; 503 int width = 0;
465 int height = con->title_height * scale; 504 int height = config->font_height * scale;
505 int baseline;
466 506
467 // We must use a non-nil cairo_t for cairo_set_font_options to work. 507 // We must use a non-nil cairo_t for cairo_set_font_options to work.
468 // Therefore, we cannot use cairo_create(NULL). 508 // Therefore, we cannot use cairo_create(NULL).
@@ -480,13 +520,28 @@ static void update_title_texture(struct sway_container *con,
480 to_cairo_subpixel_order(output->wlr_output->subpixel)); 520 to_cairo_subpixel_order(output->wlr_output->subpixel));
481 } 521 }
482 cairo_set_font_options(c, fo); 522 cairo_set_font_options(c, fo);
483 get_text_size(c, config->font, &width, NULL, NULL, scale, 523 get_text_size(c, config->font, &width, NULL, &baseline, scale,
484 config->pango_markup, "%s", con->formatted_title); 524 config->pango_markup, "%s", text);
485 cairo_surface_destroy(dummy_surface); 525 cairo_surface_destroy(dummy_surface);
486 cairo_destroy(c); 526 cairo_destroy(c);
487 527
528 if (width == 0 || height == 0) {
529 return;
530 }
531
532 if (height > config->font_height * scale) {
533 height = config->font_height * scale;
534 }
535
488 cairo_surface_t *surface = cairo_image_surface_create( 536 cairo_surface_t *surface = cairo_image_surface_create(
489 CAIRO_FORMAT_ARGB32, width, height); 537 CAIRO_FORMAT_ARGB32, width, height);
538 cairo_status_t status = cairo_surface_status(surface);
539 if (status != CAIRO_STATUS_SUCCESS) {
540 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
541 cairo_status_to_string(status));
542 return;
543 }
544
490 cairo_t *cairo = cairo_create(surface); 545 cairo_t *cairo = cairo_create(surface);
491 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 546 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
492 cairo_set_font_options(cairo, fo); 547 cairo_set_font_options(cairo, fo);
@@ -497,23 +552,39 @@ static void update_title_texture(struct sway_container *con,
497 PangoContext *pango = pango_cairo_create_context(cairo); 552 PangoContext *pango = pango_cairo_create_context(cairo);
498 cairo_set_source_rgba(cairo, class->text[0], class->text[1], 553 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
499 class->text[2], class->text[3]); 554 class->text[2], class->text[3]);
500 cairo_move_to(cairo, 0, 0); 555 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
501 556
502 pango_printf(cairo, config->font, scale, config->pango_markup, 557 render_text(cairo, config->font, scale, pango_markup, "%s", text);
503 "%s", con->formatted_title);
504 558
505 cairo_surface_flush(surface); 559 cairo_surface_flush(surface);
506 unsigned char *data = cairo_image_surface_get_data(surface); 560 unsigned char *data = cairo_image_surface_get_data(surface);
507 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 561 int stride = cairo_image_surface_get_stride(surface);
508 struct wlr_renderer *renderer = wlr_backend_get_renderer( 562 struct wlr_renderer *renderer = output->wlr_output->renderer;
509 output->wlr_output->backend);
510 *texture = wlr_texture_from_pixels( 563 *texture = wlr_texture_from_pixels(
511 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 564 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
512 cairo_surface_destroy(surface); 565 cairo_surface_destroy(surface);
513 g_object_unref(pango); 566 g_object_unref(pango);
514 cairo_destroy(cairo); 567 cairo_destroy(cairo);
515} 568}
516 569
570static void update_title_texture(struct sway_container *con,
571 struct wlr_texture **texture, struct border_colors *class) {
572 struct sway_output *output = container_get_effective_output(con);
573 if (!output) {
574 return;
575 }
576 if (*texture) {
577 wlr_texture_destroy(*texture);
578 *texture = NULL;
579 }
580 if (!con->formatted_title) {
581 return;
582 }
583
584 render_titlebar_text_texture(output, con, texture, class,
585 config->pango_markup, con->formatted_title);
586}
587
517void container_update_title_textures(struct sway_container *container) { 588void container_update_title_textures(struct sway_container *container) {
518 update_title_texture(container, &container->title_focused, 589 update_title_texture(container, &container->title_focused,
519 &config->border_colors.focused); 590 &config->border_colors.focused);
@@ -523,24 +594,11 @@ void container_update_title_textures(struct sway_container *container) {
523 &config->border_colors.unfocused); 594 &config->border_colors.unfocused);
524 update_title_texture(container, &container->title_urgent, 595 update_title_texture(container, &container->title_urgent,
525 &config->border_colors.urgent); 596 &config->border_colors.urgent);
597 update_title_texture(container, &container->title_focused_tab_title,
598 &config->border_colors.focused_tab_title);
526 container_damage_whole(container); 599 container_damage_whole(container);
527} 600}
528 601
529void container_calculate_title_height(struct sway_container *container) {
530 if (!container->formatted_title) {
531 container->title_height = 0;
532 return;
533 }
534 cairo_t *cairo = cairo_create(NULL);
535 int height;
536 int baseline;
537 get_text_size(cairo, config->font, NULL, &height, &baseline, 1,
538 config->pango_markup, "%s", container->formatted_title);
539 cairo_destroy(cairo);
540 container->title_height = height;
541 container->title_baseline = baseline;
542}
543
544/** 602/**
545 * Calculate and return the length of the tree representation. 603 * Calculate and return the length of the tree representation.
546 * An example tree representation is: V[Terminal, Firefox] 604 * An example tree representation is: V[Terminal, Firefox]
@@ -596,23 +654,22 @@ size_t container_build_representation(enum sway_container_layout layout,
596 654
597void container_update_representation(struct sway_container *con) { 655void container_update_representation(struct sway_container *con) {
598 if (!con->view) { 656 if (!con->view) {
599 size_t len = container_build_representation(con->layout, 657 size_t len = container_build_representation(con->pending.layout,
600 con->children, NULL); 658 con->pending.children, NULL);
601 free(con->formatted_title); 659 free(con->formatted_title);
602 con->formatted_title = calloc(len + 1, sizeof(char)); 660 con->formatted_title = calloc(len + 1, sizeof(char));
603 if (!sway_assert(con->formatted_title, 661 if (!sway_assert(con->formatted_title,
604 "Unable to allocate title string")) { 662 "Unable to allocate title string")) {
605 return; 663 return;
606 } 664 }
607 container_build_representation(con->layout, con->children, 665 container_build_representation(con->pending.layout, con->pending.children,
608 con->formatted_title); 666 con->formatted_title);
609 container_calculate_title_height(con);
610 container_update_title_textures(con); 667 container_update_title_textures(con);
611 } 668 }
612 if (con->parent) { 669 if (con->pending.parent) {
613 container_update_representation(con->parent); 670 container_update_representation(con->pending.parent);
614 } else if (con->workspace) { 671 } else if (con->pending.workspace) {
615 workspace_update_representation(con->workspace); 672 workspace_update_representation(con->pending.workspace);
616 } 673 }
617} 674}
618 675
@@ -663,20 +720,20 @@ static void floating_natural_resize(struct sway_container *con) {
663 floating_calculate_constraints(&min_width, &max_width, 720 floating_calculate_constraints(&min_width, &max_width,
664 &min_height, &max_height); 721 &min_height, &max_height);
665 if (!con->view) { 722 if (!con->view) {
666 con->width = fmax(min_width, fmin(con->width, max_width)); 723 con->pending.width = fmax(min_width, fmin(con->pending.width, max_width));
667 con->height = fmax(min_height, fmin(con->height, max_height)); 724 con->pending.height = fmax(min_height, fmin(con->pending.height, max_height));
668 } else { 725 } else {
669 struct sway_view *view = con->view; 726 struct sway_view *view = con->view;
670 con->content_width = 727 con->pending.content_width =
671 fmax(min_width, fmin(view->natural_width, max_width)); 728 fmax(min_width, fmin(view->natural_width, max_width));
672 con->content_height = 729 con->pending.content_height =
673 fmax(min_height, fmin(view->natural_height, max_height)); 730 fmax(min_height, fmin(view->natural_height, max_height));
674 container_set_geometry_from_content(con); 731 container_set_geometry_from_content(con);
675 } 732 }
676} 733}
677 734
678void container_floating_resize_and_center(struct sway_container *con) { 735void container_floating_resize_and_center(struct sway_container *con) {
679 struct sway_workspace *ws = con->workspace; 736 struct sway_workspace *ws = con->pending.workspace;
680 if (!ws) { 737 if (!ws) {
681 // On scratchpad, just resize 738 // On scratchpad, just resize
682 floating_natural_resize(con); 739 floating_natural_resize(con);
@@ -687,42 +744,42 @@ void container_floating_resize_and_center(struct sway_container *con) {
687 ws->output->wlr_output); 744 ws->output->wlr_output);
688 if (!ob) { 745 if (!ob) {
689 // On NOOP output. Will be called again when moved to an output 746 // On NOOP output. Will be called again when moved to an output
690 con->x = 0; 747 con->pending.x = 0;
691 con->y = 0; 748 con->pending.y = 0;
692 con->width = 0; 749 con->pending.width = 0;
693 con->height = 0; 750 con->pending.height = 0;
694 return; 751 return;
695 } 752 }
696 753
697 floating_natural_resize(con); 754 floating_natural_resize(con);
698 if (!con->view) { 755 if (!con->view) {
699 if (con->width > ws->width || con->height > ws->height) { 756 if (con->pending.width > ws->width || con->pending.height > ws->height) {
700 con->x = ob->x + (ob->width - con->width) / 2; 757 con->pending.x = ob->x + (ob->width - con->pending.width) / 2;
701 con->y = ob->y + (ob->height - con->height) / 2; 758 con->pending.y = ob->y + (ob->height - con->pending.height) / 2;
702 } else { 759 } else {
703 con->x = ws->x + (ws->width - con->width) / 2; 760 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
704 con->y = ws->y + (ws->height - con->height) / 2; 761 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
705 } 762 }
706 } else { 763 } else {
707 if (con->content_width > ws->width 764 if (con->pending.content_width > ws->width
708 || con->content_height > ws->height) { 765 || con->pending.content_height > ws->height) {
709 con->content_x = ob->x + (ob->width - con->content_width) / 2; 766 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; 767 con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2;
711 } else { 768 } else {
712 con->content_x = ws->x + (ws->width - con->content_width) / 2; 769 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; 770 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
714 } 771 }
715 772
716 // If the view's border is B_NONE then these properties are ignored. 773 // If the view's border is B_NONE then these properties are ignored.
717 con->border_top = con->border_bottom = true; 774 con->pending.border_top = con->pending.border_bottom = true;
718 con->border_left = con->border_right = true; 775 con->pending.border_left = con->pending.border_right = true;
719 776
720 container_set_geometry_from_content(con); 777 container_set_geometry_from_content(con);
721 } 778 }
722} 779}
723 780
724void container_floating_set_default_size(struct sway_container *con) { 781void container_floating_set_default_size(struct sway_container *con) {
725 if (!sway_assert(con->workspace, "Expected a container on a workspace")) { 782 if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) {
726 return; 783 return;
727 } 784 }
728 785
@@ -730,16 +787,16 @@ void container_floating_set_default_size(struct sway_container *con) {
730 floating_calculate_constraints(&min_width, &max_width, 787 floating_calculate_constraints(&min_width, &max_width,
731 &min_height, &max_height); 788 &min_height, &max_height);
732 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 789 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
733 workspace_get_box(con->workspace, box); 790 workspace_get_box(con->pending.workspace, box);
734 791
735 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 792 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)); 793 double height = fmax(min_height, fmin(box->height * 0.75, max_height));
737 if (!con->view) { 794 if (!con->view) {
738 con->width = width; 795 con->pending.width = width;
739 con->height = height; 796 con->pending.height = height;
740 } else { 797 } else {
741 con->content_width = width; 798 con->pending.content_width = width;
742 con->content_height = height; 799 con->pending.content_height = height;
743 container_set_geometry_from_content(con); 800 container_set_geometry_from_content(con);
744 } 801 }
745 802
@@ -761,8 +818,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) {
761 con->view->impl->set_resizing(con->view, resizing); 818 con->view->impl->set_resizing(con->view, resizing);
762 } 819 }
763 } else { 820 } else {
764 for (int i = 0; i < con->children->length; ++i ) { 821 for (int i = 0; i < con->pending.children->length; ++i ) {
765 struct sway_container *child = con->children->items[i]; 822 struct sway_container *child = con->pending.children->items[i];
766 container_set_resizing(child, resizing); 823 container_set_resizing(child, resizing);
767 } 824 }
768 } 825 }
@@ -774,21 +831,33 @@ void container_set_floating(struct sway_container *container, bool enable) {
774 } 831 }
775 832
776 struct sway_seat *seat = input_manager_current_seat(); 833 struct sway_seat *seat = input_manager_current_seat();
777 struct sway_workspace *workspace = container->workspace; 834 struct sway_workspace *workspace = container->pending.workspace;
835 struct sway_container *focus = seat_get_focused_container(seat);
836 bool set_focus = focus == container;
778 837
779 if (enable) { 838 if (enable) {
780 struct sway_container *old_parent = container->parent; 839 struct sway_container *old_parent = container->pending.parent;
781 container_detach(container); 840 container_detach(container);
782 workspace_add_floating(workspace, container); 841 workspace_add_floating(workspace, container);
783 if (container->view) { 842 if (container->view) {
784 view_set_tiled(container->view, false); 843 view_set_tiled(container->view, false);
785 if (container->view->using_csd) { 844 if (container->view->using_csd) {
786 container->border = B_CSD; 845 container->saved_border = container->pending.border;
846 container->pending.border = B_CSD;
847 if (container->view->xdg_decoration) {
848 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
849 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
850 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
851 }
787 } 852 }
788 } 853 }
789 container_floating_set_default_size(container); 854 container_floating_set_default_size(container);
790 container_floating_resize_and_center(container); 855 container_floating_resize_and_center(container);
791 if (old_parent) { 856 if (old_parent) {
857 if (set_focus) {
858 seat_set_raw_focus(seat, &old_parent->node);
859 seat_set_raw_focus(seat, &container->node);
860 }
792 container_reap_empty(old_parent); 861 container_reap_empty(old_parent);
793 } 862 }
794 } else { 863 } else {
@@ -800,19 +869,28 @@ void container_set_floating(struct sway_container *container, bool enable) {
800 struct sway_container *reference = 869 struct sway_container *reference =
801 seat_get_focus_inactive_tiling(seat, workspace); 870 seat_get_focus_inactive_tiling(seat, workspace);
802 if (reference) { 871 if (reference) {
803 container_add_sibling(reference, container, 1); 872 if (reference->view) {
804 container->width = reference->width; 873 container_add_sibling(reference, container, 1);
805 container->height = reference->height; 874 } else {
875 container_add_child(reference, container);
876 }
877 container->pending.width = reference->pending.width;
878 container->pending.height = reference->pending.height;
806 } else { 879 } else {
807 struct sway_container *other = 880 struct sway_container *other =
808 workspace_add_tiling(workspace, container); 881 workspace_add_tiling(workspace, container);
809 other->width = workspace->width; 882 other->pending.width = workspace->width;
810 other->height = workspace->height; 883 other->pending.height = workspace->height;
811 } 884 }
812 if (container->view) { 885 if (container->view) {
813 view_set_tiled(container->view, true); 886 view_set_tiled(container->view, true);
814 if (container->view->using_csd) { 887 if (container->view->using_csd) {
815 container->border = container->saved_border; 888 container->pending.border = container->saved_border;
889 if (container->view->xdg_decoration) {
890 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
891 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
892 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
893 }
816 } 894 }
817 } 895 }
818 container->width_fraction = 0; 896 container->width_fraction = 0;
@@ -834,22 +912,33 @@ void container_set_geometry_from_content(struct sway_container *con) {
834 size_t border_width = 0; 912 size_t border_width = 0;
835 size_t top = 0; 913 size_t top = 0;
836 914
837 if (con->border != B_CSD) { 915 if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) {
838 border_width = con->border_thickness * (con->border != B_NONE); 916 border_width = con->pending.border_thickness * (con->pending.border != B_NONE);
839 top = con->border == B_NORMAL ? 917 top = con->pending.border == B_NORMAL ?
840 container_titlebar_height() : border_width; 918 container_titlebar_height() : border_width;
841 } 919 }
842 920
843 con->x = con->content_x - border_width; 921 con->pending.x = con->pending.content_x - border_width;
844 con->y = con->content_y - top; 922 con->pending.y = con->pending.content_y - top;
845 con->width = con->content_width + border_width * 2; 923 con->pending.width = con->pending.content_width + border_width * 2;
846 con->height = top + con->content_height + border_width; 924 con->pending.height = top + con->pending.content_height + border_width;
847 node_set_dirty(&con->node); 925 node_set_dirty(&con->node);
848} 926}
849 927
850bool container_is_floating(struct sway_container *container) { 928bool container_is_floating(struct sway_container *container) {
851 if (!container->parent && container->workspace && 929 if (!container->pending.parent && container->pending.workspace &&
852 list_find(container->workspace->floating, container) != -1) { 930 list_find(container->pending.workspace->floating, container) != -1) {
931 return true;
932 }
933 if (container->scratchpad) {
934 return true;
935 }
936 return false;
937}
938
939bool container_is_current_floating(struct sway_container *container) {
940 if (!container->current.parent && container->current.workspace &&
941 list_find(container->current.workspace->floating, container) != -1) {
853 return true; 942 return true;
854 } 943 }
855 if (container->scratchpad) { 944 if (container->scratchpad) {
@@ -859,10 +948,10 @@ bool container_is_floating(struct sway_container *container) {
859} 948}
860 949
861void container_get_box(struct sway_container *container, struct wlr_box *box) { 950void container_get_box(struct sway_container *container, struct wlr_box *box) {
862 box->x = container->x; 951 box->x = container->pending.x;
863 box->y = container->y; 952 box->y = container->pending.y;
864 box->width = container->width; 953 box->width = container->pending.width;
865 box->height = container->height; 954 box->height = container->pending.height;
866} 955}
867 956
868/** 957/**
@@ -870,14 +959,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
870 */ 959 */
871void container_floating_translate(struct sway_container *con, 960void container_floating_translate(struct sway_container *con,
872 double x_amount, double y_amount) { 961 double x_amount, double y_amount) {
873 con->x += x_amount; 962 con->pending.x += x_amount;
874 con->y += y_amount; 963 con->pending.y += y_amount;
875 con->content_x += x_amount; 964 con->pending.content_x += x_amount;
876 con->content_y += y_amount; 965 con->pending.content_y += y_amount;
877 966
878 if (con->children) { 967 if (con->pending.children) {
879 for (int i = 0; i < con->children->length; ++i) { 968 for (int i = 0; i < con->pending.children->length; ++i) {
880 struct sway_container *child = con->children->items[i]; 969 struct sway_container *child = con->pending.children->items[i];
881 container_floating_translate(child, x_amount, y_amount); 970 container_floating_translate(child, x_amount, y_amount);
882 } 971 }
883 } 972 }
@@ -893,8 +982,8 @@ void container_floating_translate(struct sway_container *con,
893 * center. 982 * center.
894 */ 983 */
895struct sway_output *container_floating_find_output(struct sway_container *con) { 984struct sway_output *container_floating_find_output(struct sway_container *con) {
896 double center_x = con->x + con->width / 2; 985 double center_x = con->pending.x + con->pending.width / 2;
897 double center_y = con->y + con->height / 2; 986 double center_y = con->pending.y + con->pending.height / 2;
898 struct sway_output *closest_output = NULL; 987 struct sway_output *closest_output = NULL;
899 double closest_distance = DBL_MAX; 988 double closest_distance = DBL_MAX;
900 for (int i = 0; i < root->outputs->length; ++i) { 989 for (int i = 0; i < root->outputs->length; ++i) {
@@ -925,11 +1014,11 @@ void container_floating_move_to(struct sway_container *con,
925 "Expected a floating container")) { 1014 "Expected a floating container")) {
926 return; 1015 return;
927 } 1016 }
928 container_floating_translate(con, lx - con->x, ly - con->y); 1017 container_floating_translate(con, lx - con->pending.x, ly - con->pending.y);
929 if (container_is_scratchpad_hidden(con)) { 1018 if (container_is_scratchpad_hidden(con)) {
930 return; 1019 return;
931 } 1020 }
932 struct sway_workspace *old_workspace = con->workspace; 1021 struct sway_workspace *old_workspace = con->pending.workspace;
933 struct sway_output *new_output = container_floating_find_output(con); 1022 struct sway_output *new_output = container_floating_find_output(con);
934 if (!sway_assert(new_output, "Unable to find any output")) { 1023 if (!sway_assert(new_output, "Unable to find any output")) {
935 return; 1024 return;
@@ -951,10 +1040,10 @@ void container_floating_move_to_center(struct sway_container *con) {
951 "Expected a floating container")) { 1040 "Expected a floating container")) {
952 return; 1041 return;
953 } 1042 }
954 struct sway_workspace *ws = con->workspace; 1043 struct sway_workspace *ws = con->pending.workspace;
955 double new_lx = ws->x + (ws->width - con->width) / 2; 1044 double new_lx = ws->x + (ws->width - con->pending.width) / 2;
956 double new_ly = ws->y + (ws->height - con->height) / 2; 1045 double new_ly = ws->y + (ws->height - con->pending.height) / 2;
957 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 1046 container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y);
958} 1047}
959 1048
960static bool find_urgent_iterator(struct sway_container *con, void *data) { 1049static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -972,42 +1061,118 @@ void container_end_mouse_operation(struct sway_container *container) {
972 } 1061 }
973} 1062}
974 1063
975static void set_fullscreen_iterator(struct sway_container *con, void *data) { 1064static bool devid_from_fd(int fd, dev_t *devid) {
1065 struct stat stat;
1066 if (fstat(fd, &stat) != 0) {
1067 sway_log_errno(SWAY_ERROR, "fstat failed");
1068 return false;
1069 }
1070 *devid = stat.st_rdev;
1071 return true;
1072}
1073
1074static void set_fullscreen(struct sway_container *con, bool enable) {
976 if (!con->view) { 1075 if (!con->view) {
977 return; 1076 return;
978 } 1077 }
979 if (con->view->impl->set_fullscreen) { 1078 if (con->view->impl->set_fullscreen) {
980 bool *enable = data; 1079 con->view->impl->set_fullscreen(con->view, enable);
981 con->view->impl->set_fullscreen(con->view, *enable);
982 if (con->view->foreign_toplevel) { 1080 if (con->view->foreign_toplevel) {
983 wlr_foreign_toplevel_handle_v1_set_fullscreen( 1081 wlr_foreign_toplevel_handle_v1_set_fullscreen(
984 con->view->foreign_toplevel, *enable); 1082 con->view->foreign_toplevel, enable);
985 } 1083 }
986 } 1084 }
1085
1086 if (!server.linux_dmabuf_v1 || !con->view->surface) {
1087 return;
1088 }
1089 if (!enable) {
1090 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1091 con->view->surface, NULL);
1092 return;
1093 }
1094
1095 if (!con->pending.workspace || !con->pending.workspace->output) {
1096 return;
1097 }
1098
1099 struct sway_output *output = con->pending.workspace->output;
1100 struct wlr_output *wlr_output = output->wlr_output;
1101
1102 // TODO: add wlroots helpers for all of this stuff
1103
1104 const struct wlr_drm_format_set *renderer_formats =
1105 wlr_renderer_get_dmabuf_texture_formats(server.renderer);
1106 assert(renderer_formats);
1107
1108 int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
1109 int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
1110 if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
1111 return;
1112 }
1113
1114 dev_t render_dev, scanout_dev;
1115 if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
1116 !devid_from_fd(backend_drm_fd, &scanout_dev)) {
1117 return;
1118 }
1119
1120 const struct wlr_drm_format_set *output_formats =
1121 wlr_output_get_primary_formats(output->wlr_output,
1122 WLR_BUFFER_CAP_DMABUF);
1123 if (!output_formats) {
1124 return;
1125 }
1126
1127 struct wlr_drm_format_set scanout_formats = {0};
1128 if (!wlr_drm_format_set_intersect(&scanout_formats,
1129 output_formats, renderer_formats)) {
1130 return;
1131 }
1132
1133 struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
1134 {
1135 .target_device = scanout_dev,
1136 .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
1137 .formats = &scanout_formats,
1138 },
1139 {
1140 .target_device = render_dev,
1141 .formats = renderer_formats,
1142 },
1143 };
1144
1145 const struct wlr_linux_dmabuf_feedback_v1 feedback = {
1146 .main_device = render_dev,
1147 .tranches = tranches,
1148 .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
1149 };
1150 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1151 con->view->surface, &feedback);
1152
1153 wlr_drm_format_set_finish(&scanout_formats);
987} 1154}
988 1155
989static void container_fullscreen_workspace(struct sway_container *con) { 1156static void container_fullscreen_workspace(struct sway_container *con) {
990 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1157 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
991 "Expected a non-fullscreen container")) { 1158 "Expected a non-fullscreen container")) {
992 return; 1159 return;
993 } 1160 }
994 bool enable = true; 1161 set_fullscreen(con, true);
995 set_fullscreen_iterator(con, &enable); 1162 con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;
996 container_for_each_child(con, set_fullscreen_iterator, &enable);
997 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
998 1163
999 con->saved_x = con->x; 1164 con->saved_x = con->pending.x;
1000 con->saved_y = con->y; 1165 con->saved_y = con->pending.y;
1001 con->saved_width = con->width; 1166 con->saved_width = con->pending.width;
1002 con->saved_height = con->height; 1167 con->saved_height = con->pending.height;
1003 1168
1004 if (con->workspace) { 1169 if (con->pending.workspace) {
1005 con->workspace->fullscreen = con; 1170 con->pending.workspace->fullscreen = con;
1006 struct sway_seat *seat; 1171 struct sway_seat *seat;
1007 struct sway_workspace *focus_ws; 1172 struct sway_workspace *focus_ws;
1008 wl_list_for_each(seat, &server.input->seats, link) { 1173 wl_list_for_each(seat, &server.input->seats, link) {
1009 focus_ws = seat_get_focused_workspace(seat); 1174 focus_ws = seat_get_focused_workspace(seat);
1010 if (focus_ws == con->workspace) { 1175 if (focus_ws == con->pending.workspace) {
1011 seat_set_focus_container(seat, con); 1176 seat_set_focus_container(seat, con);
1012 } else { 1177 } else {
1013 struct sway_node *focus = 1178 struct sway_node *focus =
@@ -1023,19 +1188,17 @@ static void container_fullscreen_workspace(struct sway_container *con) {
1023} 1188}
1024 1189
1025static void container_fullscreen_global(struct sway_container *con) { 1190static void container_fullscreen_global(struct sway_container *con) {
1026 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1191 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
1027 "Expected a non-fullscreen container")) { 1192 "Expected a non-fullscreen container")) {
1028 return; 1193 return;
1029 } 1194 }
1030 bool enable = true; 1195 set_fullscreen(con, true);
1031 set_fullscreen_iterator(con, &enable);
1032 container_for_each_child(con, set_fullscreen_iterator, &enable);
1033 1196
1034 root->fullscreen_global = con; 1197 root->fullscreen_global = con;
1035 con->saved_x = con->x; 1198 con->saved_x = con->pending.x;
1036 con->saved_y = con->y; 1199 con->saved_y = con->pending.y;
1037 con->saved_width = con->width; 1200 con->saved_width = con->pending.width;
1038 con->saved_height = con->height; 1201 con->saved_height = con->pending.height;
1039 1202
1040 struct sway_seat *seat; 1203 struct sway_seat *seat;
1041 wl_list_for_each(seat, &server.input->seats, link) { 1204 wl_list_for_each(seat, &server.input->seats, link) {
@@ -1045,34 +1208,32 @@ static void container_fullscreen_global(struct sway_container *con) {
1045 } 1208 }
1046 } 1209 }
1047 1210
1048 con->fullscreen_mode = FULLSCREEN_GLOBAL; 1211 con->pending.fullscreen_mode = FULLSCREEN_GLOBAL;
1049 container_end_mouse_operation(con); 1212 container_end_mouse_operation(con);
1050 ipc_event_window(con, "fullscreen_mode"); 1213 ipc_event_window(con, "fullscreen_mode");
1051} 1214}
1052 1215
1053void container_fullscreen_disable(struct sway_container *con) { 1216void container_fullscreen_disable(struct sway_container *con) {
1054 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, 1217 if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,
1055 "Expected a fullscreen container")) { 1218 "Expected a fullscreen container")) {
1056 return; 1219 return;
1057 } 1220 }
1058 bool enable = false; 1221 set_fullscreen(con, false);
1059 set_fullscreen_iterator(con, &enable);
1060 container_for_each_child(con, set_fullscreen_iterator, &enable);
1061 1222
1062 if (container_is_floating(con)) { 1223 if (container_is_floating(con)) {
1063 con->x = con->saved_x; 1224 con->pending.x = con->saved_x;
1064 con->y = con->saved_y; 1225 con->pending.y = con->saved_y;
1065 con->width = con->saved_width; 1226 con->pending.width = con->saved_width;
1066 con->height = con->saved_height; 1227 con->pending.height = con->saved_height;
1067 } 1228 }
1068 1229
1069 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1230 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1070 if (con->workspace) { 1231 if (con->pending.workspace) {
1071 con->workspace->fullscreen = NULL; 1232 con->pending.workspace->fullscreen = NULL;
1072 if (container_is_floating(con)) { 1233 if (container_is_floating(con)) {
1073 struct sway_output *output = 1234 struct sway_output *output =
1074 container_floating_find_output(con); 1235 container_floating_find_output(con);
1075 if (con->workspace->output != output) { 1236 if (con->pending.workspace->output != output) {
1076 container_floating_move_to_center(con); 1237 container_floating_move_to_center(con);
1077 } 1238 }
1078 } 1239 }
@@ -1084,11 +1245,11 @@ void container_fullscreen_disable(struct sway_container *con) {
1084 // If the container was mapped as fullscreen and set as floating by 1245 // 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 1246 // criteria, it needs to be reinitialized as floating to get the proper
1086 // size and location 1247 // size and location
1087 if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { 1248 if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {
1088 container_floating_resize_and_center(con); 1249 container_floating_resize_and_center(con);
1089 } 1250 }
1090 1251
1091 con->fullscreen_mode = FULLSCREEN_NONE; 1252 con->pending.fullscreen_mode = FULLSCREEN_NONE;
1092 container_end_mouse_operation(con); 1253 container_end_mouse_operation(con);
1093 ipc_event_window(con, "fullscreen_mode"); 1254 ipc_event_window(con, "fullscreen_mode");
1094 1255
@@ -1106,7 +1267,7 @@ void container_fullscreen_disable(struct sway_container *con) {
1106 1267
1107void container_set_fullscreen(struct sway_container *con, 1268void container_set_fullscreen(struct sway_container *con,
1108 enum sway_fullscreen_mode mode) { 1269 enum sway_fullscreen_mode mode) {
1109 if (con->fullscreen_mode == mode) { 1270 if (con->pending.fullscreen_mode == mode) {
1110 return; 1271 return;
1111 } 1272 }
1112 1273
@@ -1118,8 +1279,8 @@ void container_set_fullscreen(struct sway_container *con,
1118 if (root->fullscreen_global) { 1279 if (root->fullscreen_global) {
1119 container_fullscreen_disable(root->fullscreen_global); 1280 container_fullscreen_disable(root->fullscreen_global);
1120 } 1281 }
1121 if (con->workspace && con->workspace->fullscreen) { 1282 if (con->pending.workspace && con->pending.workspace->fullscreen) {
1122 container_fullscreen_disable(con->workspace->fullscreen); 1283 container_fullscreen_disable(con->pending.workspace->fullscreen);
1123 } 1284 }
1124 container_fullscreen_workspace(con); 1285 container_fullscreen_workspace(con);
1125 break; 1286 break;
@@ -1127,7 +1288,7 @@ void container_set_fullscreen(struct sway_container *con,
1127 if (root->fullscreen_global) { 1288 if (root->fullscreen_global) {
1128 container_fullscreen_disable(root->fullscreen_global); 1289 container_fullscreen_disable(root->fullscreen_global);
1129 } 1290 }
1130 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1291 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1131 container_fullscreen_disable(con); 1292 container_fullscreen_disable(con);
1132 } 1293 }
1133 container_fullscreen_global(con); 1294 container_fullscreen_global(con);
@@ -1137,8 +1298,8 @@ void container_set_fullscreen(struct sway_container *con,
1137 1298
1138struct sway_container *container_toplevel_ancestor( 1299struct sway_container *container_toplevel_ancestor(
1139 struct sway_container *container) { 1300 struct sway_container *container) {
1140 while (container->parent) { 1301 while (container->pending.parent) {
1141 container = container->parent; 1302 container = container->pending.parent;
1142 } 1303 }
1143 1304
1144 return container; 1305 return container;
@@ -1150,10 +1311,10 @@ bool container_is_floating_or_child(struct sway_container *container) {
1150 1311
1151bool container_is_fullscreen_or_child(struct sway_container *container) { 1312bool container_is_fullscreen_or_child(struct sway_container *container) {
1152 do { 1313 do {
1153 if (container->fullscreen_mode) { 1314 if (container->pending.fullscreen_mode) {
1154 return true; 1315 return true;
1155 } 1316 }
1156 container = container->parent; 1317 container = container->pending.parent;
1157 } while (container); 1318 } while (container);
1158 1319
1159 return false; 1320 return false;
@@ -1226,11 +1387,11 @@ void container_discover_outputs(struct sway_container *con) {
1226} 1387}
1227 1388
1228enum sway_container_layout container_parent_layout(struct sway_container *con) { 1389enum sway_container_layout container_parent_layout(struct sway_container *con) {
1229 if (con->parent) { 1390 if (con->pending.parent) {
1230 return con->parent->layout; 1391 return con->pending.parent->pending.layout;
1231 } 1392 }
1232 if (con->workspace) { 1393 if (con->pending.workspace) {
1233 return con->workspace->layout; 1394 return con->pending.workspace->layout;
1234 } 1395 }
1235 return L_NONE; 1396 return L_NONE;
1236} 1397}
@@ -1244,16 +1405,16 @@ enum sway_container_layout container_current_parent_layout(
1244} 1405}
1245 1406
1246list_t *container_get_siblings(struct sway_container *container) { 1407list_t *container_get_siblings(struct sway_container *container) {
1247 if (container->parent) { 1408 if (container->pending.parent) {
1248 return container->parent->children; 1409 return container->pending.parent->pending.children;
1249 } 1410 }
1250 if (container_is_scratchpad_hidden(container)) { 1411 if (container_is_scratchpad_hidden(container)) {
1251 return NULL; 1412 return NULL;
1252 } 1413 }
1253 if (list_find(container->workspace->tiling, container) != -1) { 1414 if (list_find(container->pending.workspace->tiling, container) != -1) {
1254 return container->workspace->tiling; 1415 return container->pending.workspace->tiling;
1255 } 1416 }
1256 return container->workspace->floating; 1417 return container->pending.workspace->floating;
1257} 1418}
1258 1419
1259int container_sibling_index(struct sway_container *child) { 1420int container_sibling_index(struct sway_container *child) {
@@ -1268,30 +1429,30 @@ list_t *container_get_current_siblings(struct sway_container *container) {
1268} 1429}
1269 1430
1270void container_handle_fullscreen_reparent(struct sway_container *con) { 1431void container_handle_fullscreen_reparent(struct sway_container *con) {
1271 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || 1432 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1272 con->workspace->fullscreen == con) { 1433 con->pending.workspace->fullscreen == con) {
1273 return; 1434 return;
1274 } 1435 }
1275 if (con->workspace->fullscreen) { 1436 if (con->pending.workspace->fullscreen) {
1276 container_fullscreen_disable(con->workspace->fullscreen); 1437 container_fullscreen_disable(con->pending.workspace->fullscreen);
1277 } 1438 }
1278 con->workspace->fullscreen = con; 1439 con->pending.workspace->fullscreen = con;
1279 1440
1280 arrange_workspace(con->workspace); 1441 arrange_workspace(con->pending.workspace);
1281} 1442}
1282 1443
1283static void set_workspace(struct sway_container *container, void *data) { 1444static void set_workspace(struct sway_container *container, void *data) {
1284 container->workspace = container->parent->workspace; 1445 container->pending.workspace = container->pending.parent->pending.workspace;
1285} 1446}
1286 1447
1287void container_insert_child(struct sway_container *parent, 1448void container_insert_child(struct sway_container *parent,
1288 struct sway_container *child, int i) { 1449 struct sway_container *child, int i) {
1289 if (child->workspace) { 1450 if (child->pending.workspace) {
1290 container_detach(child); 1451 container_detach(child);
1291 } 1452 }
1292 list_insert(parent->children, i, child); 1453 list_insert(parent->pending.children, i, child);
1293 child->parent = parent; 1454 child->pending.parent = parent;
1294 child->workspace = parent->workspace; 1455 child->pending.workspace = parent->pending.workspace;
1295 container_for_each_child(child, set_workspace, NULL); 1456 container_for_each_child(child, set_workspace, NULL);
1296 container_handle_fullscreen_reparent(child); 1457 container_handle_fullscreen_reparent(child);
1297 container_update_representation(parent); 1458 container_update_representation(parent);
@@ -1299,14 +1460,14 @@ void container_insert_child(struct sway_container *parent,
1299 1460
1300void container_add_sibling(struct sway_container *fixed, 1461void container_add_sibling(struct sway_container *fixed,
1301 struct sway_container *active, bool after) { 1462 struct sway_container *active, bool after) {
1302 if (active->workspace) { 1463 if (active->pending.workspace) {
1303 container_detach(active); 1464 container_detach(active);
1304 } 1465 }
1305 list_t *siblings = container_get_siblings(fixed); 1466 list_t *siblings = container_get_siblings(fixed);
1306 int index = list_find(siblings, fixed); 1467 int index = list_find(siblings, fixed);
1307 list_insert(siblings, index + after, active); 1468 list_insert(siblings, index + after, active);
1308 active->parent = fixed->parent; 1469 active->pending.parent = fixed->pending.parent;
1309 active->workspace = fixed->workspace; 1470 active->pending.workspace = fixed->pending.workspace;
1310 container_for_each_child(active, set_workspace, NULL); 1471 container_for_each_child(active, set_workspace, NULL);
1311 container_handle_fullscreen_reparent(active); 1472 container_handle_fullscreen_reparent(active);
1312 container_update_representation(active); 1473 container_update_representation(active);
@@ -1314,17 +1475,13 @@ void container_add_sibling(struct sway_container *fixed,
1314 1475
1315void container_add_child(struct sway_container *parent, 1476void container_add_child(struct sway_container *parent,
1316 struct sway_container *child) { 1477 struct sway_container *child) {
1317 if (child->workspace) { 1478 if (child->pending.workspace) {
1318 container_detach(child); 1479 container_detach(child);
1319 } 1480 }
1320 list_add(parent->children, child); 1481 list_add(parent->pending.children, child);
1321 child->parent = parent; 1482 child->pending.parent = parent;
1322 child->workspace = parent->workspace; 1483 child->pending.workspace = parent->pending.workspace;
1323 container_for_each_child(child, set_workspace, NULL); 1484 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); 1485 container_handle_fullscreen_reparent(child);
1329 container_update_representation(parent); 1486 container_update_representation(parent);
1330 node_set_dirty(&child->node); 1487 node_set_dirty(&child->node);
@@ -1332,15 +1489,15 @@ void container_add_child(struct sway_container *parent,
1332} 1489}
1333 1490
1334void container_detach(struct sway_container *child) { 1491void container_detach(struct sway_container *child) {
1335 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1492 if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1336 child->workspace->fullscreen = NULL; 1493 child->pending.workspace->fullscreen = NULL;
1337 } 1494 }
1338 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { 1495 if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1339 root->fullscreen_global = NULL; 1496 root->fullscreen_global = NULL;
1340 } 1497 }
1341 1498
1342 struct sway_container *old_parent = child->parent; 1499 struct sway_container *old_parent = child->pending.parent;
1343 struct sway_workspace *old_workspace = child->workspace; 1500 struct sway_workspace *old_workspace = child->pending.workspace;
1344 list_t *siblings = container_get_siblings(child); 1501 list_t *siblings = container_get_siblings(child);
1345 if (siblings) { 1502 if (siblings) {
1346 int index = list_find(siblings, child); 1503 int index = list_find(siblings, child);
@@ -1348,8 +1505,8 @@ void container_detach(struct sway_container *child) {
1348 list_del(siblings, index); 1505 list_del(siblings, index);
1349 } 1506 }
1350 } 1507 }
1351 child->parent = NULL; 1508 child->pending.parent = NULL;
1352 child->workspace = NULL; 1509 child->pending.workspace = NULL;
1353 container_for_each_child(child, set_workspace, NULL); 1510 container_for_each_child(child, set_workspace, NULL);
1354 1511
1355 if (old_parent) { 1512 if (old_parent) {
@@ -1364,18 +1521,18 @@ void container_detach(struct sway_container *child) {
1364 1521
1365void container_replace(struct sway_container *container, 1522void container_replace(struct sway_container *container,
1366 struct sway_container *replacement) { 1523 struct sway_container *replacement) {
1367 enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; 1524 enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode;
1368 bool scratchpad = container->scratchpad; 1525 bool scratchpad = container->scratchpad;
1369 struct sway_workspace *ws = NULL; 1526 struct sway_workspace *ws = NULL;
1370 if (fullscreen != FULLSCREEN_NONE) { 1527 if (fullscreen != FULLSCREEN_NONE) {
1371 container_fullscreen_disable(container); 1528 container_fullscreen_disable(container);
1372 } 1529 }
1373 if (scratchpad) { 1530 if (scratchpad) {
1374 ws = container->workspace; 1531 ws = container->pending.workspace;
1375 root_scratchpad_show(container); 1532 root_scratchpad_show(container);
1376 root_scratchpad_remove_container(container); 1533 root_scratchpad_remove_container(container);
1377 } 1534 }
1378 if (container->parent || container->workspace) { 1535 if (container->pending.parent || container->pending.workspace) {
1379 float width_fraction = container->width_fraction; 1536 float width_fraction = container->width_fraction;
1380 float height_fraction = container->height_fraction; 1537 float height_fraction = container->height_fraction;
1381 container_add_sibling(container, replacement, 1); 1538 container_add_sibling(container, replacement, 1);
@@ -1403,7 +1560,7 @@ struct sway_container *container_split(struct sway_container *child,
1403 enum sway_container_layout layout) { 1560 enum sway_container_layout layout) {
1404 // i3 doesn't split singleton H/V containers 1561 // i3 doesn't split singleton H/V containers
1405 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 1562 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354
1406 if (child->parent || child->workspace) { 1563 if (child->pending.parent || child->pending.workspace) {
1407 list_t *siblings = container_get_siblings(child); 1564 list_t *siblings = container_get_siblings(child);
1408 if (siblings->length == 1) { 1565 if (siblings->length == 1) {
1409 enum sway_container_layout current = container_parent_layout(child); 1566 enum sway_container_layout current = container_parent_layout(child);
@@ -1411,12 +1568,12 @@ struct sway_container *container_split(struct sway_container *child,
1411 current = L_NONE; 1568 current = L_NONE;
1412 } 1569 }
1413 if (current == L_HORIZ || current == L_VERT) { 1570 if (current == L_HORIZ || current == L_VERT) {
1414 if (child->parent) { 1571 if (child->pending.parent) {
1415 child->parent->layout = layout; 1572 child->pending.parent->pending.layout = layout;
1416 container_update_representation(child->parent); 1573 container_update_representation(child->pending.parent);
1417 } else { 1574 } else {
1418 child->workspace->layout = layout; 1575 child->pending.workspace->layout = layout;
1419 workspace_update_representation(child->workspace); 1576 workspace_update_representation(child->pending.workspace);
1420 } 1577 }
1421 return child; 1578 return child;
1422 } 1579 }
@@ -1429,25 +1586,25 @@ struct sway_container *container_split(struct sway_container *child,
1429 if (container_is_floating(child) && child->view) { 1586 if (container_is_floating(child) && child->view) {
1430 view_set_tiled(child->view, true); 1587 view_set_tiled(child->view, true);
1431 if (child->view->using_csd) { 1588 if (child->view->using_csd) {
1432 child->border = child->saved_border; 1589 child->pending.border = child->saved_border;
1433 } 1590 }
1434 } 1591 }
1435 1592
1436 struct sway_container *cont = container_create(NULL); 1593 struct sway_container *cont = container_create(NULL);
1437 cont->width = child->width; 1594 cont->pending.width = child->pending.width;
1438 cont->height = child->height; 1595 cont->pending.height = child->pending.height;
1439 cont->width_fraction = child->width_fraction; 1596 cont->width_fraction = child->width_fraction;
1440 cont->height_fraction = child->height_fraction; 1597 cont->height_fraction = child->height_fraction;
1441 cont->x = child->x; 1598 cont->pending.x = child->pending.x;
1442 cont->y = child->y; 1599 cont->pending.y = child->pending.y;
1443 cont->layout = layout; 1600 cont->pending.layout = layout;
1444 1601
1445 container_replace(child, cont); 1602 container_replace(child, cont);
1446 container_add_child(cont, child); 1603 container_add_child(cont, child);
1447 1604
1448 if (set_focus) { 1605 if (set_focus) {
1449 seat_set_raw_focus(seat, &cont->node); 1606 seat_set_raw_focus(seat, &cont->node);
1450 if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { 1607 if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1451 seat_set_focus(seat, &child->node); 1608 seat_set_focus(seat, &child->node);
1452 } else { 1609 } else {
1453 seat_set_raw_focus(seat, &child->node); 1610 seat_set_raw_focus(seat, &child->node);
@@ -1554,39 +1711,8 @@ static void update_marks_texture(struct sway_container *con,
1554 } 1711 }
1555 free(part); 1712 free(part);
1556 1713
1557 double scale = output->wlr_output->scale; 1714 render_titlebar_text_texture(output, con, texture, class, false, buffer);
1558 int width = 0;
1559 int height = con->title_height * scale;
1560
1561 cairo_t *c = cairo_create(NULL);
1562 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1563 "%s", buffer);
1564 cairo_destroy(c);
1565
1566 cairo_surface_t *surface = cairo_image_surface_create(
1567 CAIRO_FORMAT_ARGB32, width, height);
1568 cairo_t *cairo = cairo_create(surface);
1569 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1570 class->background[2], class->background[3]);
1571 cairo_paint(cairo);
1572 PangoContext *pango = pango_cairo_create_context(cairo);
1573 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1574 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1575 class->text[2], class->text[3]);
1576 cairo_move_to(cairo, 0, 0);
1577
1578 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1579 1715
1580 cairo_surface_flush(surface);
1581 unsigned char *data = cairo_image_surface_get_data(surface);
1582 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
1583 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1584 output->wlr_output->backend);
1585 *texture = wlr_texture_from_pixels(
1586 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
1587 cairo_surface_destroy(surface);
1588 g_object_unref(pango);
1589 cairo_destroy(cairo);
1590 free(buffer); 1716 free(buffer);
1591} 1717}
1592 1718
@@ -1602,25 +1728,27 @@ void container_update_marks_textures(struct sway_container *con) {
1602 &config->border_colors.unfocused); 1728 &config->border_colors.unfocused);
1603 update_marks_texture(con, &con->marks_urgent, 1729 update_marks_texture(con, &con->marks_urgent,
1604 &config->border_colors.urgent); 1730 &config->border_colors.urgent);
1731 update_marks_texture(con, &con->marks_focused_tab_title,
1732 &config->border_colors.focused_tab_title);
1605 container_damage_whole(con); 1733 container_damage_whole(con);
1606} 1734}
1607 1735
1608void container_raise_floating(struct sway_container *con) { 1736void container_raise_floating(struct sway_container *con) {
1609 // Bring container to front by putting it at the end of the floating list. 1737 // Bring container to front by putting it at the end of the floating list.
1610 struct sway_container *floater = container_toplevel_ancestor(con); 1738 struct sway_container *floater = container_toplevel_ancestor(con);
1611 if (container_is_floating(floater) && floater->workspace) { 1739 if (container_is_floating(floater) && floater->pending.workspace) {
1612 list_move_to_end(floater->workspace->floating, floater); 1740 list_move_to_end(floater->pending.workspace->floating, floater);
1613 node_set_dirty(&floater->workspace->node); 1741 node_set_dirty(&floater->pending.workspace->node);
1614 } 1742 }
1615} 1743}
1616 1744
1617bool container_is_scratchpad_hidden(struct sway_container *con) { 1745bool container_is_scratchpad_hidden(struct sway_container *con) {
1618 return con->scratchpad && !con->workspace; 1746 return con->scratchpad && !con->pending.workspace;
1619} 1747}
1620 1748
1621bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { 1749bool container_is_scratchpad_hidden_or_child(struct sway_container *con) {
1622 con = container_toplevel_ancestor(con); 1750 con = container_toplevel_ancestor(con);
1623 return con->scratchpad && !con->workspace; 1751 return con->scratchpad && !con->pending.workspace;
1624} 1752}
1625 1753
1626bool container_is_sticky(struct sway_container *con) { 1754bool container_is_sticky(struct sway_container *con) {
@@ -1648,39 +1776,39 @@ static bool is_parallel(enum sway_container_layout first,
1648static bool container_is_squashable(struct sway_container *con, 1776static bool container_is_squashable(struct sway_container *con,
1649 struct sway_container *child) { 1777 struct sway_container *child) {
1650 enum sway_container_layout gp_layout = container_parent_layout(con); 1778 enum sway_container_layout gp_layout = container_parent_layout(con);
1651 return (con->layout == L_HORIZ || con->layout == L_VERT) && 1779 return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) &&
1652 (child->layout == L_HORIZ || child->layout == L_VERT) && 1780 (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) &&
1653 !is_parallel(con->layout, child->layout) && 1781 !is_parallel(con->pending.layout, child->pending.layout) &&
1654 is_parallel(gp_layout, child->layout); 1782 is_parallel(gp_layout, child->pending.layout);
1655} 1783}
1656 1784
1657static void container_squash_children(struct sway_container *con) { 1785static void container_squash_children(struct sway_container *con) {
1658 for (int i = 0; i < con->children->length; i++) { 1786 for (int i = 0; i < con->pending.children->length; i++) {
1659 struct sway_container *child = con->children->items[i]; 1787 struct sway_container *child = con->pending.children->items[i];
1660 i += container_squash(child); 1788 i += container_squash(child);
1661 } 1789 }
1662} 1790}
1663 1791
1664int container_squash(struct sway_container *con) { 1792int container_squash(struct sway_container *con) {
1665 if (!con->children) { 1793 if (!con->pending.children) {
1666 return 0; 1794 return 0;
1667 } 1795 }
1668 if (con->children->length != 1) { 1796 if (con->pending.children->length != 1) {
1669 container_squash_children(con); 1797 container_squash_children(con);
1670 return 0; 1798 return 0;
1671 } 1799 }
1672 struct sway_container *child = con->children->items[0]; 1800 struct sway_container *child = con->pending.children->items[0];
1673 int idx = container_sibling_index(con); 1801 int idx = container_sibling_index(con);
1674 int change = 0; 1802 int change = 0;
1675 if (container_is_squashable(con, child)) { 1803 if (container_is_squashable(con, child)) {
1676 // con and child are a redundant H/V pair. Destroy them. 1804 // con and child are a redundant H/V pair. Destroy them.
1677 while (child->children->length) { 1805 while (child->pending.children->length) {
1678 struct sway_container *current = child->children->items[0]; 1806 struct sway_container *current = child->pending.children->items[0];
1679 container_detach(current); 1807 container_detach(current);
1680 if (con->parent) { 1808 if (con->pending.parent) {
1681 container_insert_child(con->parent, current, idx); 1809 container_insert_child(con->pending.parent, current, idx);
1682 } else { 1810 } else {
1683 workspace_insert_tiling_direct(con->workspace, current, idx); 1811 workspace_insert_tiling_direct(con->pending.workspace, current, idx);
1684 } 1812 }
1685 change++; 1813 change++;
1686 } 1814 }
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..ad8d2482 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -56,8 +56,8 @@ static void restore_workspaces(struct sway_output *output) {
56 } 56 }
57 57
58 // Saved workspaces 58 // Saved workspaces
59 while (root->noop_output->workspaces->length) { 59 while (root->fallback_output->workspaces->length) {
60 struct sway_workspace *ws = root->noop_output->workspaces->items[0]; 60 struct sway_workspace *ws = root->fallback_output->workspaces->items[0];
61 workspace_detach(ws); 61 workspace_detach(ws);
62 output_add_workspace(output, ws); 62 output_add_workspace(output, ws);
63 63
@@ -70,13 +70,13 @@ static void restore_workspaces(struct sway_output *output) {
70 // floater re-centered 70 // floater re-centered
71 for (int i = 0; i < ws->floating->length; i++) { 71 for (int i = 0; i < ws->floating->length; i++) {
72 struct sway_container *floater = ws->floating->items[i]; 72 struct sway_container *floater = ws->floating->items[i];
73 if (floater->width == 0 || floater->height == 0 || 73 if (floater->pending.width == 0 || floater->pending.height == 0 ||
74 floater->width > output->width || 74 floater->pending.width > output->width ||
75 floater->height > output->height || 75 floater->pending.height > output->height ||
76 floater->x > output->lx + output->width || 76 floater->pending.x > output->lx + output->width ||
77 floater->y > output->ly + output->height || 77 floater->pending.y > output->ly + output->height ||
78 floater->x + floater->width < output->lx || 78 floater->pending.x + floater->pending.width < output->lx ||
79 floater->y + floater->height < output->ly) { 79 floater->pending.y + floater->pending.height < output->ly) {
80 container_floating_resize_and_center(floater); 80 container_floating_resize_and_center(floater);
81 } 81 }
82 } 82 }
@@ -95,7 +95,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
95 output->detected_subpixel = wlr_output->subpixel; 95 output->detected_subpixel = wlr_output->subpixel;
96 output->scale_filter = SCALE_FILTER_NEAREST; 96 output->scale_filter = SCALE_FILTER_NEAREST;
97 97
98 wl_signal_init(&output->events.destroy); 98 wl_signal_init(&output->events.disable);
99 99
100 wl_list_insert(&root->all_outputs, &output->link); 100 wl_list_insert(&root->all_outputs, &output->link);
101 101
@@ -192,7 +192,7 @@ static void output_evacuate(struct sway_output *output) {
192 new_output = fallback_output; 192 new_output = fallback_output;
193 } 193 }
194 if (!new_output) { 194 if (!new_output) {
195 new_output = root->noop_output; 195 new_output = root->fallback_output;
196 } 196 }
197 197
198 struct sway_workspace *new_output_ws = 198 struct sway_workspace *new_output_ws =
@@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) {
262 } 262 }
263 263
264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); 264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
265 wl_signal_emit(&output->events.destroy, output); 265 wl_signal_emit(&output->events.disable, output);
266 266
267 output_evacuate(output); 267 output_evacuate(output);
268 268
@@ -286,13 +286,10 @@ void output_begin_destroy(struct sway_output *output) {
286 return; 286 return;
287 } 287 }
288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); 288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
289 wl_signal_emit(&output->node.events.destroy, &output->node);
289 290
290 output->node.destroying = true; 291 output->node.destroying = true;
291 node_set_dirty(&output->node); 292 node_set_dirty(&output->node);
292
293 wl_list_remove(&output->link);
294 output->wlr_output->data = NULL;
295 output->wlr_output = NULL;
296} 293}
297 294
298struct sway_output *output_from_wlr_output(struct wlr_output *output) { 295struct sway_output *output_from_wlr_output(struct wlr_output *output) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index ebd185ec..73f3993c 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);
@@ -365,8 +374,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
365 } 374 }
366 375
367 // Saved workspaces 376 // Saved workspaces
368 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 377 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
369 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 378 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
370 workspace_for_each_container(ws, f, data); 379 workspace_for_each_container(ws, f, data);
371 } 380 }
372} 381}
@@ -418,8 +427,8 @@ struct sway_container *root_find_container(
418 } 427 }
419 428
420 // Saved workspaces 429 // Saved workspaces
421 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 430 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
422 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 431 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
423 if ((result = workspace_find_container(ws, test, data))) { 432 if ((result = workspace_find_container(ws, test, data))) {
424 return result; 433 return result;
425 } 434 }
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7afcdf31..7d9e038d 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -6,6 +6,7 @@
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_output_layout.h> 7#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 8#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_subcompositor.h>
9#include <wlr/types/wlr_xdg_decoration_v1.h> 10#include <wlr/types/wlr_xdg_decoration_v1.h>
10#include "config.h" 11#include "config.h"
11#if HAVE_XWAYLAND 12#if HAVE_XWAYLAND
@@ -56,6 +57,7 @@ void view_destroy(struct sway_view *view) {
56 "(might have a pending transaction?)")) { 57 "(might have a pending transaction?)")) {
57 return; 58 return;
58 } 59 }
60 wl_list_remove(&view->events.unmap.listener_list);
59 if (!wl_list_empty(&view->saved_buffers)) { 61 if (!wl_list_empty(&view->saved_buffers)) {
60 view_remove_saved_buffer(view); 62 view_remove_saved_buffer(view);
61 } 63 }
@@ -206,7 +208,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) {
206 } else { 208 } else {
207 only_visible = true; 209 only_visible = true;
208 } 210 }
209 con = con->parent; 211 con = con->pending.parent;
210 } 212 }
211 return only_visible; 213 return only_visible;
212} 214}
@@ -222,72 +224,73 @@ static bool view_is_only_visible(struct sway_view *view) {
222 } 224 }
223 } 225 }
224 226
225 con = con->parent; 227 con = con->pending.parent;
226 } 228 }
227 229
228 return true; 230 return true;
229} 231}
230 232
231static bool gaps_to_edge(struct sway_view *view) { 233static bool gaps_to_edge(struct sway_view *view) {
232 struct side_gaps gaps = view->container->workspace->current_gaps; 234 struct side_gaps gaps = view->container->pending.workspace->current_gaps;
233 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; 235 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0;
234} 236}
235 237
236void view_autoconfigure(struct sway_view *view) { 238void view_autoconfigure(struct sway_view *view) {
237 struct sway_container *con = view->container; 239 struct sway_container *con = view->container;
238 struct sway_workspace *ws = con->workspace; 240 struct sway_workspace *ws = con->pending.workspace;
239 241
240 if (container_is_scratchpad_hidden(con) && 242 if (container_is_scratchpad_hidden(con) &&
241 con->fullscreen_mode != FULLSCREEN_GLOBAL) { 243 con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
242 return; 244 return;
243 } 245 }
244 struct sway_output *output = ws ? ws->output : NULL; 246 struct sway_output *output = ws ? ws->output : NULL;
245 247
246 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 248 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
247 con->content_x = output->lx; 249 con->pending.content_x = output->lx;
248 con->content_y = output->ly; 250 con->pending.content_y = output->ly;
249 con->content_width = output->width; 251 con->pending.content_width = output->width;
250 con->content_height = output->height; 252 con->pending.content_height = output->height;
251 return; 253 return;
252 } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 254 } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
253 con->content_x = root->x; 255 con->pending.content_x = root->x;
254 con->content_y = root->y; 256 con->pending.content_y = root->y;
255 con->content_width = root->width; 257 con->pending.content_width = root->width;
256 con->content_height = root->height; 258 con->pending.content_height = root->height;
257 return; 259 return;
258 } 260 }
259 261
260 con->border_top = con->border_bottom = true; 262 con->pending.border_top = con->pending.border_bottom = true;
261 con->border_left = con->border_right = true; 263 con->pending.border_left = con->pending.border_right = true;
262 double y_offset = 0; 264 double y_offset = 0;
263 265
264 if (!container_is_floating(con) && ws) { 266 if (!container_is_floating_or_child(con) && ws) {
265 if (config->hide_edge_borders == E_BOTH 267 if (config->hide_edge_borders == E_BOTH
266 || config->hide_edge_borders == E_VERTICAL) { 268 || config->hide_edge_borders == E_VERTICAL) {
267 con->border_left = con->x != ws->x; 269 con->pending.border_left = con->pending.x != ws->x;
268 int right_x = con->x + con->width; 270 int right_x = con->pending.x + con->pending.width;
269 con->border_right = right_x != ws->x + ws->width; 271 con->pending.border_right = right_x != ws->x + ws->width;
270 } 272 }
271 273
272 if (config->hide_edge_borders == E_BOTH 274 if (config->hide_edge_borders == E_BOTH
273 || config->hide_edge_borders == E_HORIZONTAL) { 275 || config->hide_edge_borders == E_HORIZONTAL) {
274 con->border_top = con->y != ws->y; 276 con->pending.border_top = con->pending.y != ws->y;
275 int bottom_y = con->y + con->height; 277 int bottom_y = con->pending.y + con->pending.height;
276 con->border_bottom = bottom_y != ws->y + ws->height; 278 con->pending.border_bottom = bottom_y != ws->y + ws->height;
277 } 279 }
278 280
279 bool smart = config->hide_edge_borders_smart == ESMART_ON || 281 bool smart = config->hide_edge_borders_smart == ESMART_ON ||
280 (config->hide_edge_borders_smart == ESMART_NO_GAPS && 282 (config->hide_edge_borders_smart == ESMART_NO_GAPS &&
281 !gaps_to_edge(view)); 283 !gaps_to_edge(view));
282 if (smart) { 284 if (smart) {
283 bool show_border = container_is_floating_or_child(con) || 285 bool show_border = !view_is_only_visible(view);
284 !view_is_only_visible(view); 286 con->pending.border_left &= show_border;
285 con->border_left &= show_border; 287 con->pending.border_right &= show_border;
286 con->border_right &= show_border; 288 con->pending.border_top &= show_border;
287 con->border_top &= show_border; 289 con->pending.border_bottom &= show_border;
288 con->border_bottom &= show_border;
289 } 290 }
291 }
290 292
293 if (!container_is_floating(con)) {
291 // In a tabbed or stacked container, the container's y is the top of the 294 // In a tabbed or stacked container, the container's y is the top of the
292 // title area. We have to offset the surface y by the height of the title, 295 // title area. We have to offset the surface y by the height of the title,
293 // bar, and disable any top border because we'll always have the title bar. 296 // bar, and disable any top border because we'll always have the title bar.
@@ -298,56 +301,56 @@ void view_autoconfigure(struct sway_view *view) {
298 enum sway_container_layout layout = container_parent_layout(con); 301 enum sway_container_layout layout = container_parent_layout(con);
299 if (layout == L_TABBED) { 302 if (layout == L_TABBED) {
300 y_offset = container_titlebar_height(); 303 y_offset = container_titlebar_height();
301 con->border_top = false; 304 con->pending.border_top = false;
302 } else if (layout == L_STACKED) { 305 } else if (layout == L_STACKED) {
303 y_offset = container_titlebar_height() * siblings->length; 306 y_offset = container_titlebar_height() * siblings->length;
304 con->border_top = false; 307 con->pending.border_top = false;
305 } 308 }
306 } 309 }
307 } 310 }
308 311
309 double x, y, width, height; 312 double x, y, width, height;
310 switch (con->border) { 313 switch (con->pending.border) {
311 default: 314 default:
312 case B_CSD: 315 case B_CSD:
313 case B_NONE: 316 case B_NONE:
314 x = con->x; 317 x = con->pending.x;
315 y = con->y + y_offset; 318 y = con->pending.y + y_offset;
316 width = con->width; 319 width = con->pending.width;
317 height = con->height - y_offset; 320 height = con->pending.height - y_offset;
318 break; 321 break;
319 case B_PIXEL: 322 case B_PIXEL:
320 x = con->x + con->border_thickness * con->border_left; 323 x = con->pending.x + con->pending.border_thickness * con->pending.border_left;
321 y = con->y + con->border_thickness * con->border_top + y_offset; 324 y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset;
322 width = con->width 325 width = con->pending.width
323 - con->border_thickness * con->border_left 326 - con->pending.border_thickness * con->pending.border_left
324 - con->border_thickness * con->border_right; 327 - con->pending.border_thickness * con->pending.border_right;
325 height = con->height - y_offset 328 height = con->pending.height - y_offset
326 - con->border_thickness * con->border_top 329 - con->pending.border_thickness * con->pending.border_top
327 - con->border_thickness * con->border_bottom; 330 - con->pending.border_thickness * con->pending.border_bottom;
328 break; 331 break;
329 case B_NORMAL: 332 case B_NORMAL:
330 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border 333 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
331 x = con->x + con->border_thickness * con->border_left; 334 x = con->pending.x + con->pending.border_thickness * con->pending.border_left;
332 width = con->width 335 width = con->pending.width
333 - con->border_thickness * con->border_left 336 - con->pending.border_thickness * con->pending.border_left
334 - con->border_thickness * con->border_right; 337 - con->pending.border_thickness * con->pending.border_right;
335 if (y_offset) { 338 if (y_offset) {
336 y = con->y + y_offset; 339 y = con->pending.y + y_offset;
337 height = con->height - y_offset 340 height = con->pending.height - y_offset
338 - con->border_thickness * con->border_bottom; 341 - con->pending.border_thickness * con->pending.border_bottom;
339 } else { 342 } else {
340 y = con->y + container_titlebar_height(); 343 y = con->pending.y + container_titlebar_height();
341 height = con->height - container_titlebar_height() 344 height = con->pending.height - container_titlebar_height()
342 - con->border_thickness * con->border_bottom; 345 - con->pending.border_thickness * con->pending.border_bottom;
343 } 346 }
344 break; 347 break;
345 } 348 }
346 349
347 con->content_x = x; 350 con->pending.content_x = x;
348 con->content_y = y; 351 con->pending.content_y = y;
349 con->content_width = width; 352 con->pending.content_width = width;
350 con->content_height = height; 353 con->pending.content_height = height;
351} 354}
352 355
353void view_set_activated(struct sway_view *view, bool activated) { 356void view_set_activated(struct sway_view *view, bool activated) {
@@ -361,7 +364,7 @@ void view_set_activated(struct sway_view *view, bool activated) {
361} 364}
362 365
363void view_request_activate(struct sway_view *view) { 366void view_request_activate(struct sway_view *view) {
364 struct sway_workspace *ws = view->container->workspace; 367 struct sway_workspace *ws = view->container->pending.workspace;
365 if (!ws) { // hidden scratchpad container 368 if (!ws) { // hidden scratchpad container
366 return; 369 return;
367 } 370 }
@@ -401,13 +404,13 @@ void view_set_csd_from_server(struct sway_view *view, bool enabled) {
401void view_update_csd_from_client(struct sway_view *view, bool enabled) { 404void view_update_csd_from_client(struct sway_view *view, bool enabled) {
402 sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); 405 sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled);
403 struct sway_container *con = view->container; 406 struct sway_container *con = view->container;
404 if (enabled && con && con->border != B_CSD) { 407 if (enabled && con && con->pending.border != B_CSD) {
405 con->saved_border = con->border; 408 con->saved_border = con->pending.border;
406 if (container_is_floating(con)) { 409 if (container_is_floating(con)) {
407 con->border = B_CSD; 410 con->pending.border = B_CSD;
408 } 411 }
409 } else if (!enabled && con && con->border == B_CSD) { 412 } else if (!enabled && con && con->pending.border == B_CSD) {
410 con->border = con->saved_border; 413 con->pending.border = con->saved_border;
411 } 414 }
412 view->using_csd = enabled; 415 view->using_csd = enabled;
413} 416}
@@ -465,6 +468,9 @@ static void view_subsurface_create(struct sway_view *view,
465static void view_init_subsurfaces(struct sway_view *view, 468static void view_init_subsurfaces(struct sway_view *view,
466 struct wlr_surface *surface); 469 struct wlr_surface *surface);
467 470
471static void view_child_init_subsurfaces(struct sway_view_child *view_child,
472 struct wlr_surface *surface);
473
468static void view_handle_surface_new_subsurface(struct wl_listener *listener, 474static void view_handle_surface_new_subsurface(struct wl_listener *listener,
469 void *data) { 475 void *data) {
470 struct sway_view *view = 476 struct sway_view *view =
@@ -577,7 +583,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
577 if (node && node->type == N_WORKSPACE) { 583 if (node && node->type == N_WORKSPACE) {
578 return node->sway_workspace; 584 return node->sway_workspace;
579 } else if (node && node->type == N_CONTAINER) { 585 } else if (node && node->type == N_CONTAINER) {
580 return node->sway_container->workspace; 586 return node->sway_container->pending.workspace;
581 } 587 }
582 588
583 // When there's no outputs connected, the above should match a workspace on 589 // When there's no outputs connected, the above should match a workspace on
@@ -590,12 +596,17 @@ static bool should_focus(struct sway_view *view) {
590 struct sway_seat *seat = input_manager_current_seat(); 596 struct sway_seat *seat = input_manager_current_seat();
591 struct sway_container *prev_con = seat_get_focused_container(seat); 597 struct sway_container *prev_con = seat_get_focused_container(seat);
592 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); 598 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
593 struct sway_workspace *map_ws = view->container->workspace; 599 struct sway_workspace *map_ws = view->container->pending.workspace;
594 600
595 if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { 601 if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
596 return true; 602 return true;
597 } 603 }
598 604
605 // View opened "under" fullscreen view should not be given focus.
606 if (root->fullscreen_global || !map_ws || map_ws->fullscreen) {
607 return false;
608 }
609
599 // Views can only take focus if they are mapped into the active workspace 610 // Views can only take focus if they are mapped into the active workspace
600 if (prev_ws != map_ws) { 611 if (prev_ws != map_ws) {
601 return false; 612 return false;
@@ -603,9 +614,9 @@ static bool should_focus(struct sway_view *view) {
603 614
604 // If the view is the only one in the focused workspace, it'll get focus 615 // If the view is the only one in the focused workspace, it'll get focus
605 // regardless of any no_focus criteria. 616 // regardless of any no_focus criteria.
606 if (!view->container->parent && !prev_con) { 617 if (!view->container->pending.parent && !prev_con) {
607 size_t num_children = view->container->workspace->tiling->length + 618 size_t num_children = view->container->pending.workspace->tiling->length +
608 view->container->workspace->floating->length; 619 view->container->pending.workspace->floating->length;
609 if (num_children == 1) { 620 if (num_children == 1) {
610 return true; 621 return true;
611 } 622 }
@@ -635,6 +646,7 @@ static void handle_foreign_activate_request(
635 break; 646 break;
636 } 647 }
637 } 648 }
649 transaction_commit_dirty();
638} 650}
639 651
640static void handle_foreign_fullscreen_request( 652static void handle_foreign_fullscreen_request(
@@ -645,9 +657,21 @@ static void handle_foreign_fullscreen_request(
645 657
646 // Match fullscreen command behavior for scratchpad hidden views 658 // Match fullscreen command behavior for scratchpad hidden views
647 struct sway_container *container = view->container; 659 struct sway_container *container = view->container;
648 if (!container->workspace) { 660 if (!container->pending.workspace) {
649 while (container->parent) { 661 while (container->pending.parent) {
650 container = container->parent; 662 container = container->pending.parent;
663 }
664 }
665
666 if (event->fullscreen && event->output && event->output->data) {
667 struct sway_output *output = event->output->data;
668 struct sway_workspace *ws = output_get_active_workspace(output);
669 if (ws && !container_is_scratchpad_hidden(view->container)) {
670 if (container_is_floating(view->container)) {
671 workspace_add_floating(ws, view->container);
672 } else {
673 workspace_add_tiling(ws, view->container);
674 }
651 } 675 }
652 } 676 }
653 677
@@ -656,12 +680,13 @@ static void handle_foreign_fullscreen_request(
656 if (event->fullscreen) { 680 if (event->fullscreen) {
657 arrange_root(); 681 arrange_root();
658 } else { 682 } else {
659 if (container->parent) { 683 if (container->pending.parent) {
660 arrange_container(container->parent); 684 arrange_container(container->pending.parent);
661 } else if (container->workspace) { 685 } else if (container->pending.workspace) {
662 arrange_workspace(container->workspace); 686 arrange_workspace(container->pending.workspace);
663 } 687 }
664 } 688 }
689 transaction_commit_dirty();
665} 690}
666 691
667static void handle_foreign_close_request( 692static void handle_foreign_close_request(
@@ -705,10 +730,29 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
705 } 730 }
706 731
707 struct sway_seat *seat = input_manager_current_seat(); 732 struct sway_seat *seat = input_manager_current_seat();
708 struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) 733 struct sway_node *node =
709 : seat_get_focus_inactive(seat, &root->node); 734 seat_get_focus_inactive(seat, ws ? &ws->node : &root->node);
710 struct sway_container *target_sibling = node->type == N_CONTAINER ? 735 struct sway_container *target_sibling = NULL;
711 node->sway_container : NULL; 736 if (node && node->type == N_CONTAINER) {
737 if (container_is_floating(node->sway_container)) {
738 // If we're about to launch the view into the floating container, then
739 // launch it as a tiled view instead.
740 if (ws) {
741 target_sibling = seat_get_focus_inactive_tiling(seat, ws);
742 if (target_sibling) {
743 struct sway_container *con =
744 seat_get_focus_inactive_view(seat, &target_sibling->node);
745 if (con) {
746 target_sibling = con;
747 }
748 }
749 } else {
750 ws = seat_get_last_known_workspace(seat);
751 }
752 } else {
753 target_sibling = node->sway_container;
754 }
755 }
712 756
713 view->foreign_toplevel = 757 view->foreign_toplevel =
714 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 758 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
@@ -725,13 +769,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
725 wl_signal_add(&view->foreign_toplevel->events.destroy, 769 wl_signal_add(&view->foreign_toplevel->events.destroy,
726 &view->foreign_destroy); 770 &view->foreign_destroy);
727 771
728 // If we're about to launch the view into the floating container, then
729 // launch it as a tiled view in the root of the workspace instead.
730 if (target_sibling && container_is_floating(target_sibling)) {
731 target_sibling = NULL;
732 ws = seat_get_last_known_workspace(seat);
733 }
734
735 struct sway_container *container = view->container; 772 struct sway_container *container = view->container;
736 if (target_sibling) { 773 if (target_sibling) {
737 container_add_sibling(target_sibling, container, 1); 774 container_add_sibling(target_sibling, container, 1);
@@ -742,7 +779,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
742 779
743 view_init_subsurfaces(view, wlr_surface); 780 view_init_subsurfaces(view, wlr_surface);
744 wl_signal_add(&wlr_surface->events.new_subsurface, 781 wl_signal_add(&wlr_surface->events.new_subsurface,
745 &view->surface_new_subsurface); 782 &view->surface_new_subsurface);
746 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; 783 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
747 784
748 if (decoration) { 785 if (decoration) {
@@ -750,20 +787,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
750 } 787 }
751 788
752 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 789 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
753 view->container->border = config->floating_border; 790 view->container->pending.border = config->floating_border;
754 view->container->border_thickness = config->floating_border_thickness; 791 view->container->pending.border_thickness = config->floating_border_thickness;
755 container_set_floating(view->container, true); 792 container_set_floating(view->container, true);
756 } else { 793 } else {
757 view->container->border = config->border; 794 view->container->pending.border = config->border;
758 view->container->border_thickness = config->border_thickness; 795 view->container->pending.border_thickness = config->border_thickness;
759 view_set_tiled(view, true); 796 view_set_tiled(view, true);
760 } 797 }
761 798
762 if (config->popup_during_fullscreen == POPUP_LEAVE && 799 if (config->popup_during_fullscreen == POPUP_LEAVE &&
763 container->workspace && 800 container->pending.workspace &&
764 container->workspace->fullscreen && 801 container->pending.workspace->fullscreen &&
765 container->workspace->fullscreen->view) { 802 container->pending.workspace->fullscreen->view) {
766 struct sway_container *fs = container->workspace->fullscreen; 803 struct sway_container *fs = container->pending.workspace->fullscreen;
767 if (view_is_transient_for(view, fs->view)) { 804 if (view_is_transient_for(view, fs->view)) {
768 container_set_fullscreen(fs, false); 805 container_set_fullscreen(fs, false);
769 } 806 }
@@ -774,12 +811,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
774 811
775 if (fullscreen) { 812 if (fullscreen) {
776 container_set_fullscreen(view->container, true); 813 container_set_fullscreen(view->container, true);
777 arrange_workspace(view->container->workspace); 814 arrange_workspace(view->container->pending.workspace);
778 } else { 815 } else {
779 if (container->parent) { 816 if (container->pending.parent) {
780 arrange_container(container->parent); 817 arrange_container(container->pending.parent);
781 } else if (container->workspace) { 818 } else if (container->pending.workspace) {
782 arrange_workspace(container->workspace); 819 arrange_workspace(container->pending.workspace);
783 } 820 }
784 } 821 }
785 822
@@ -790,9 +827,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
790#if HAVE_XWAYLAND 827#if HAVE_XWAYLAND
791 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 828 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
792 struct wlr_xwayland_surface *xsurface = 829 struct wlr_xwayland_surface *xsurface =
793 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 830 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
794 set_focus = (wlr_xwayland_icccm_input_model(xsurface) != 831 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
795 WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; 832 WLR_ICCCM_INPUT_MODEL_NONE;
796 } 833 }
797#endif 834#endif
798 835
@@ -803,11 +840,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
803 const char *app_id; 840 const char *app_id;
804 const char *class; 841 const char *class;
805 if ((app_id = view_get_app_id(view)) != NULL) { 842 if ((app_id = view_get_app_id(view)) != NULL) {
806 wlr_foreign_toplevel_handle_v1_set_app_id( 843 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);
807 view->foreign_toplevel, app_id);
808 } else if ((class = view_get_class(view)) != NULL) { 844 } else if ((class = view_get_class(view)) != NULL) {
809 wlr_foreign_toplevel_handle_v1_set_app_id( 845 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class);
810 view->foreign_toplevel, class);
811 } 846 }
812} 847}
813 848
@@ -826,8 +861,8 @@ void view_unmap(struct sway_view *view) {
826 view->foreign_toplevel = NULL; 861 view->foreign_toplevel = NULL;
827 } 862 }
828 863
829 struct sway_container *parent = view->container->parent; 864 struct sway_container *parent = view->container->pending.parent;
830 struct sway_workspace *ws = view->container->workspace; 865 struct sway_workspace *ws = view->container->pending.workspace;
831 container_begin_destroy(view->container); 866 container_begin_destroy(view->container);
832 if (parent) { 867 if (parent) {
833 container_reap_empty(parent); 868 container_reap_empty(parent);
@@ -860,47 +895,38 @@ void view_unmap(struct sway_view *view) {
860 view->surface = NULL; 895 view->surface = NULL;
861} 896}
862 897
863void view_update_size(struct sway_view *view, int width, int height) { 898void view_update_size(struct sway_view *view) {
864 struct sway_container *con = view->container; 899 struct sway_container *con = view->container;
900 con->pending.content_width = view->geometry.width;
901 con->pending.content_height = view->geometry.height;
902 container_set_geometry_from_content(con);
903}
865 904
866 if (container_is_floating(con)) { 905void view_center_surface(struct sway_view *view) {
867 con->content_width = width; 906 struct sway_container *con = view->container;
868 con->content_height = height; 907 // We always center the current coordinates rather than the next, as the
869 container_set_geometry_from_content(con); 908 // geometry immediately affects the currently active rendering.
870 } else { 909 con->surface_x = fmax(con->current.content_x, con->current.content_x +
871 con->surface_x = con->content_x + (con->content_width - width) / 2; 910 (con->current.content_width - view->geometry.width) / 2);
872 con->surface_y = con->content_y + (con->content_height - height) / 2; 911 con->surface_y = fmax(con->current.content_y, con->current.content_y +
873 con->surface_x = fmax(con->surface_x, con->content_x); 912 (con->current.content_height - view->geometry.height) / 2);
874 con->surface_y = fmax(con->surface_y, con->content_y);
875 }
876} 913}
877 914
878static const struct sway_view_child_impl subsurface_impl; 915static const struct sway_view_child_impl subsurface_impl;
879 916
880static void subsurface_get_root_coords(struct sway_view_child *child, 917static void subsurface_get_view_coords(struct sway_view_child *child,
881 int *root_sx, int *root_sy) { 918 int *sx, int *sy) {
882 struct wlr_surface *surface = child->surface; 919 struct wlr_surface *surface = child->surface;
883 *root_sx = -child->view->geometry.x;
884 *root_sy = -child->view->geometry.y;
885
886 if (child->parent && child->parent->impl && 920 if (child->parent && child->parent->impl &&
887 child->parent->impl->get_root_coords) { 921 child->parent->impl->get_view_coords) {
888 int sx, sy; 922 child->parent->impl->get_view_coords(child->parent, sx, sy);
889 child->parent->impl->get_root_coords(child->parent, &sx, &sy);
890 *root_sx += sx;
891 *root_sy += sy;
892 } else { 923 } else {
893 while (surface && wlr_surface_is_subsurface(surface)) { 924 *sx = *sy = 0;
894 struct wlr_subsurface *subsurface =
895 wlr_subsurface_from_wlr_surface(surface);
896 if (subsurface == NULL) {
897 break;
898 }
899 *root_sx += subsurface->current.x;
900 *root_sy += subsurface->current.y;
901 surface = subsurface->parent;
902 }
903 } 925 }
926 struct wlr_subsurface *subsurface =
927 wlr_subsurface_from_wlr_surface(surface);
928 *sx += subsurface->current.x;
929 *sy += subsurface->current.y;
904} 930}
905 931
906static void subsurface_destroy(struct sway_view_child *child) { 932static void subsurface_destroy(struct sway_view_child *child) {
@@ -914,7 +940,7 @@ static void subsurface_destroy(struct sway_view_child *child) {
914} 940}
915 941
916static const struct sway_view_child_impl subsurface_impl = { 942static const struct sway_view_child_impl subsurface_impl = {
917 .get_root_coords = subsurface_get_root_coords, 943 .get_view_coords = subsurface_get_view_coords,
918 .destroy = subsurface_destroy, 944 .destroy = subsurface_destroy,
919}; 945};
920 946
@@ -968,15 +994,27 @@ static void view_child_subsurface_create(struct sway_view_child *child,
968 view_child_damage(&subsurface->child, true); 994 view_child_damage(&subsurface->child, true);
969} 995}
970 996
997static bool view_child_is_mapped(struct sway_view_child *child) {
998 while (child) {
999 if (!child->mapped) {
1000 return false;
1001 }
1002 child = child->parent;
1003 }
1004 return true;
1005}
1006
971static void view_child_damage(struct sway_view_child *child, bool whole) { 1007static void view_child_damage(struct sway_view_child *child, bool whole) {
972 if (!child || !child->mapped || !child->view || !child->view->container) { 1008 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
973 return; 1009 return;
974 } 1010 }
975 int sx, sy; 1011 int sx, sy;
976 child->impl->get_root_coords(child, &sx, &sy); 1012 child->impl->get_view_coords(child, &sx, &sy);
977 desktop_damage_surface(child->surface, 1013 desktop_damage_surface(child->surface,
978 child->view->container->content_x + sx, 1014 child->view->container->pending.content_x -
979 child->view->container->content_y + sy, whole); 1015 child->view->geometry.x + sx,
1016 child->view->container->pending.content_y -
1017 child->view->geometry.y + sy, whole);
980} 1018}
981 1019
982static void view_child_handle_surface_commit(struct wl_listener *listener, 1020static void view_child_handle_surface_commit(struct wl_listener *listener,
@@ -1004,11 +1042,29 @@ static void view_child_handle_surface_destroy(struct wl_listener *listener,
1004static void view_init_subsurfaces(struct sway_view *view, 1042static void view_init_subsurfaces(struct sway_view *view,
1005 struct wlr_surface *surface) { 1043 struct wlr_surface *surface) {
1006 struct wlr_subsurface *subsurface; 1044 struct wlr_subsurface *subsurface;
1007 wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { 1045 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1046 current.link) {
1047 view_subsurface_create(view, subsurface);
1048 }
1049 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1050 current.link) {
1008 view_subsurface_create(view, subsurface); 1051 view_subsurface_create(view, subsurface);
1009 } 1052 }
1010} 1053}
1011 1054
1055static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1056 struct wlr_surface *surface) {
1057 struct wlr_subsurface *subsurface;
1058 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1059 current.link) {
1060 view_child_subsurface_create(view_child, subsurface);
1061 }
1062 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1063 current.link) {
1064 view_child_subsurface_create(view_child, subsurface);
1065 }
1066}
1067
1012static void view_child_handle_surface_map(struct wl_listener *listener, 1068static void view_child_handle_surface_map(struct wl_listener *listener,
1013 void *data) { 1069 void *data) {
1014 struct sway_view_child *child = 1070 struct sway_view_child *child =
@@ -1059,16 +1115,19 @@ void view_child_init(struct sway_view_child *child,
1059 wl_signal_add(&view->events.unmap, &child->view_unmap); 1115 wl_signal_add(&view->events.unmap, &child->view_unmap);
1060 child->view_unmap.notify = view_child_handle_view_unmap; 1116 child->view_unmap.notify = view_child_handle_view_unmap;
1061 1117
1062 struct sway_workspace *workspace = child->view->container->workspace; 1118 struct sway_container *container = child->view->container;
1063 if (workspace) { 1119 if (container != NULL) {
1064 wlr_surface_send_enter(child->surface, workspace->output->wlr_output); 1120 struct sway_workspace *workspace = container->pending.workspace;
1121 if (workspace) {
1122 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1123 }
1065 } 1124 }
1066 1125
1067 view_init_subsurfaces(child->view, surface); 1126 view_child_init_subsurfaces(child, surface);
1068} 1127}
1069 1128
1070void view_child_destroy(struct sway_view_child *child) { 1129void view_child_destroy(struct sway_view_child *child) {
1071 if (child->mapped && child->view->container != NULL) { 1130 if (view_child_is_mapped(child) && child->view->container != NULL) {
1072 view_child_damage(child, true); 1131 view_child_damage(child, true);
1073 } 1132 }
1074 1133
@@ -1081,6 +1140,9 @@ void view_child_destroy(struct sway_view_child *child) {
1081 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { 1140 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1082 wl_list_remove(&subchild->link); 1141 wl_list_remove(&subchild->link);
1083 subchild->parent = NULL; 1142 subchild->parent = NULL;
1143 // The subchild lost its parent link, so it cannot see that the parent
1144 // is unmapped. Unmap it directly.
1145 subchild->mapped = false;
1084 } 1146 }
1085 1147
1086 wl_list_remove(&child->surface_commit.link); 1148 wl_list_remove(&child->surface_commit.link);
@@ -1101,18 +1163,27 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1101 if (wlr_surface_is_xdg_surface(wlr_surface)) { 1163 if (wlr_surface_is_xdg_surface(wlr_surface)) {
1102 struct wlr_xdg_surface *xdg_surface = 1164 struct wlr_xdg_surface *xdg_surface =
1103 wlr_xdg_surface_from_wlr_surface(wlr_surface); 1165 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1166 if (xdg_surface == NULL) {
1167 return NULL;
1168 }
1104 return view_from_wlr_xdg_surface(xdg_surface); 1169 return view_from_wlr_xdg_surface(xdg_surface);
1105 } 1170 }
1106#if HAVE_XWAYLAND 1171#if HAVE_XWAYLAND
1107 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 1172 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
1108 struct wlr_xwayland_surface *xsurface = 1173 struct wlr_xwayland_surface *xsurface =
1109 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 1174 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1175 if (xsurface == NULL) {
1176 return NULL;
1177 }
1110 return view_from_wlr_xwayland_surface(xsurface); 1178 return view_from_wlr_xwayland_surface(xsurface);
1111 } 1179 }
1112#endif 1180#endif
1113 if (wlr_surface_is_subsurface(wlr_surface)) { 1181 if (wlr_surface_is_subsurface(wlr_surface)) {
1114 struct wlr_subsurface *subsurface = 1182 struct wlr_subsurface *subsurface =
1115 wlr_subsurface_from_wlr_surface(wlr_surface); 1183 wlr_subsurface_from_wlr_surface(wlr_surface);
1184 if (subsurface == NULL) {
1185 return NULL;
1186 }
1116 return view_from_wlr_surface(subsurface->parent); 1187 return view_from_wlr_surface(subsurface->parent);
1117 } 1188 }
1118 if (wlr_surface_is_layer_surface(wlr_surface)) { 1189 if (wlr_surface_is_layer_surface(wlr_surface)) {
@@ -1225,8 +1296,6 @@ void view_update_title(struct sway_view *view, bool force) {
1225 view->container->title = NULL; 1296 view->container->title = NULL;
1226 view->container->formatted_title = NULL; 1297 view->container->formatted_title = NULL;
1227 } 1298 }
1228 container_calculate_title_height(view->container);
1229 config_update_font_height(false);
1230 1299
1231 // Update title after the global font height is updated 1300 // Update title after the global font height is updated
1232 container_update_title_textures(view->container); 1301 container_update_title_textures(view->container);
@@ -1242,15 +1311,15 @@ bool view_is_visible(struct sway_view *view) {
1242 if (view->container->node.destroying) { 1311 if (view->container->node.destroying) {
1243 return false; 1312 return false;
1244 } 1313 }
1245 struct sway_workspace *workspace = view->container->workspace; 1314 struct sway_workspace *workspace = view->container->pending.workspace;
1246 if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { 1315 if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
1247 bool fs_global_descendant = false; 1316 bool fs_global_descendant = false;
1248 struct sway_container *parent = view->container->parent; 1317 struct sway_container *parent = view->container->pending.parent;
1249 while (parent) { 1318 while (parent) {
1250 if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { 1319 if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1251 fs_global_descendant = true; 1320 fs_global_descendant = true;
1252 } 1321 }
1253 parent = parent->parent; 1322 parent = parent->pending.parent;
1254 } 1323 }
1255 if (!fs_global_descendant) { 1324 if (!fs_global_descendant) {
1256 return false; 1325 return false;
@@ -1268,13 +1337,13 @@ bool view_is_visible(struct sway_view *view) {
1268 enum sway_container_layout layout = container_parent_layout(con); 1337 enum sway_container_layout layout = container_parent_layout(con);
1269 if ((layout == L_TABBED || layout == L_STACKED) 1338 if ((layout == L_TABBED || layout == L_STACKED)
1270 && !container_is_floating(con)) { 1339 && !container_is_floating(con)) {
1271 struct sway_node *parent = con->parent ? 1340 struct sway_node *parent = con->pending.parent ?
1272 &con->parent->node : &con->workspace->node; 1341 &con->pending.parent->node : &con->pending.workspace->node;
1273 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1342 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
1274 return false; 1343 return false;
1275 } 1344 }
1276 } 1345 }
1277 con = con->parent; 1346 con = con->pending.parent;
1278 } 1347 }
1279 // Check view isn't hidden by another fullscreen view 1348 // Check view isn't hidden by another fullscreen view
1280 struct sway_container *fs = root->fullscreen_global ? 1349 struct sway_container *fs = root->fullscreen_global ?
@@ -1308,7 +1377,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1308 ipc_event_window(view->container, "urgent"); 1377 ipc_event_window(view->container, "urgent");
1309 1378
1310 if (!container_is_scratchpad_hidden(view->container)) { 1379 if (!container_is_scratchpad_hidden(view->container)) {
1311 workspace_detect_urgent(view->container->workspace); 1380 workspace_detect_urgent(view->container->pending.workspace);
1312 } 1381 }
1313} 1382}
1314 1383
@@ -1338,11 +1407,11 @@ static void view_save_buffer_iterator(struct wlr_surface *surface,
1338 saved_buffer->buffer = surface->buffer; 1407 saved_buffer->buffer = surface->buffer;
1339 saved_buffer->width = surface->current.width; 1408 saved_buffer->width = surface->current.width;
1340 saved_buffer->height = surface->current.height; 1409 saved_buffer->height = surface->current.height;
1341 saved_buffer->x = sx; 1410 saved_buffer->x = view->container->surface_x + sx;
1342 saved_buffer->y = sy; 1411 saved_buffer->y = view->container->surface_y + sy;
1343 saved_buffer->transform = surface->current.transform; 1412 saved_buffer->transform = surface->current.transform;
1344 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); 1413 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1345 wl_list_insert(&view->saved_buffers, &saved_buffer->link); 1414 wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
1346 } 1415 }
1347} 1416}
1348 1417
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 921b7d19..c84320bd 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -48,10 +48,10 @@ struct sway_output *workspace_get_initial_output(const char *name) {
48 if (focus && focus->type == N_WORKSPACE) { 48 if (focus && focus->type == N_WORKSPACE) {
49 return focus->sway_workspace->output; 49 return focus->sway_workspace->output;
50 } else if (focus && focus->type == N_CONTAINER) { 50 } else if (focus && focus->type == N_CONTAINER) {
51 return focus->sway_container->workspace->output; 51 return focus->sway_container->pending.workspace->output;
52 } 52 }
53 // Fallback to the first output or noop output for headless 53 // Fallback to the first output or the headless output
54 return root->outputs->length ? root->outputs->items[0] : root->noop_output; 54 return root->outputs->length ? root->outputs->items[0] : root->fallback_output;
55} 55}
56 56
57struct sway_workspace *workspace_create(struct sway_output *output, 57struct sway_workspace *workspace_create(struct sway_output *output,
@@ -222,10 +222,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
222 // not a command about workspaces 222 // not a command about workspaces
223 if (strcmp(_target, "next") == 0 || 223 if (strcmp(_target, "next") == 0 ||
224 strcmp(_target, "prev") == 0 || 224 strcmp(_target, "prev") == 0 ||
225 strncmp(_target, "next_on_output", 225 strcmp(_target, "next_on_output") == 0 ||
226 strlen("next_on_output")) == 0 || 226 strcmp(_target, "prev_on_output") == 0 ||
227 strncmp(_target, "prev_on_output",
228 strlen("next_on_output")) == 0 ||
229 strcmp(_target, "number") == 0 || 227 strcmp(_target, "number") == 0 ||
230 strcmp(_target, "back_and_forth") == 0 || 228 strcmp(_target, "back_and_forth") == 0 ||
231 strcmp(_target, "current") == 0) { 229 strcmp(_target, "current") == 0) {
@@ -363,11 +361,11 @@ struct sway_workspace *workspace_by_name(const char *name) {
363 if (current && strcmp(name, "prev") == 0) { 361 if (current && strcmp(name, "prev") == 0) {
364 return workspace_prev(current); 362 return workspace_prev(current);
365 } else if (current && strcmp(name, "prev_on_output") == 0) { 363 } else if (current && strcmp(name, "prev_on_output") == 0) {
366 return workspace_output_prev(current, false); 364 return workspace_output_prev(current);
367 } else if (current && strcmp(name, "next") == 0) { 365 } else if (current && strcmp(name, "next") == 0) {
368 return workspace_next(current); 366 return workspace_next(current);
369 } else if (current && strcmp(name, "next_on_output") == 0) { 367 } else if (current && strcmp(name, "next_on_output") == 0) {
370 return workspace_output_next(current, false); 368 return workspace_output_next(current);
371 } else if (strcmp(name, "current") == 0) { 369 } else if (strcmp(name, "current") == 0) {
372 return current; 370 return current;
373 } else if (strcasecmp(name, "back_and_forth") == 0) { 371 } else if (strcasecmp(name, "back_and_forth") == 0) {
@@ -530,7 +528,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) {
530 * otherwise the next one is returned. 528 * otherwise the next one is returned.
531 */ 529 */
532static struct sway_workspace *workspace_output_prev_next_impl( 530static struct sway_workspace *workspace_output_prev_next_impl(
533 struct sway_output *output, int dir, bool create) { 531 struct sway_output *output, int dir) {
534 struct sway_seat *seat = input_manager_current_seat(); 532 struct sway_seat *seat = input_manager_current_seat();
535 struct sway_workspace *workspace = seat_get_focused_workspace(seat); 533 struct sway_workspace *workspace = seat_get_focused_workspace(seat);
536 if (!workspace) { 534 if (!workspace) {
@@ -540,46 +538,43 @@ static struct sway_workspace *workspace_output_prev_next_impl(
540 } 538 }
541 539
542 int index = list_find(output->workspaces, workspace); 540 int index = list_find(output->workspaces, workspace);
543 if (!workspace_is_empty(workspace) && create &&
544 (index + dir < 0 || index + dir == output->workspaces->length)) {
545 struct sway_output *output = workspace->output;
546 char *next = workspace_next_name(output->wlr_output->name);
547 workspace_create(output, next);
548 free(next);
549 }
550 size_t new_index = wrap(index + dir, output->workspaces->length); 541 size_t new_index = wrap(index + dir, output->workspaces->length);
551 return output->workspaces->items[new_index]; 542 return output->workspaces->items[new_index];
552} 543}
553 544
554struct sway_workspace *workspace_output_next( 545
555 struct sway_workspace *current, bool create) { 546struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
556 return workspace_output_prev_next_impl(current->output, 1, create); 547 return workspace_output_prev_next_impl(current->output, 1);
557} 548}
558 549
559struct sway_workspace *workspace_output_prev( 550struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
560 struct sway_workspace *current, bool create) { 551 return workspace_output_prev_next_impl(current->output, -1);
561 return workspace_output_prev_next_impl(current->output, -1, create);
562} 552}
563 553
564bool workspace_switch(struct sway_workspace *workspace, 554struct sway_workspace *workspace_auto_back_and_forth(
565 bool no_auto_back_and_forth) { 555 struct sway_workspace *workspace) {
566 struct sway_seat *seat = input_manager_current_seat(); 556 struct sway_seat *seat = input_manager_current_seat();
567 struct sway_workspace *active_ws = NULL; 557 struct sway_workspace *active_ws = NULL;
568 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); 558 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
569 if (focus && focus->type == N_WORKSPACE) { 559 if (focus && focus->type == N_WORKSPACE) {
570 active_ws = focus->sway_workspace; 560 active_ws = focus->sway_workspace;
571 } else if (focus && focus->type == N_CONTAINER) { 561 } else if (focus && focus->type == N_CONTAINER) {
572 active_ws = focus->sway_container->workspace; 562 active_ws = focus->sway_container->pending.workspace;
573 } 563 }
574 564
575 if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws 565 if (config->auto_back_and_forth && active_ws && active_ws == workspace &&
576 && active_ws == workspace && seat->prev_workspace_name) { 566 seat->prev_workspace_name) {
577 struct sway_workspace *new_ws = 567 struct sway_workspace *new_ws =
578 workspace_by_name(seat->prev_workspace_name); 568 workspace_by_name(seat->prev_workspace_name);
579 workspace = new_ws ? 569 workspace = new_ws ?
580 new_ws : 570 new_ws :
581 workspace_create(NULL, seat->prev_workspace_name); 571 workspace_create(NULL, seat->prev_workspace_name);
582 } 572 }
573 return workspace;
574}
575
576bool workspace_switch(struct sway_workspace *workspace) {
577 struct sway_seat *seat = input_manager_current_seat();
583 578
584 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", 579 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
585 workspace, workspace->name); 580 workspace, workspace->name);
@@ -736,13 +731,13 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws,
736} 731}
737 732
738static void set_workspace(struct sway_container *container, void *data) { 733static void set_workspace(struct sway_container *container, void *data) {
739 container->workspace = container->parent->workspace; 734 container->pending.workspace = container->pending.parent->pending.workspace;
740} 735}
741 736
742static void workspace_attach_tiling(struct sway_workspace *ws, 737static void workspace_attach_tiling(struct sway_workspace *ws,
743 struct sway_container *con) { 738 struct sway_container *con) {
744 list_add(ws->tiling, con); 739 list_add(ws->tiling, con);
745 con->workspace = ws; 740 con->pending.workspace = ws;
746 container_for_each_child(con, set_workspace, NULL); 741 container_for_each_child(con, set_workspace, NULL);
747 container_handle_fullscreen_reparent(con); 742 container_handle_fullscreen_reparent(con);
748 workspace_update_representation(ws); 743 workspace_update_representation(ws);
@@ -753,7 +748,7 @@ static void workspace_attach_tiling(struct sway_workspace *ws,
753struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { 748struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
754 struct sway_container *fs = ws->fullscreen; 749 struct sway_container *fs = ws->fullscreen;
755 struct sway_container *middle = container_create(NULL); 750 struct sway_container *middle = container_create(NULL);
756 middle->layout = ws->layout; 751 middle->pending.layout = ws->layout;
757 while (ws->tiling->length) { 752 while (ws->tiling->length) {
758 struct sway_container *child = ws->tiling->items[0]; 753 struct sway_container *child = ws->tiling->items[0];
759 container_detach(child); 754 container_detach(child);
@@ -771,9 +766,9 @@ void workspace_unwrap_children(struct sway_workspace *ws,
771 return; 766 return;
772 } 767 }
773 768
774 ws->layout = wrap->layout; 769 ws->layout = wrap->pending.layout;
775 while (wrap->children->length) { 770 while (wrap->pending.children->length) {
776 struct sway_container *child = wrap->children->items[0]; 771 struct sway_container *child = wrap->pending.children->items[0];
777 container_detach(child); 772 container_detach(child);
778 workspace_add_tiling(ws, child); 773 workspace_add_tiling(ws, child);
779 } 774 }
@@ -793,14 +788,18 @@ void workspace_detach(struct sway_workspace *workspace) {
793 788
794struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, 789struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
795 struct sway_container *con) { 790 struct sway_container *con) {
796 if (con->workspace) { 791 if (con->pending.workspace) {
792 struct sway_container *old_parent = con->pending.parent;
797 container_detach(con); 793 container_detach(con);
794 if (old_parent) {
795 container_reap_empty(old_parent);
796 }
798 } 797 }
799 if (config->default_layout != L_NONE) { 798 if (config->default_layout != L_NONE) {
800 con = container_split(con, config->default_layout); 799 con = container_split(con, config->default_layout);
801 } 800 }
802 list_add(workspace->tiling, con); 801 list_add(workspace->tiling, con);
803 con->workspace = workspace; 802 con->pending.workspace = workspace;
804 container_for_each_child(con, set_workspace, NULL); 803 container_for_each_child(con, set_workspace, NULL);
805 container_handle_fullscreen_reparent(con); 804 container_handle_fullscreen_reparent(con);
806 workspace_update_representation(workspace); 805 workspace_update_representation(workspace);
@@ -811,11 +810,11 @@ struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
811 810
812void workspace_add_floating(struct sway_workspace *workspace, 811void workspace_add_floating(struct sway_workspace *workspace,
813 struct sway_container *con) { 812 struct sway_container *con) {
814 if (con->workspace) { 813 if (con->pending.workspace) {
815 container_detach(con); 814 container_detach(con);
816 } 815 }
817 list_add(workspace->floating, con); 816 list_add(workspace->floating, con);
818 con->workspace = workspace; 817 con->pending.workspace = workspace;
819 container_for_each_child(con, set_workspace, NULL); 818 container_for_each_child(con, set_workspace, NULL);
820 container_handle_fullscreen_reparent(con); 819 container_handle_fullscreen_reparent(con);
821 node_set_dirty(&workspace->node); 820 node_set_dirty(&workspace->node);
@@ -825,7 +824,7 @@ void workspace_add_floating(struct sway_workspace *workspace,
825void workspace_insert_tiling_direct(struct sway_workspace *workspace, 824void workspace_insert_tiling_direct(struct sway_workspace *workspace,
826 struct sway_container *con, int index) { 825 struct sway_container *con, int index) {
827 list_insert(workspace->tiling, index, con); 826 list_insert(workspace->tiling, index, con);
828 con->workspace = workspace; 827 con->pending.workspace = workspace;
829 container_for_each_child(con, set_workspace, NULL); 828 container_for_each_child(con, set_workspace, NULL);
830 container_handle_fullscreen_reparent(con); 829 container_handle_fullscreen_reparent(con);
831 workspace_update_representation(workspace); 830 workspace_update_representation(workspace);
@@ -835,7 +834,7 @@ void workspace_insert_tiling_direct(struct sway_workspace *workspace,
835 834
836struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, 835struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
837 struct sway_container *con, int index) { 836 struct sway_container *con, int index) {
838 if (con->workspace) { 837 if (con->pending.workspace) {
839 container_detach(con); 838 container_detach(con);
840 } 839 }
841 if (config->default_layout != L_NONE) { 840 if (config->default_layout != L_NONE) {
@@ -845,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
845 return con; 844 return con;
846} 845}
847 846
847bool workspace_has_single_visible_container(struct sway_workspace *ws) {
848 struct sway_seat *seat = input_manager_get_default_seat();
849 struct sway_container *focus =
850 seat_get_focus_inactive_tiling(seat, ws);
851 if (focus && !focus->view) {
852 focus = seat_get_focus_inactive_view(seat, &focus->node);
853 }
854 return (focus && focus->view && view_ancestor_is_only_visible(focus->view));
855}
856
848void workspace_add_gaps(struct sway_workspace *ws) { 857void workspace_add_gaps(struct sway_workspace *ws) {
849 if (config->smart_gaps) { 858 if (config->smart_gaps == SMART_GAPS_ON
850 struct sway_seat *seat = input_manager_get_default_seat(); 859 && workspace_has_single_visible_container(ws)) {
851 struct sway_container *focus = 860 ws->current_gaps.top = 0;
852 seat_get_focus_inactive_tiling(seat, ws); 861 ws->current_gaps.right = 0;
853 if (focus && !focus->view) { 862 ws->current_gaps.bottom = 0;
854 focus = seat_get_focus_inactive_view(seat, &focus->node); 863 ws->current_gaps.left = 0;
855 } 864 return;
856 if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { 865 }
857 ws->current_gaps.top = 0; 866
858 ws->current_gaps.right = 0; 867 if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER
859 ws->current_gaps.bottom = 0; 868 && !workspace_has_single_visible_container(ws)) {
860 ws->current_gaps.left = 0; 869 ws->current_gaps.top = 0;
861 return; 870 ws->current_gaps.right = 0;
862 } 871 ws->current_gaps.bottom = 0;
872 ws->current_gaps.left = 0;
873 } else {
874 ws->current_gaps = ws->gaps_outer;
863 } 875 }
864 876
865 ws->current_gaps = ws->gaps_outer;
866 // Add inner gaps and make sure we don't turn out negative 877 // Add inner gaps and make sure we don't turn out negative
867 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); 878 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);
868 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); 879 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);
@@ -905,7 +916,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace,
905 enum sway_container_layout old_layout = workspace->layout; 916 enum sway_container_layout old_layout = workspace->layout;
906 struct sway_container *middle = workspace_wrap_children(workspace); 917 struct sway_container *middle = workspace_wrap_children(workspace);
907 workspace->layout = layout; 918 workspace->layout = layout;
908 middle->layout = old_layout; 919 middle->pending.layout = old_layout;
909 920
910 struct sway_seat *seat; 921 struct sway_seat *seat;
911 wl_list_for_each(seat, &server.input->seats, link) { 922 wl_list_for_each(seat, &server.input->seats, link) {
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c
new file mode 100644
index 00000000..6c70c785
--- /dev/null
+++ b/sway/xdg_activation_v1.c
@@ -0,0 +1,20 @@
1#include <wlr/types/wlr_xdg_activation_v1.h>
2#include "sway/tree/view.h"
3
4void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
5 void *data) {
6 const struct wlr_xdg_activation_v1_request_activate_event *event = data;
7
8 if (!wlr_surface_is_xdg_surface(event->surface)) {
9 return;
10 }
11
12 struct wlr_xdg_surface *xdg_surface =
13 wlr_xdg_surface_from_wlr_surface(event->surface);
14 struct sway_view *view = xdg_surface->data;
15 if (!xdg_surface->mapped || view == NULL) {
16 return;
17 }
18
19 view_request_activate(view);
20}
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c
index e7c3ea73..ec9e8d68 100644
--- a/sway/xdg_decoration.c
+++ b/sway/xdg_decoration.c
@@ -10,7 +10,7 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener,
10 void *data) { 10 void *data) {
11 struct sway_xdg_decoration *deco = 11 struct sway_xdg_decoration *deco =
12 wl_container_of(listener, deco, destroy); 12 wl_container_of(listener, deco, destroy);
13 if(deco->view) { 13 if (deco->view) {
14 deco->view->xdg_decoration = NULL; 14 deco->view->xdg_decoration = NULL;
15 } 15 }
16 wl_list_remove(&deco->destroy.link); 16 wl_list_remove(&deco->destroy.link);
@@ -23,8 +23,32 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener,
23 void *data) { 23 void *data) {
24 struct sway_xdg_decoration *deco = 24 struct sway_xdg_decoration *deco =
25 wl_container_of(listener, deco, request_mode); 25 wl_container_of(listener, deco, request_mode);
26 struct sway_view *view = deco->view;
27 enum wlr_xdg_toplevel_decoration_v1_mode mode =
28 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
29 enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
30 deco->wlr_xdg_decoration->requested_mode;
31
32 bool floating;
33 if (view->container) {
34 floating = container_is_floating(view->container);
35 bool csd = false;
36 csd = client_mode ==
37 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
38 view_update_csd_from_client(view, csd);
39 arrange_container(view->container);
40 transaction_commit_dirty();
41 } else {
42 floating = view->impl->wants_floating &&
43 view->impl->wants_floating(view);
44 }
45
46 if (floating && client_mode) {
47 mode = client_mode;
48 }
49
26 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, 50 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
27 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); 51 mode);
28} 52}
29 53
30void handle_xdg_decoration(struct wl_listener *listener, void *data) { 54void handle_xdg_decoration(struct wl_listener *listener, void *data) {
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 231c1ad7..6ffdc9b4 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -54,7 +54,6 @@ static void swaybar_output_free(struct swaybar_output *output) {
54 if (output->input_region != NULL) { 54 if (output->input_region != NULL) {
55 wl_region_destroy(output->input_region); 55 wl_region_destroy(output->input_region);
56 } 56 }
57 zxdg_output_v1_destroy(output->xdg_output);
58 wl_output_destroy(output->output); 57 wl_output_destroy(output->output);
59 destroy_buffer(&output->buffers[0]); 58 destroy_buffer(&output->buffers[0]);
60 destroy_buffer(&output->buffers[1]); 59 destroy_buffer(&output->buffers[1]);
@@ -90,7 +89,7 @@ static void layer_surface_closed(void *_output,
90 swaybar_output_free(output); 89 swaybar_output_free(output);
91} 90}
92 91
93struct zwlr_layer_surface_v1_listener layer_surface_listener = { 92static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
94 .configure = layer_surface_configure, 93 .configure = layer_surface_configure,
95 .closed = layer_surface_closed, 94 .closed = layer_surface_closed,
96}; 95};
@@ -172,7 +171,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
172 if (bar->status) { 171 if (bar->status) {
173 sway_log(SWAY_DEBUG, "Sending %s signal to status command", 172 sway_log(SWAY_DEBUG, "Sending %s signal to status command",
174 visible ? "cont" : "stop"); 173 visible ? "cont" : "stop");
175 kill(bar->status->pid, visible ? 174 kill(-bar->status->pid, visible ?
176 bar->status->cont_signal : bar->status->stop_signal); 175 bar->status->cont_signal : bar->status->stop_signal);
177 } 176 }
178 } 177 }
@@ -230,7 +229,7 @@ static void output_scale(void *data, struct wl_output *wl_output,
230 } 229 }
231} 230}
232 231
233struct wl_output_listener output_listener = { 232static const struct wl_output_listener output_listener = {
234 .geometry = output_geometry, 233 .geometry = output_geometry,
235 .mode = output_mode, 234 .mode = output_mode,
236 .done = output_done, 235 .done = output_done,
@@ -307,7 +306,7 @@ static void xdg_output_handle_description(void *data,
307 } 306 }
308} 307}
309 308
310struct zxdg_output_v1_listener xdg_output_listener = { 309static const struct zxdg_output_v1_listener xdg_output_listener = {
311 .logical_position = xdg_output_handle_logical_position, 310 .logical_position = xdg_output_handle_logical_position,
312 .logical_size = xdg_output_handle_logical_size, 311 .logical_size = xdg_output_handle_logical_size,
313 .done = xdg_output_handle_done, 312 .done = xdg_output_handle_done,
@@ -461,13 +460,28 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
461 460
462static void display_in(int fd, short mask, void *data) { 461static void display_in(int fd, short mask, void *data) {
463 struct swaybar *bar = data; 462 struct swaybar *bar = data;
463 if (mask & (POLLHUP | POLLERR)) {
464 if (mask & POLLERR) {
465 sway_log(SWAY_ERROR, "Wayland display poll error");
466 }
467 bar->running = false;
468 return;
469 }
464 if (wl_display_dispatch(bar->display) == -1) { 470 if (wl_display_dispatch(bar->display) == -1) {
471 sway_log(SWAY_ERROR, "wl_display_dispatch failed");
465 bar->running = false; 472 bar->running = false;
466 } 473 }
467} 474}
468 475
469static void ipc_in(int fd, short mask, void *data) { 476static void ipc_in(int fd, short mask, void *data) {
470 struct swaybar *bar = data; 477 struct swaybar *bar = data;
478 if (mask & (POLLHUP | POLLERR)) {
479 if (mask & POLLERR) {
480 sway_log(SWAY_ERROR, "IPC poll error");
481 }
482 bar->running = false;
483 return;
484 }
471 if (handle_ipc_readable(bar)) { 485 if (handle_ipc_readable(bar)) {
472 set_bar_dirty(bar); 486 set_bar_dirty(bar);
473 } 487 }
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 4bcd5843..6d00befb 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -28,6 +28,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
28 } 28 }
29} 29}
30 30
31static bool i3bar_parse_json_color(json_object *json, uint32_t *color) {
32 if (!json) {
33 return false;
34 }
35
36 const char *hexstring = json_object_get_string(json);
37 bool color_set = parse_color(hexstring, color);
38 if (!color_set) {
39 sway_log(SWAY_ERROR, "Ignoring invalid block hexadecimal color string: %s", hexstring);
40 }
41 return color_set;
42}
43
31static void i3bar_parse_json(struct status_line *status, 44static void i3bar_parse_json(struct status_line *status,
32 struct json_object *json_array) { 45 struct json_object *json_array) {
33 struct i3bar_block *block, *tmp; 46 struct i3bar_block *block, *tmp;
@@ -68,13 +81,7 @@ static void i3bar_parse_json(struct status_line *status,
68 strdup(json_object_get_string(full_text)) : NULL; 81 strdup(json_object_get_string(full_text)) : NULL;
69 block->short_text = short_text ? 82 block->short_text = short_text ?
70 strdup(json_object_get_string(short_text)) : NULL; 83 strdup(json_object_get_string(short_text)) : NULL;
71 if (color) { 84 block->color_set = i3bar_parse_json_color(color, &block->color);
72 const char *hexstring = json_object_get_string(color);
73 block->color_set = parse_color(hexstring, &block->color);
74 if (!block->color_set) {
75 sway_log(SWAY_ERROR, "Invalid block color: %s", hexstring);
76 }
77 }
78 if (min_width) { 85 if (min_width) {
79 json_type type = json_object_get_type(min_width); 86 json_type type = json_object_get_type(min_width);
80 if (type == json_type_int) { 87 if (type == json_type_int) {
@@ -100,14 +107,8 @@ static void i3bar_parse_json(struct status_line *status,
100 block->separator_block_width = separator_block_width ? 107 block->separator_block_width = separator_block_width ?
101 json_object_get_int(separator_block_width) : 9; 108 json_object_get_int(separator_block_width) : 9;
102 // Airblader features 109 // Airblader features
103 const char *hex = background ? json_object_get_string(background) : NULL; 110 i3bar_parse_json_color(background, &block->background);
104 if (hex && !parse_color(hex, &block->background)) { 111 block->border_set = i3bar_parse_json_color(border, &block->border);
105 sway_log(SWAY_ERROR, "Ignoring invalid block background: %s", hex);
106 }
107 hex = border ? json_object_get_string(border) : NULL;
108 if (hex && !parse_color(hex, &block->border)) {
109 sway_log(SWAY_ERROR, "Ignoring invalid block border: %s", hex);
110 }
111 block->border_top = border_top ? json_object_get_int(border_top) : 1; 112 block->border_top = border_top ? json_object_get_int(border_top) : 1;
112 block->border_bottom = border_bottom ? 113 block->border_bottom = border_bottom ?
113 json_object_get_int(border_bottom) : 1; 114 json_object_get_int(border_bottom) : 1;
diff --git a/swaybar/input.c b/swaybar/input.c
index 4fe6dd93..c8c8f0d4 100644
--- a/swaybar/input.c
+++ b/swaybar/input.c
@@ -101,6 +101,8 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
101 wl_fixed_t surface_x, wl_fixed_t surface_y) { 101 wl_fixed_t surface_x, wl_fixed_t surface_y) {
102 struct swaybar_seat *seat = data; 102 struct swaybar_seat *seat = data;
103 struct swaybar_pointer *pointer = &seat->pointer; 103 struct swaybar_pointer *pointer = &seat->pointer;
104 seat->pointer.x = wl_fixed_to_double(surface_x);
105 seat->pointer.y = wl_fixed_to_double(surface_y);
104 pointer->serial = serial; 106 pointer->serial = serial;
105 struct swaybar_output *output; 107 struct swaybar_output *output;
106 wl_list_for_each(output, &seat->bar->outputs, link) { 108 wl_list_for_each(output, &seat->bar->outputs, link) {
@@ -140,13 +142,11 @@ static bool check_bindings(struct swaybar *bar, uint32_t button,
140 142
141static bool process_hotspots(struct swaybar_output *output, 143static bool process_hotspots(struct swaybar_output *output,
142 double x, double y, uint32_t button) { 144 double x, double y, uint32_t button) {
143 double px = x * output->scale;
144 double py = y * output->scale;
145 struct swaybar_hotspot *hotspot; 145 struct swaybar_hotspot *hotspot;
146 wl_list_for_each(hotspot, &output->hotspots, link) { 146 wl_list_for_each(hotspot, &output->hotspots, link) {
147 if (px >= hotspot->x && py >= hotspot->y 147 if (x >= hotspot->x && y >= hotspot->y
148 && px < hotspot->x + hotspot->width 148 && x < hotspot->x + hotspot->width
149 && py < hotspot->y + hotspot->height) { 149 && y < hotspot->y + hotspot->height) {
150 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 150 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
151 button, hotspot->data)) { 151 button, hotspot->data)) {
152 return true; 152 return true;
@@ -339,7 +339,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
339 seat->axis[axis].discrete_steps += abs(discrete); 339 seat->axis[axis].discrete_steps += abs(discrete);
340} 340}
341 341
342static struct wl_pointer_listener pointer_listener = { 342static const struct wl_pointer_listener pointer_listener = {
343 .enter = wl_pointer_enter, 343 .enter = wl_pointer_enter,
344 .leave = wl_pointer_leave, 344 .leave = wl_pointer_leave,
345 .motion = wl_pointer_motion, 345 .motion = wl_pointer_motion,
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 6bbe9408..2cb235bf 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -547,9 +547,23 @@ bool handle_ipc_readable(struct swaybar *bar) {
547 return false; 547 return false;
548 } 548 }
549 549
550 json_object *result = json_tokener_parse(resp->payload); 550 // The default depth of 32 is too small to represent some nested layouts, but
551 if (!result) { 551 // we can't pass INT_MAX here because json-c (as of this writing) prefaults
552 sway_log(SWAY_ERROR, "failed to parse payload as json"); 552 // all the memory for its stack.
553 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
554 if (!tok) {
555 sway_log_errno(SWAY_ERROR, "failed to create tokener");
556 free_ipc_response(resp);
557 return false;
558 }
559
560 json_object *result = json_tokener_parse_ex(tok, resp->payload, -1);
561 enum json_tokener_error err = json_tokener_get_error(tok);
562 json_tokener_free(tok);
563
564 if (err != json_tokener_success) {
565 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
566 json_tokener_error_desc(err));
553 free_ipc_response(resp); 567 free_ipc_response(resp);
554 return false; 568 return false;
555 } 569 }
diff --git a/swaybar/main.c b/swaybar/main.c
index 5c36d66b..a44c1e63 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -18,7 +18,7 @@ int main(int argc, char **argv) {
18 char *socket_path = NULL; 18 char *socket_path = NULL;
19 bool debug = false; 19 bool debug = false;
20 20
21 static struct option long_options[] = { 21 static const struct option long_options[] = {
22 {"help", no_argument, NULL, 'h'}, 22 {"help", no_argument, NULL, 'h'},
23 {"version", no_argument, NULL, 'v'}, 23 {"version", no_argument, NULL, 'v'},
24 {"socket", required_argument, NULL, 's'}, 24 {"socket", required_argument, NULL, 's'},
diff --git a/swaybar/render.c b/swaybar/render.c
index df066622..dcde6b9e 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -5,7 +5,7 @@
5#include <stdlib.h> 5#include <stdlib.h>
6#include <stdint.h> 6#include <stdint.h>
7#include <string.h> 7#include <string.h>
8#include "cairo.h" 8#include "cairo_util.h"
9#include "pango.h" 9#include "pango.h"
10#include "pool-buffer.h" 10#include "pool-buffer.h"
11#include "swaybar/bar.h" 11#include "swaybar/bar.h"
@@ -14,6 +14,7 @@
14#include "swaybar/ipc.h" 14#include "swaybar/ipc.h"
15#include "swaybar/render.h" 15#include "swaybar/render.h"
16#include "swaybar/status_line.h" 16#include "swaybar/status_line.h"
17#include "log.h"
17#if HAVE_TRAY 18#if HAVE_TRAY
18#include "swaybar/tray/tray.h" 19#include "swaybar/tray/tray.h"
19#endif 20#endif
@@ -23,28 +24,51 @@ static const int WS_HORIZONTAL_PADDING = 5;
23static const double WS_VERTICAL_PADDING = 1.5; 24static const double WS_VERTICAL_PADDING = 1.5;
24static const double BORDER_WIDTH = 1; 25static const double BORDER_WIDTH = 1;
25 26
26static uint32_t render_status_line_error(cairo_t *cairo, 27struct render_context {
27 struct swaybar_output *output, double *x) { 28 cairo_t *cairo;
29 struct swaybar_output *output;
30 cairo_font_options_t *textaa_sharp;
31 cairo_font_options_t *textaa_safe;
32 uint32_t background_color;
33};
34
35static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {
36 uint32_t salpha = fontcolor & 0xFF;
37 uint32_t balpha = ctx->background_color & 0xFF;
38
39 // Subpixel antialiasing requires blend be done in cairo, not compositor
40 cairo_font_options_t *fo = salpha == balpha ?
41 ctx->textaa_sharp : ctx->textaa_safe;
42 cairo_set_font_options(ctx->cairo, fo);
43
44 // Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE'
45 cairo_operator_t op = salpha == 0xFF ?
46 CAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE;
47 cairo_set_operator(ctx->cairo, op);
48}
49
50static uint32_t render_status_line_error(struct render_context *ctx, double *x) {
51 struct swaybar_output *output = ctx->output;
28 const char *error = output->bar->status->text; 52 const char *error = output->bar->status->text;
29 if (!error) { 53 if (!error) {
30 return 0; 54 return 0;
31 } 55 }
32 56
33 uint32_t height = output->height * output->scale; 57 uint32_t height = output->height;
34 58
59 cairo_t *cairo = ctx->cairo;
35 cairo_set_source_u32(cairo, 0xFF0000FF); 60 cairo_set_source_u32(cairo, 0xFF0000FF);
36 61
37 int margin = 3 * output->scale; 62 int margin = 3;
38 double ws_vertical_padding = 63 double ws_vertical_padding = output->bar->config->status_padding;
39 output->bar->config->status_padding * output->scale;
40 64
41 char *font = output->bar->config->font; 65 char *font = output->bar->config->font;
42 int text_width, text_height; 66 int text_width, text_height;
43 get_text_size(cairo, font, &text_width, &text_height, NULL, 67 get_text_size(cairo, font, &text_width, &text_height, NULL,
44 output->scale, false, "%s", error); 68 1, false, "%s", error);
45 69
46 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 70 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
47 uint32_t ideal_surface_height = ideal_height / output->scale; 71 uint32_t ideal_surface_height = ideal_height;
48 if (!output->bar->config->height && 72 if (!output->bar->config->height &&
49 output->height < ideal_surface_height) { 73 output->height < ideal_surface_height) {
50 return ideal_surface_height; 74 return ideal_surface_height;
@@ -53,42 +77,45 @@ static uint32_t render_status_line_error(cairo_t *cairo,
53 77
54 double text_y = height / 2.0 - text_height / 2.0; 78 double text_y = height / 2.0 - text_height / 2.0;
55 cairo_move_to(cairo, *x, (int)floor(text_y)); 79 cairo_move_to(cairo, *x, (int)floor(text_y));
56 pango_printf(cairo, font, output->scale, false, "%s", error); 80 choose_text_aa_mode(ctx, 0xFF0000FF);
81 render_text(cairo, font, 1, false, "%s", error);
57 *x -= margin; 82 *x -= margin;
58 return output->height; 83 return output->height;
59} 84}
60 85
61static uint32_t render_status_line_text(cairo_t *cairo, 86static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
62 struct swaybar_output *output, double *x) { 87 struct swaybar_output *output = ctx->output;
63 const char *text = output->bar->status->text; 88 const char *text = output->bar->status->text;
64 if (!text) { 89 if (!text) {
65 return 0; 90 return 0;
66 } 91 }
67 92
93 cairo_t *cairo = ctx->cairo;
68 struct swaybar_config *config = output->bar->config; 94 struct swaybar_config *config = output->bar->config;
69 cairo_set_source_u32(cairo, output->focused ? 95 uint32_t fontcolor = output->focused ?
70 config->colors.focused_statusline : config->colors.statusline); 96 config->colors.focused_statusline : config->colors.statusline;
97 cairo_set_source_u32(cairo, fontcolor);
71 98
72 int text_width, text_height; 99 int text_width, text_height;
73 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 100 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
74 output->scale, config->pango_markup, "%s", text); 101 1, config->pango_markup, "%s", text);
75 102
76 double ws_vertical_padding = config->status_padding * output->scale; 103 double ws_vertical_padding = config->status_padding;
77 int margin = 3 * output->scale; 104 int margin = 3;
78 105
79 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 106 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
80 uint32_t ideal_surface_height = ideal_height / output->scale; 107 uint32_t ideal_surface_height = ideal_height;
81 if (!output->bar->config->height && 108 if (!output->bar->config->height &&
82 output->height < ideal_surface_height) { 109 output->height < ideal_surface_height) {
83 return ideal_surface_height; 110 return ideal_surface_height;
84 } 111 }
85 112
86 *x -= text_width + margin; 113 *x -= text_width + margin;
87 uint32_t height = output->height * output->scale; 114 uint32_t height = output->height;
88 double text_y = height / 2.0 - text_height / 2.0; 115 double text_y = height / 2.0 - text_height / 2.0;
89 cairo_move_to(cairo, *x, (int)floor(text_y)); 116 cairo_move_to(cairo, *x, (int)floor(text_y));
90 pango_printf(cairo, config->font, output->scale, 117 choose_text_aa_mode(ctx, fontcolor);
91 config->pango_markup, "%s", text); 118 render_text(cairo, config->font, 1, config->pango_markup, "%s", text);
92 *x -= margin; 119 *x -= margin;
93 return output->height; 120 return output->height;
94} 121}
@@ -96,6 +123,7 @@ static uint32_t render_status_line_text(cairo_t *cairo,
96static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, 123static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
97 double x, double y, double width, double height) { 124 double x, double y, double width, double height) {
98 cairo_save(cairo); 125 cairo_save(cairo);
126 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
99 cairo_set_source_u32(cairo, color); 127 cairo_set_source_u32(cairo, color);
100 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 128 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
101 cairo_rectangle(cairo, x, y, width, height); 129 cairo_rectangle(cairo, x, y, width, height);
@@ -109,6 +137,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
109 render_sharp_rectangle(cairo, color, x, y, width, height); 137 render_sharp_rectangle(cairo, color, x, y, width, height);
110 } else { 138 } else {
111 cairo_save(cairo); 139 cairo_save(cairo);
140 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
112 cairo_set_source_u32(cairo, color); 141 cairo_set_source_u32(cairo, color);
113 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 142 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
114 if (width == 1) { 143 if (width == 1) {
@@ -135,10 +164,10 @@ static enum hotspot_event_handling block_hotspot_callback(
135 struct i3bar_block *block = data; 164 struct i3bar_block *block = data;
136 struct status_line *status = output->bar->status; 165 struct status_line *status = output->bar->status;
137 return i3bar_block_send_click(status, block, x, y, 166 return i3bar_block_send_click(status, block, x, y,
138 x - (double)hotspot->x / output->scale, 167 x - (double)hotspot->x,
139 y - (double)hotspot->y / output->scale, 168 y - (double)hotspot->y,
140 (double)hotspot->width / output->scale, 169 (double)hotspot->width,
141 (double)hotspot->height / output->scale, 170 (double)hotspot->height,
142 output->scale, button); 171 output->scale, button);
143} 172}
144 173
@@ -146,9 +175,8 @@ static void i3bar_block_unref_callback(void *data) {
146 i3bar_block_unref(data); 175 i3bar_block_unref(data);
147} 176}
148 177
149static uint32_t render_status_block(cairo_t *cairo, 178static uint32_t render_status_block(struct render_context *ctx,
150 struct swaybar_output *output, struct i3bar_block *block, double *x, 179 struct i3bar_block *block, double *x, bool edge, bool use_short_text) {
151 bool edge, bool use_short_text) {
152 if (!block->full_text || !*block->full_text) { 180 if (!block->full_text || !*block->full_text) {
153 return 0; 181 return 0;
154 } 182 }
@@ -158,20 +186,21 @@ static uint32_t render_status_block(cairo_t *cairo,
158 text = block->short_text; 186 text = block->short_text;
159 } 187 }
160 188
189 cairo_t *cairo = ctx->cairo;
190 struct swaybar_output *output = ctx->output;
161 struct swaybar_config *config = output->bar->config; 191 struct swaybar_config *config = output->bar->config;
162
163 int text_width, text_height; 192 int text_width, text_height;
164 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 193 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1,
165 output->scale, block->markup, "%s", text); 194 block->markup, "%s", text);
166 195
167 int margin = 3 * output->scale; 196 int margin = 3;
168 double ws_vertical_padding = config->status_padding * output->scale; 197 double ws_vertical_padding = config->status_padding;
169 198
170 int width = text_width; 199 int width = text_width;
171 if (block->min_width_str) { 200 if (block->min_width_str) {
172 int w; 201 int w;
173 get_text_size(cairo, config->font, &w, NULL, NULL, 202 get_text_size(cairo, config->font, &w, NULL, NULL, 1, block->markup,
174 output->scale, block->markup, "%s", block->min_width_str); 203 "%s", block->min_width_str);
175 block->min_width = w; 204 block->min_width = w;
176 } 205 }
177 if (width < block->min_width) { 206 if (width < block->min_width) {
@@ -180,20 +209,20 @@ static uint32_t render_status_block(cairo_t *cairo,
180 209
181 double block_width = width; 210 double block_width = width;
182 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 211 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
183 uint32_t ideal_surface_height = ideal_height / output->scale; 212 uint32_t ideal_surface_height = ideal_height;
184 if (!output->bar->config->height && 213 if (!output->bar->config->height &&
185 output->height < ideal_surface_height) { 214 output->height < ideal_surface_height) {
186 return ideal_surface_height; 215 return ideal_surface_height;
187 } 216 }
188 217
189 *x -= width; 218 *x -= width;
190 if ((block->border || block->urgent) && block->border_left > 0) { 219 if ((block->border_set || block->urgent) && block->border_left > 0) {
191 *x -= (block->border_left * output->scale + margin); 220 *x -= (block->border_left + margin);
192 block_width += block->border_left * output->scale + margin; 221 block_width += block->border_left + margin;
193 } 222 }
194 if ((block->border || block->urgent) && block->border_right > 0) { 223 if ((block->border_set || block->urgent) && block->border_right > 0) {
195 *x -= (block->border_right * output->scale + margin); 224 *x -= (block->border_right + margin);
196 block_width += block->border_right * output->scale + margin; 225 block_width += block->border_right + margin;
197 } 226 }
198 227
199 int sep_width, sep_height; 228 int sep_width, sep_height;
@@ -201,9 +230,9 @@ static uint32_t render_status_block(cairo_t *cairo,
201 if (!edge) { 230 if (!edge) {
202 if (config->sep_symbol) { 231 if (config->sep_symbol) {
203 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 232 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL,
204 output->scale, false, "%s", config->sep_symbol); 233 1, false, "%s", config->sep_symbol);
205 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 234 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
206 uint32_t _ideal_surface_height = _ideal_height / output->scale; 235 uint32_t _ideal_surface_height = _ideal_height;
207 if (!output->bar->config->height && 236 if (!output->bar->config->height &&
208 output->height < _ideal_surface_height) { 237 output->height < _ideal_surface_height) {
209 return _ideal_surface_height; 238 return _ideal_surface_height;
@@ -214,10 +243,10 @@ static uint32_t render_status_block(cairo_t *cairo,
214 } 243 }
215 *x -= sep_block_width; 244 *x -= sep_block_width;
216 } else if (config->status_edge_padding) { 245 } else if (config->status_edge_padding) {
217 *x -= config->status_edge_padding * output->scale; 246 *x -= config->status_edge_padding;
218 } 247 }
219 248
220 uint32_t height = output->height * output->scale; 249 uint32_t height = output->height;
221 if (output->bar->status->click_events) { 250 if (output->bar->status->click_events) {
222 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 251 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
223 hotspot->x = *x; 252 hotspot->x = *x;
@@ -240,23 +269,26 @@ static uint32_t render_status_block(cairo_t *cairo,
240 if (bg_color) { 269 if (bg_color) {
241 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, 270 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
242 block_width, render_height); 271 block_width, render_height);
272 ctx->background_color = bg_color;
243 } 273 }
244 274
245 uint32_t border_color = block->urgent 275 uint32_t border_color = block->urgent
246 ? config->colors.urgent_workspace.border : block->border; 276 ? config->colors.urgent_workspace.border : block->border;
247 if (border_color && block->border_top > 0) { 277 if (block->border_set || block->urgent) {
248 render_sharp_line(cairo, border_color, x_pos, y_pos, 278 if (block->border_top > 0) {
249 block_width, block->border_top * output->scale); 279 render_sharp_line(cairo, border_color, x_pos, y_pos,
250 } 280 block_width, block->border_top);
251 if (border_color && block->border_bottom > 0) { 281 }
252 render_sharp_line(cairo, border_color, x_pos, 282 if (block->border_bottom > 0) {
253 y_pos + render_height - block->border_bottom * output->scale, 283 render_sharp_line(cairo, border_color, x_pos,
254 block_width, block->border_bottom * output->scale); 284 y_pos + render_height - block->border_bottom,
255 } 285 block_width, block->border_bottom);
256 if (border_color && block->border_left > 0) { 286 }
257 render_sharp_line(cairo, border_color, x_pos, y_pos, 287 if (block->border_left > 0) {
258 block->border_left * output->scale, render_height); 288 render_sharp_line(cairo, border_color, x_pos, y_pos,
259 x_pos += block->border_left * output->scale + margin; 289 block->border_left, render_height);
290 }
291 x_pos += block->border_left + margin;
260 } 292 }
261 293
262 double offset = 0; 294 double offset = 0;
@@ -274,30 +306,35 @@ static uint32_t render_status_block(cairo_t *cairo,
274 color = block->color_set ? block->color : color; 306 color = block->color_set ? block->color : color;
275 color = block->urgent ? config->colors.urgent_workspace.text : color; 307 color = block->urgent ? config->colors.urgent_workspace.text : color;
276 cairo_set_source_u32(cairo, color); 308 cairo_set_source_u32(cairo, color);
277 pango_printf(cairo, config->font, output->scale, 309 choose_text_aa_mode(ctx, color);
278 block->markup, "%s", text); 310 render_text(cairo, config->font, 1, block->markup, "%s", text);
279 x_pos += width; 311 x_pos += width;
280 312
281 if (block->border && block->border_right > 0) { 313 if (block->border_set || block->urgent) {
282 x_pos += margin; 314 x_pos += margin;
283 render_sharp_line(cairo, border_color, x_pos, y_pos, 315 if (block->border_right > 0) {
284 block->border_right * output->scale, render_height); 316 render_sharp_line(cairo, border_color, x_pos, y_pos,
285 x_pos += block->border_right * output->scale; 317 block->border_right, render_height);
318 }
319 x_pos += block->border_right;
286 } 320 }
287 321
288 if (!edge && block->separator) { 322 if (!edge && block->separator) {
289 if (output->focused) { 323 if (output->focused) {
290 cairo_set_source_u32(cairo, config->colors.focused_separator); 324 color = config->colors.focused_separator;
291 } else { 325 } else {
292 cairo_set_source_u32(cairo, config->colors.separator); 326 color = config->colors.separator;
293 } 327 }
328 cairo_set_source_u32(cairo, color);
294 if (config->sep_symbol) { 329 if (config->sep_symbol) {
295 offset = x_pos + (sep_block_width - sep_width) / 2; 330 offset = x_pos + (sep_block_width - sep_width) / 2;
296 double sep_y = height / 2.0 - sep_height / 2.0; 331 double sep_y = height / 2.0 - sep_height / 2.0;
297 cairo_move_to(cairo, offset, (int)floor(sep_y)); 332 cairo_move_to(cairo, offset, (int)floor(sep_y));
298 pango_printf(cairo, config->font, output->scale, false, 333 choose_text_aa_mode(ctx, color);
334 render_text(cairo, config->font, 1, false,
299 "%s", config->sep_symbol); 335 "%s", config->sep_symbol);
300 } else { 336 } else {
337 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
301 cairo_set_line_width(cairo, 1); 338 cairo_set_line_width(cairo, 1);
302 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); 339 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
303 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); 340 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
@@ -317,18 +354,18 @@ static void predict_status_block_pos(cairo_t *cairo,
317 struct swaybar_config *config = output->bar->config; 354 struct swaybar_config *config = output->bar->config;
318 355
319 int text_width, text_height; 356 int text_width, text_height;
320 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 357 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1,
321 output->scale, block->markup, "%s", block->full_text); 358 block->markup, "%s", block->full_text);
322 359
323 int margin = 3 * output->scale; 360 int margin = 3;
324 double ws_vertical_padding = config->status_padding * output->scale; 361 double ws_vertical_padding = config->status_padding;
325 362
326 int width = text_width; 363 int width = text_width;
327 364
328 if (block->min_width_str) { 365 if (block->min_width_str) {
329 int w; 366 int w;
330 get_text_size(cairo, config->font, &w, NULL, NULL, 367 get_text_size(cairo, config->font, &w, NULL, NULL,
331 output->scale, block->markup, "%s", block->min_width_str); 368 1, block->markup, "%s", block->min_width_str);
332 block->min_width = w; 369 block->min_width = w;
333 } 370 }
334 if (width < block->min_width) { 371 if (width < block->min_width) {
@@ -336,18 +373,18 @@ static void predict_status_block_pos(cairo_t *cairo,
336 } 373 }
337 374
338 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 375 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
339 uint32_t ideal_surface_height = ideal_height / output->scale; 376 uint32_t ideal_surface_height = ideal_height;
340 if (!output->bar->config->height && 377 if (!output->bar->config->height &&
341 output->height < ideal_surface_height) { 378 output->height < ideal_surface_height) {
342 return; 379 return;
343 } 380 }
344 381
345 *x -= width; 382 *x -= width;
346 if ((block->border || block->urgent) && block->border_left > 0) { 383 if ((block->border_set || block->urgent) && block->border_left > 0) {
347 *x -= (block->border_left * output->scale + margin); 384 *x -= (block->border_left + margin);
348 } 385 }
349 if ((block->border || block->urgent) && block->border_right > 0) { 386 if ((block->border_set || block->urgent) && block->border_right > 0) {
350 *x -= (block->border_right * output->scale + margin); 387 *x -= (block->border_right + margin);
351 } 388 }
352 389
353 int sep_width, sep_height; 390 int sep_width, sep_height;
@@ -355,9 +392,9 @@ static void predict_status_block_pos(cairo_t *cairo,
355 if (!edge) { 392 if (!edge) {
356 if (config->sep_symbol) { 393 if (config->sep_symbol) {
357 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 394 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL,
358 output->scale, false, "%s", config->sep_symbol); 395 1, false, "%s", config->sep_symbol);
359 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 396 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
360 uint32_t _ideal_surface_height = _ideal_height / output->scale; 397 uint32_t _ideal_surface_height = _ideal_height;
361 if (!output->bar->config->height && 398 if (!output->bar->config->height &&
362 output->height < _ideal_surface_height) { 399 output->height < _ideal_surface_height) {
363 return; 400 return;
@@ -368,13 +405,13 @@ static void predict_status_block_pos(cairo_t *cairo,
368 } 405 }
369 *x -= sep_block_width; 406 *x -= sep_block_width;
370 } else if (config->status_edge_padding) { 407 } else if (config->status_edge_padding) {
371 *x -= config->status_edge_padding * output->scale; 408 *x -= config->status_edge_padding;
372 } 409 }
373} 410}
374 411
375static double predict_status_line_pos(cairo_t *cairo, 412static double predict_status_line_pos(cairo_t *cairo,
376 struct swaybar_output *output, double x) { 413 struct swaybar_output *output, double x) {
377 bool edge = x == output->width * output->scale; 414 bool edge = x == output->width;
378 struct i3bar_block *block; 415 struct i3bar_block *block;
379 wl_list_for_each(block, &output->bar->status->blocks, link) { 416 wl_list_for_each(block, &output->bar->status->blocks, link) {
380 predict_status_block_pos(cairo, output, block, &x, edge); 417 predict_status_block_pos(cairo, output, block, &x, edge);
@@ -389,24 +426,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo,
389 struct swaybar_config *config = output->bar->config; 426 struct swaybar_config *config = output->bar->config;
390 427
391 int text_width, text_height; 428 int text_width, text_height;
392 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 429 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1,
393 output->scale, config->pango_markup, "%s", ws->label); 430 config->pango_markup, "%s", ws->label);
394 431
395 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 432 int ws_vertical_padding = WS_VERTICAL_PADDING;
396 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 433 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
397 int border_width = BORDER_WIDTH * output->scale; 434 int border_width = BORDER_WIDTH;
398 435
399 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 436 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
400 + border_width * 2; 437 + border_width * 2;
401 uint32_t ideal_surface_height = ideal_height / output->scale; 438 uint32_t ideal_surface_height = ideal_height;
402 if (!output->bar->config->height && 439 if (!output->bar->config->height &&
403 output->height < ideal_surface_height) { 440 output->height < ideal_surface_height) {
404 return 0; 441 return 0;
405 } 442 }
406 443
407 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 444 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
408 if (width < config->workspace_min_width * output->scale) { 445 if (width < config->workspace_min_width) {
409 width = config->workspace_min_width * output->scale; 446 width = config->workspace_min_width;
410 } 447 }
411 return width; 448 return width;
412} 449}
@@ -438,38 +475,39 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
438 475
439 int text_width, text_height; 476 int text_width, text_height;
440 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 477 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
441 output->scale, output->bar->mode_pango_markup, 478 1, output->bar->mode_pango_markup,
442 "%s", mode); 479 "%s", mode);
443 480
444 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 481 int ws_vertical_padding = WS_VERTICAL_PADDING;
445 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 482 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
446 int border_width = BORDER_WIDTH * output->scale; 483 int border_width = BORDER_WIDTH;
447 484
448 uint32_t ideal_height = text_height + ws_vertical_padding * 2 485 uint32_t ideal_height = text_height + ws_vertical_padding * 2
449 + border_width * 2; 486 + border_width * 2;
450 uint32_t ideal_surface_height = ideal_height / output->scale; 487 uint32_t ideal_surface_height = ideal_height;
451 if (!output->bar->config->height && 488 if (!output->bar->config->height &&
452 output->height < ideal_surface_height) { 489 output->height < ideal_surface_height) {
453 return 0; 490 return 0;
454 } 491 }
455 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 492 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
456 if (width < config->workspace_min_width * output->scale) { 493 if (width < config->workspace_min_width) {
457 width = config->workspace_min_width * output->scale; 494 width = config->workspace_min_width;
458 } 495 }
459 return width; 496 return width;
460} 497}
461 498
462static uint32_t render_status_line_i3bar(cairo_t *cairo, 499static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
463 struct swaybar_output *output, double *x) { 500 struct swaybar_output *output = ctx->output;
464 uint32_t max_height = 0; 501 uint32_t max_height = 0;
465 bool edge = *x == output->width * output->scale; 502 bool edge = *x == output->width;
466 struct i3bar_block *block; 503 struct i3bar_block *block;
467 bool use_short_text = false; 504 bool use_short_text = false;
468 505
506 cairo_t *cairo = ctx->cairo;
469 double reserved_width = 507 double reserved_width =
470 predict_workspace_buttons_length(cairo, output) + 508 predict_workspace_buttons_length(cairo, output) +
471 predict_binding_mode_indicator_length(cairo, output) + 509 predict_binding_mode_indicator_length(cairo, output) +
472 3 * output->scale; // require a bit of space for margin 510 3; // require a bit of space for margin
473 511
474 double predicted_full_pos = 512 double predicted_full_pos =
475 predict_status_line_pos(cairo, output, *x); 513 predict_status_line_pos(cairo, output, *x);
@@ -479,7 +517,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
479 } 517 }
480 518
481 wl_list_for_each(block, &output->bar->status->blocks, link) { 519 wl_list_for_each(block, &output->bar->status->blocks, link) {
482 uint32_t h = render_status_block(cairo, output, block, x, edge, 520 uint32_t h = render_status_block(ctx, block, x, edge,
483 use_short_text); 521 use_short_text);
484 max_height = h > max_height ? h : max_height; 522 max_height = h > max_height ? h : max_height;
485 edge = false; 523 edge = false;
@@ -487,53 +525,56 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
487 return max_height; 525 return max_height;
488} 526}
489 527
490static uint32_t render_status_line(cairo_t *cairo, 528static uint32_t render_status_line(struct render_context *ctx, double *x) {
491 struct swaybar_output *output, double *x) { 529 struct status_line *status = ctx->output->bar->status;
492 struct status_line *status = output->bar->status;
493 switch (status->protocol) { 530 switch (status->protocol) {
494 case PROTOCOL_ERROR: 531 case PROTOCOL_ERROR:
495 return render_status_line_error(cairo, output, x); 532 return render_status_line_error(ctx, x);
496 case PROTOCOL_TEXT: 533 case PROTOCOL_TEXT:
497 return render_status_line_text(cairo, output, x); 534 return render_status_line_text(ctx, x);
498 case PROTOCOL_I3BAR: 535 case PROTOCOL_I3BAR:
499 return render_status_line_i3bar(cairo, output, x); 536 return render_status_line_i3bar(ctx, x);
500 case PROTOCOL_UNDEF: 537 case PROTOCOL_UNDEF:
501 return 0; 538 return 0;
502 } 539 }
503 return 0; 540 return 0;
504} 541}
505 542
506static uint32_t render_binding_mode_indicator(cairo_t *cairo, 543static uint32_t render_binding_mode_indicator(struct render_context *ctx,
507 struct swaybar_output *output, double x) { 544 double x) {
545 struct swaybar_output *output = ctx->output;
508 const char *mode = output->bar->mode; 546 const char *mode = output->bar->mode;
509 if (!mode) { 547 if (!mode) {
510 return 0; 548 return 0;
511 } 549 }
512 550
551 cairo_t *cairo = ctx->cairo;
513 struct swaybar_config *config = output->bar->config; 552 struct swaybar_config *config = output->bar->config;
514 int text_width, text_height; 553 int text_width, text_height;
515 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 554 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
516 output->scale, output->bar->mode_pango_markup, 555 1, output->bar->mode_pango_markup,
517 "%s", mode); 556 "%s", mode);
518 557
519 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 558 int ws_vertical_padding = WS_VERTICAL_PADDING;
520 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 559 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
521 int border_width = BORDER_WIDTH * output->scale; 560 int border_width = BORDER_WIDTH;
522 561
523 uint32_t ideal_height = text_height + ws_vertical_padding * 2 562 uint32_t ideal_height = text_height + ws_vertical_padding * 2
524 + border_width * 2; 563 + border_width * 2;
525 uint32_t ideal_surface_height = ideal_height / output->scale; 564 uint32_t ideal_surface_height = ideal_height;
526 if (!output->bar->config->height && 565 if (!output->bar->config->height &&
527 output->height < ideal_surface_height) { 566 output->height < ideal_surface_height) {
528 return ideal_surface_height; 567 return ideal_surface_height;
529 } 568 }
530 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 569 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
531 if (width < config->workspace_min_width * output->scale) { 570 if (width < config->workspace_min_width) {
532 width = config->workspace_min_width * output->scale; 571 width = config->workspace_min_width;
533 } 572 }
534 573
535 uint32_t height = output->height * output->scale; 574 uint32_t height = output->height;
575 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
536 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 576 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
577 ctx->background_color = config->colors.binding_mode.background;
537 cairo_rectangle(cairo, x, 0, width, height); 578 cairo_rectangle(cairo, x, 0, width, height);
538 cairo_fill(cairo); 579 cairo_fill(cairo);
539 580
@@ -550,8 +591,9 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
550 double text_y = height / 2.0 - text_height / 2.0; 591 double text_y = height / 2.0 - text_height / 2.0;
551 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 592 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
552 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 593 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
553 pango_printf(cairo, config->font, output->scale, 594 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
554 output->bar->mode_pango_markup, "%s", mode); 595 render_text(cairo, config->font, 1, output->bar->mode_pango_markup,
596 "%s", mode);
555 return output->height; 597 return output->height;
556} 598}
557 599
@@ -565,9 +607,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(
565 return HOTSPOT_IGNORE; 607 return HOTSPOT_IGNORE;
566} 608}
567 609
568static uint32_t render_workspace_button(cairo_t *cairo, 610static uint32_t render_workspace_button(struct render_context *ctx,
569 struct swaybar_output *output,
570 struct swaybar_workspace *ws, double *x) { 611 struct swaybar_workspace *ws, double *x) {
612 struct swaybar_output *output = ctx->output;
571 struct swaybar_config *config = output->bar->config; 613 struct swaybar_config *config = output->bar->config;
572 struct box_colors box_colors; 614 struct box_colors box_colors;
573 if (ws->urgent) { 615 if (ws->urgent) {
@@ -580,30 +622,33 @@ static uint32_t render_workspace_button(cairo_t *cairo,
580 box_colors = config->colors.inactive_workspace; 622 box_colors = config->colors.inactive_workspace;
581 } 623 }
582 624
583 uint32_t height = output->height * output->scale; 625 uint32_t height = output->height;
584 626
627 cairo_t *cairo = ctx->cairo;
585 int text_width, text_height; 628 int text_width, text_height;
586 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 629 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
587 output->scale, config->pango_markup, "%s", ws->label); 630 1, config->pango_markup, "%s", ws->label);
588 631
589 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 632 int ws_vertical_padding = WS_VERTICAL_PADDING;
590 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 633 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
591 int border_width = BORDER_WIDTH * output->scale; 634 int border_width = BORDER_WIDTH;
592 635
593 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 636 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
594 + border_width * 2; 637 + border_width * 2;
595 uint32_t ideal_surface_height = ideal_height / output->scale; 638 uint32_t ideal_surface_height = ideal_height;
596 if (!output->bar->config->height && 639 if (!output->bar->config->height &&
597 output->height < ideal_surface_height) { 640 output->height < ideal_surface_height) {
598 return ideal_surface_height; 641 return ideal_surface_height;
599 } 642 }
600 643
601 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 644 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
602 if (width < config->workspace_min_width * output->scale) { 645 if (width < config->workspace_min_width) {
603 width = config->workspace_min_width * output->scale; 646 width = config->workspace_min_width;
604 } 647 }
605 648
649 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
606 cairo_set_source_u32(cairo, box_colors.background); 650 cairo_set_source_u32(cairo, box_colors.background);
651 ctx->background_color = box_colors.background;
607 cairo_rectangle(cairo, *x, 0, width, height); 652 cairo_rectangle(cairo, *x, 0, width, height);
608 cairo_fill(cairo); 653 cairo_fill(cairo);
609 654
@@ -620,7 +665,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
620 double text_y = height / 2.0 - text_height / 2.0; 665 double text_y = height / 2.0 - text_height / 2.0;
621 cairo_set_source_u32(cairo, box_colors.text); 666 cairo_set_source_u32(cairo, box_colors.text);
622 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 667 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
623 pango_printf(cairo, config->font, output->scale, config->pango_markup, 668 choose_text_aa_mode(ctx, box_colors.text);
669 render_text(cairo, config->font, 1, config->pango_markup,
624 "%s", ws->label); 670 "%s", ws->label);
625 671
626 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 672 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
@@ -637,20 +683,24 @@ static uint32_t render_workspace_button(cairo_t *cairo,
637 return output->height; 683 return output->height;
638} 684}
639 685
640static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { 686static uint32_t render_to_cairo(struct render_context *ctx) {
687 cairo_t *cairo = ctx->cairo;
688 struct swaybar_output *output = ctx->output;
641 struct swaybar *bar = output->bar; 689 struct swaybar *bar = output->bar;
642 struct swaybar_config *config = bar->config; 690 struct swaybar_config *config = bar->config;
643 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 691 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
644 if (output->focused) { 692 if (output->focused) {
645 cairo_set_source_u32(cairo, config->colors.focused_background); 693 ctx->background_color = config->colors.focused_background;
646 } else { 694 } else {
647 cairo_set_source_u32(cairo, config->colors.background); 695 ctx->background_color = config->colors.background;
648 } 696 }
697
698 cairo_set_source_u32(cairo, ctx->background_color);
649 cairo_paint(cairo); 699 cairo_paint(cairo);
650 700
651 int th; 701 int th;
652 get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); 702 get_text_size(cairo, config->font, NULL, &th, NULL, 1, false, "");
653 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; 703 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4);
654 /* 704 /*
655 * Each render_* function takes the actual height of the bar, and returns 705 * Each render_* function takes the actual height of the bar, and returns
656 * the ideal height. If the actual height is too short, the render function 706 * the ideal height. If the actual height is too short, the render function
@@ -658,7 +708,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
658 * height is too tall, the render function should adapt its drawing to 708 * height is too tall, the render function should adapt its drawing to
659 * utilize the available space. 709 * utilize the available space.
660 */ 710 */
661 double x = output->width * output->scale; 711 double x = output->width;
662#if HAVE_TRAY 712#if HAVE_TRAY
663 if (bar->tray) { 713 if (bar->tray) {
664 uint32_t h = render_tray(cairo, output, &x); 714 uint32_t h = render_tray(cairo, output, &x);
@@ -666,19 +716,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
666 } 716 }
667#endif 717#endif
668 if (bar->status) { 718 if (bar->status) {
669 uint32_t h = render_status_line(cairo, output, &x); 719 uint32_t h = render_status_line(ctx, &x);
670 max_height = h > max_height ? h : max_height; 720 max_height = h > max_height ? h : max_height;
671 } 721 }
672 x = 0; 722 x = 0;
673 if (config->workspace_buttons) { 723 if (config->workspace_buttons) {
674 struct swaybar_workspace *ws; 724 struct swaybar_workspace *ws;
675 wl_list_for_each(ws, &output->workspaces, link) { 725 wl_list_for_each(ws, &output->workspaces, link) {
676 uint32_t h = render_workspace_button(cairo, output, ws, &x); 726 uint32_t h = render_workspace_button(ctx, ws, &x);
677 max_height = h > max_height ? h : max_height; 727 max_height = h > max_height ? h : max_height;
678 } 728 }
679 } 729 }
680 if (config->binding_mode_indicator) { 730 if (config->binding_mode_indicator) {
681 uint32_t h = render_binding_mode_indicator(cairo, output, x); 731 uint32_t h = render_binding_mode_indicator(ctx, x);
682 max_height = h > max_height ? h : max_height; 732 max_height = h > max_height ? h : max_height;
683 } 733 }
684 734
@@ -708,26 +758,36 @@ void render_frame(struct swaybar_output *output) {
708 758
709 free_hotspots(&output->hotspots); 759 free_hotspots(&output->hotspots);
710 760
761 struct render_context ctx = { 0 };
762 ctx.output = output;
763
711 cairo_surface_t *recorder = cairo_recording_surface_create( 764 cairo_surface_t *recorder = cairo_recording_surface_create(
712 CAIRO_CONTENT_COLOR_ALPHA, NULL); 765 CAIRO_CONTENT_COLOR_ALPHA, NULL);
713 cairo_t *cairo = cairo_create(recorder); 766 cairo_t *cairo = cairo_create(recorder);
767 cairo_scale(cairo, output->scale, output->scale);
714 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 768 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
769 ctx.cairo = cairo;
770
715 cairo_font_options_t *fo = cairo_font_options_create(); 771 cairo_font_options_t *fo = cairo_font_options_create();
716 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); 772 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
773 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
774 ctx.textaa_safe = fo;
717 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 775 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
718 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 776 ctx.textaa_sharp = ctx.textaa_safe;
719 } else { 777 } else {
778 fo = cairo_font_options_create();
779 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
720 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 780 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
721 cairo_font_options_set_subpixel_order(fo, 781 cairo_font_options_set_subpixel_order(fo,
722 to_cairo_subpixel_order(output->subpixel)); 782 to_cairo_subpixel_order(output->subpixel));
783 ctx.textaa_sharp = fo;
723 } 784 }
724 cairo_set_font_options(cairo, fo); 785
725 cairo_font_options_destroy(fo);
726 cairo_save(cairo); 786 cairo_save(cairo);
727 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 787 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
728 cairo_paint(cairo); 788 cairo_paint(cairo);
729 cairo_restore(cairo); 789 cairo_restore(cairo);
730 uint32_t height = render_to_cairo(cairo, output); 790 uint32_t height = render_to_cairo(&ctx);
731 int config_height = output->bar->config->height; 791 int config_height = output->bar->config->height;
732 if (config_height > 0) { 792 if (config_height > 0) {
733 height = config_height; 793 height = config_height;
@@ -753,9 +813,7 @@ void render_frame(struct swaybar_output *output) {
753 output->width * output->scale, 813 output->width * output->scale,
754 output->height * output->scale); 814 output->height * output->scale);
755 if (!output->current_buffer) { 815 if (!output->current_buffer) {
756 cairo_surface_destroy(recorder); 816 goto cleanup;
757 cairo_destroy(cairo);
758 return;
759 } 817 }
760 cairo_t *shm = output->current_buffer->cairo; 818 cairo_t *shm = output->current_buffer->cairo;
761 819
@@ -779,6 +837,12 @@ void render_frame(struct swaybar_output *output) {
779 837
780 wl_surface_commit(output->surface); 838 wl_surface_commit(output->surface);
781 } 839 }
840
841cleanup:
842 if (ctx.textaa_sharp != ctx.textaa_safe) {
843 cairo_font_options_destroy(ctx.textaa_sharp);
844 }
845 cairo_font_options_destroy(ctx.textaa_safe);
782 cairo_surface_destroy(recorder); 846 cairo_surface_destroy(recorder);
783 cairo_destroy(cairo); 847 cairo_destroy(cairo);
784} 848}
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index ecd91032..2e9bb7f1 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -117,11 +117,11 @@ bool status_handle_readable(struct status_line *status) {
117 status->text = status->buffer; 117 status->text = status->buffer;
118 // intentional fall-through 118 // intentional fall-through
119 case PROTOCOL_TEXT: 119 case PROTOCOL_TEXT:
120 errno = 0;
121 while (true) { 120 while (true) {
122 if (status->buffer[read_bytes - 1] == '\n') { 121 if (status->buffer[read_bytes - 1] == '\n') {
123 status->buffer[read_bytes - 1] = '\0'; 122 status->buffer[read_bytes - 1] = '\0';
124 } 123 }
124 errno = 0;
125 read_bytes = getline(&status->buffer, 125 read_bytes = getline(&status->buffer,
126 &status->buffer_size, status->read); 126 &status->buffer_size, status->read);
127 if (errno == EAGAIN) { 127 if (errno == EAGAIN) {
@@ -157,7 +157,12 @@ struct status_line *status_line_init(char *cmd) {
157 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " 157 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before "
158 " starting `status-command`; WAYLAND_SOCKET should not be set"); 158 " starting `status-command`; WAYLAND_SOCKET should not be set");
159 status->pid = fork(); 159 status->pid = fork();
160 if (status->pid == 0) { 160 if (status->pid < 0) {
161 sway_log_errno(SWAY_ERROR, "fork failed");
162 exit(1);
163 } else if (status->pid == 0) {
164 setpgid(0, 0);
165
161 dup2(pipe_read_fd[1], STDOUT_FILENO); 166 dup2(pipe_read_fd[1], STDOUT_FILENO);
162 close(pipe_read_fd[0]); 167 close(pipe_read_fd[0]);
163 close(pipe_read_fd[1]); 168 close(pipe_read_fd[1]);
@@ -185,8 +190,8 @@ struct status_line *status_line_init(char *cmd) {
185 190
186void status_line_free(struct status_line *status) { 191void status_line_free(struct status_line *status) {
187 status_line_close_fds(status); 192 status_line_close_fds(status);
188 kill(status->pid, status->cont_signal); 193 kill(-status->pid, status->cont_signal);
189 kill(status->pid, SIGTERM); 194 kill(-status->pid, SIGTERM);
190 waitpid(status->pid, NULL, 0); 195 waitpid(status->pid, NULL, 0);
191 if (status->protocol == PROTOCOL_I3BAR) { 196 if (status->protocol == PROTOCOL_I3BAR) {
192 struct i3bar_block *block, *tmp; 197 struct i3bar_block *block, *tmp;
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index a5660f62..6d4b17bf 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 }
@@ -488,24 +493,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
488 cairo_destroy(cairo_icon); 493 cairo_destroy(cairo_icon);
489 } 494 }
490 495
491 int padded_size = icon_size + 2*padding; 496 double descaled_padding = (double)padding / output->scale;
492 *x -= padded_size; 497 double descaled_icon_size = (double)icon_size / output->scale;
493 int y = floor((height - padded_size) / 2.0); 498
499 int size = descaled_icon_size + 2 * descaled_padding;
500 *x -= size;
501 int icon_y = floor((output->height - size) / 2.0);
494 502
495 cairo_operator_t op = cairo_get_operator(cairo); 503 cairo_operator_t op = cairo_get_operator(cairo);
496 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 504 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
497 cairo_set_source_surface(cairo, icon, *x + padding, y + padding); 505
498 cairo_rectangle(cairo, *x, y, padded_size, padded_size); 506 cairo_matrix_t scale_matrix;
507 cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon);
508 // TODO: check cairo_pattern_status for "ENOMEM"
509 cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale);
510 cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding));
511 cairo_pattern_set_matrix(icon_pattern, &scale_matrix);
512 cairo_set_source(cairo, icon_pattern);
513 cairo_rectangle(cairo, *x, icon_y, size, size);
499 cairo_fill(cairo); 514 cairo_fill(cairo);
515
500 cairo_set_operator(cairo, op); 516 cairo_set_operator(cairo, op);
501 517
518 cairo_pattern_destroy(icon_pattern);
502 cairo_surface_destroy(icon); 519 cairo_surface_destroy(icon);
503 520
504 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 521 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
505 hotspot->x = *x; 522 hotspot->x = *x;
506 hotspot->y = 0; 523 hotspot->y = 0;
507 hotspot->width = height; 524 hotspot->width = size;
508 hotspot->height = height; 525 hotspot->height = output->height;
509 hotspot->callback = icon_hotspot_callback; 526 hotspot->callback = icon_hotspot_callback;
510 hotspot->destroy = free; 527 hotspot->destroy = free;
511 hotspot->data = strdup(sni->watcher_id); 528 hotspot->data = strdup(sni->watcher_id);
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 5fe6f9c3..b0545f4a 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -116,8 +116,8 @@ uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) {
116 } 116 }
117 } // else display on all 117 } // else display on all
118 118
119 if ((int) output->height*output->scale <= 2*config->tray_padding) { 119 if ((int)(output->height * output->scale) <= 2 * config->tray_padding) {
120 return 2*config->tray_padding + 1; 120 return (2 * config->tray_padding + 1) / output->scale;
121 } 121 }
122 122
123 uint32_t max_height = 0; 123 uint32_t max_height = 0;
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 60536e48..02bb12c6 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -1,4 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2
3#include <limits.h>
2#include <stdio.h> 4#include <stdio.h>
3#include <stdlib.h> 5#include <stdlib.h>
4#include <string.h> 6#include <string.h>
@@ -286,28 +288,74 @@ static void pretty_print_config(json_object *c) {
286 printf("%s\n", json_object_get_string(config)); 288 printf("%s\n", json_object_get_string(config));
287} 289}
288 290
289static void pretty_print(int type, json_object *resp) { 291static void pretty_print_tree(json_object *obj, int indent) {
290 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && 292 for (int i = 0; i < indent; i++) {
291 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && 293 printf(" ");
292 type != IPC_GET_VERSION && type != IPC_GET_SEATS &&
293 type != IPC_GET_CONFIG && type != IPC_SEND_TICK) {
294 printf("%s\n", json_object_to_json_string_ext(resp,
295 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
296 return;
297 } 294 }
298 295
299 if (type == IPC_SEND_TICK) { 296 int id = json_object_get_int(json_object_object_get(obj, "id"));
300 return; 297 const char *name = json_object_get_string(json_object_object_get(obj, "name"));
298 const char *type = json_object_get_string(json_object_object_get(obj, "type"));
299 const char *shell = json_object_get_string(json_object_object_get(obj, "shell"));
300
301 printf("#%d: %s \"%s\"", id, type, name);
302
303 if (shell != NULL) {
304 int pid = json_object_get_int(json_object_object_get(obj, "pid"));
305 const char *app_id = json_object_get_string(json_object_object_get(obj, "app_id"));
306 json_object *window_props_obj = json_object_object_get(obj, "window_properties");
307 const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance"));
308 const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class"));
309 int x11_id = json_object_get_int(json_object_object_get(obj, "window"));
310
311 printf(" (%s, pid: %d", shell, pid);
312 if (app_id != NULL) {
313 printf(", app_id: \"%s\"", app_id);
314 }
315 if (instance != NULL) {
316 printf(", instance: \"%s\"", instance);
317 }
318 if (class != NULL) {
319 printf(", class: \"%s\"", class);
320 }
321 if (x11_id != 0) {
322 printf(", X11 window: 0x%X", x11_id);
323 }
324 printf(")");
301 } 325 }
302 326
303 if (type == IPC_GET_VERSION) { 327 printf("\n");
304 pretty_print_version(resp); 328
305 return; 329 json_object *nodes_obj = json_object_object_get(obj, "nodes");
330 size_t len = json_object_array_length(nodes_obj);
331 for (size_t i = 0; i < len; i++) {
332 pretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1);
306 } 333 }
334}
307 335
308 if (type == IPC_GET_CONFIG) { 336static void pretty_print(int type, json_object *resp) {
337 switch (type) {
338 case IPC_SEND_TICK:
339 return;
340 case IPC_GET_VERSION:
341 pretty_print_version(resp);
342 return;
343 case IPC_GET_CONFIG:
309 pretty_print_config(resp); 344 pretty_print_config(resp);
310 return; 345 return;
346 case IPC_GET_TREE:
347 pretty_print_tree(resp, 0);
348 return;
349 case IPC_COMMAND:
350 case IPC_GET_WORKSPACES:
351 case IPC_GET_INPUTS:
352 case IPC_GET_OUTPUTS:
353 case IPC_GET_SEATS:
354 break;
355 default:
356 printf("%s\n", json_object_to_json_string_ext(resp,
357 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
358 return;
311 } 359 }
312 360
313 json_object *obj; 361 json_object *obj;
@@ -343,7 +391,7 @@ int main(int argc, char **argv) {
343 391
344 sway_log_init(SWAY_INFO, NULL); 392 sway_log_init(SWAY_INFO, NULL);
345 393
346 static struct option long_options[] = { 394 static const struct option long_options[] = {
347 {"help", no_argument, NULL, 'h'}, 395 {"help", no_argument, NULL, 'h'},
348 {"monitor", no_argument, NULL, 'm'}, 396 {"monitor", no_argument, NULL, 'm'},
349 {"pretty", no_argument, NULL, 'p'}, 397 {"pretty", no_argument, NULL, 'p'},
@@ -480,12 +528,20 @@ int main(int argc, char **argv) {
480 char *resp = ipc_single_command(socketfd, type, command, &len); 528 char *resp = ipc_single_command(socketfd, type, command, &len);
481 529
482 // pretty print the json 530 // pretty print the json
483 json_object *obj = json_tokener_parse(resp); 531 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
484 if (obj == NULL) { 532 if (tok == NULL) {
533 if (quiet) {
534 exit(EXIT_FAILURE);
535 }
536 sway_abort("failed allocating json_tokener");
537 }
538 json_object *obj = json_tokener_parse_ex(tok, resp, -1);
539 enum json_tokener_error err = json_tokener_get_error(tok);
540 json_tokener_free(tok);
541 if (obj == NULL || err != json_tokener_success) {
485 if (!quiet) { 542 if (!quiet) {
486 fprintf(stderr, "ERROR: Could not parse json response from ipc. " 543 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
487 "This is a bug in sway."); 544 json_tokener_error_desc(err));
488 printf("%s\n", resp);
489 } 545 }
490 ret = 1; 546 ret = 1;
491 } else { 547 } else {
@@ -517,13 +573,22 @@ int main(int argc, char **argv) {
517 break; 573 break;
518 } 574 }
519 575
520 json_object *obj = json_tokener_parse(reply->payload); 576 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
521 if (obj == NULL) { 577 if (tok == NULL) {
578 if (quiet) {
579 exit(EXIT_FAILURE);
580 }
581 sway_abort("failed allocating json_tokener");
582 }
583 json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1);
584 enum json_tokener_error err = json_tokener_get_error(tok);
585 json_tokener_free(tok);
586 if (obj == NULL || err != json_tokener_success) {
522 if (!quiet) { 587 if (!quiet) {
523 fprintf(stderr, "ERROR: Could not parse json response from" 588 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
524 " ipc. This is a bug in sway."); 589 json_tokener_error_desc(err));
525 ret = 1;
526 } 590 }
591 ret = 1;
527 break; 592 break;
528 } else if (quiet) { 593 } else if (quiet) {
529 json_object_put(obj); 594 json_object_put(obj);
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index b69013b5..24a9d6c9 100644
--- a/swaymsg/swaymsg.1.scd
+++ b/swaymsg/swaymsg.1.scd
@@ -21,12 +21,13 @@ _swaymsg_ [options...] [message]
21 21
22*-p, --pretty* 22*-p, --pretty*
23 Use pretty output even when not using a tty. 23 Use pretty output even when not using a tty.
24 Not available for all message types.
24 25
25*-q, --quiet* 26*-q, --quiet*
26 Sends the IPC message but does not print the response from sway. 27 Sends the IPC message but does not print the response from sway.
27 28
28*-r, --raw* 29*-r, --raw*
29 Use raw output even if using a tty. 30 Use raw JSON output even if using a tty.
30 31
31*-s, --socket* <path> 32*-s, --socket* <path>
32 Use the specified socket path. Otherwise, swaymsg will ask sway where the 33 Use the specified socket path. Otherwise, swaymsg will ask sway where the
@@ -46,6 +47,11 @@ _swaymsg_ [options...] [message]
46 47
47 See *sway*(5) for a list of commands. 48 See *sway*(5) for a list of commands.
48 49
50 _swaymsg_ can return pretty printed (standalone-default) or JSON-formatted
51 (*--raw*) output. For detailed documentation on the returned JSON-data of
52 each message type listed below, refer to *sway-ipc*(7). The JSON-format can
53 contain more information than the pretty print.
54
49 Tips: 55 Tips:
50 - Command expansion is performed twice: once by swaymsg, and again by sway. 56 - Command expansion is performed twice: once by swaymsg, and again by sway.
51 If you have quoted multi-word strings in your command, enclose the entire 57 If you have quoted multi-word strings in your command, enclose the entire
@@ -60,20 +66,20 @@ _swaymsg_ [options...] [message]
60 _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. 66 _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_.
61 67
62*get\_workspaces* 68*get\_workspaces*
63 Gets a JSON-encoded list of workspaces and their status. 69 Gets a list of workspaces and their status.
64 70
65*get\_inputs* 71*get\_inputs*
66 Gets a JSON-encoded list of current inputs. 72 Gets a list of current inputs.
67 73
68*get\_outputs* 74*get\_outputs*
69 Gets a JSON-encoded list of current outputs. 75 Gets a list of current outputs.
70 76
71*get\_tree* 77*get\_tree*
72 Gets a JSON-encoded layout tree of all open windows, containers, outputs, 78 Gets a JSON-encoded layout tree of all open windows, containers, outputs,
73 workspaces, and so on. 79 workspaces, and so on.
74 80
75*get\_seats* 81*get\_seats*
76 Gets a JSON-encoded list of all seats, 82 Gets a list of all seats,
77 its properties and all assigned devices. 83 its properties and all assigned devices.
78 84
79*get\_marks* 85*get\_marks*
@@ -83,7 +89,7 @@ _swaymsg_ [options...] [message]
83 Get a JSON-encoded configuration for swaybar. 89 Get a JSON-encoded configuration for swaybar.
84 90
85*get\_version* 91*get\_version*
86 Get JSON-encoded version information for the running instance of sway. 92 Get version information for the running instance of sway.
87 93
88*get\_binding\_modes* 94*get\_binding\_modes*
89 Gets a JSON-encoded list of currently configured binding modes. 95 Gets a JSON-encoded list of currently configured binding modes.
@@ -92,7 +98,7 @@ _swaymsg_ [options...] [message]
92 Gets JSON-encoded info about the current binding state. 98 Gets JSON-encoded info about the current binding state.
93 99
94*get\_config* 100*get\_config*
95 Gets a JSON-encoded copy of the current configuration. 101 Gets a copy of the current configuration. Doesn't expand includes.
96 102
97*send\_tick* 103*send\_tick*
98 Sends a tick event to all subscribed clients. 104 Sends a tick event to all subscribed clients.
diff --git a/swaynag/config.c b/swaynag/config.c
index ca7f4eb2..6db7cce5 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -51,7 +51,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
51 TO_PADDING_BTN, 51 TO_PADDING_BTN,
52 }; 52 };
53 53
54 static struct option opts[] = { 54 static const struct option opts[] = {
55 {"button", required_argument, NULL, 'b'}, 55 {"button", required_argument, NULL, 'b'},
56 {"button-no-terminal", required_argument, NULL, 'B'}, 56 {"button-no-terminal", required_argument, NULL, 'B'},
57 {"button-dismiss", required_argument, NULL, 'z'}, 57 {"button-dismiss", required_argument, NULL, 'z'},
@@ -59,6 +59,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
59 {"config", required_argument, NULL, 'c'}, 59 {"config", required_argument, NULL, 'c'},
60 {"debug", no_argument, NULL, 'd'}, 60 {"debug", no_argument, NULL, 'd'},
61 {"edge", required_argument, NULL, 'e'}, 61 {"edge", required_argument, NULL, 'e'},
62 {"layer", required_argument, NULL, 'y'},
62 {"font", required_argument, NULL, 'f'}, 63 {"font", required_argument, NULL, 'f'},
63 {"help", no_argument, NULL, 'h'}, 64 {"help", no_argument, NULL, 'h'},
64 {"detailed-message", no_argument, NULL, 'l'}, 65 {"detailed-message", no_argument, NULL, 'l'},
@@ -104,6 +105,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
104 " -c, --config <path> Path to config file.\n" 105 " -c, --config <path> Path to config file.\n"
105 " -d, --debug Enable debugging.\n" 106 " -d, --debug Enable debugging.\n"
106 " -e, --edge top|bottom Set the edge to use.\n" 107 " -e, --edge top|bottom Set the edge to use.\n"
108 " -y, --layer overlay|top|bottom|background\n"
109 " Set the layer to use.\n"
107 " -f, --font <font> Set the font to use.\n" 110 " -f, --font <font> Set the font to use.\n"
108 " -h, --help Show help message and quit.\n" 111 " -h, --help Show help message and quit.\n"
109 " -l, --detailed-message Read a detailed message from stdin.\n" 112 " -l, --detailed-message Read a detailed message from stdin.\n"
@@ -133,7 +136,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
133 136
134 optind = 1; 137 optind = 1;
135 while (1) { 138 while (1) {
136 int c = getopt_long(argc, argv, "b:B:z:Z:c:de:f:hlL:m:o:s:t:v", opts, NULL); 139 int c = getopt_long(argc, argv, "b:B:z:Z:c:de:y:f:hlL:m:o:s:t:v", opts, NULL);
137 if (c == -1) { 140 if (c == -1) {
138 break; 141 break;
139 } 142 }
@@ -184,6 +187,24 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
184 } 187 }
185 } 188 }
186 break; 189 break;
190 case 'y': // Layer
191 if (type) {
192 if (strcmp(optarg, "background") == 0) {
193 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
194 } else if (strcmp(optarg, "bottom") == 0) {
195 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
196 } else if (strcmp(optarg, "top") == 0) {
197 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
198 } else if (strcmp(optarg, "overlay") == 0) {
199 type->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
200 } else {
201 fprintf(stderr, "Invalid layer: %s\n"
202 "Usage: --layer overlay|top|bottom|background\n",
203 optarg);
204 return EXIT_FAILURE;
205 }
206 }
207 break;
187 case 'f': // Font 208 case 'f': // Font
188 if (type) { 209 if (type) {
189 free(type->font); 210 free(type->font);
diff --git a/swaynag/render.c b/swaynag/render.c
index cf2cc9e0..d72f42c2 100644
--- a/swaynag/render.c
+++ b/swaynag/render.c
@@ -1,5 +1,5 @@
1#include <stdint.h> 1#include <stdint.h>
2#include "cairo.h" 2#include "cairo_util.h"
3#include "log.h" 3#include "log.h"
4#include "pango.h" 4#include "pango.h"
5#include "pool-buffer.h" 5#include "pool-buffer.h"
@@ -10,19 +10,19 @@
10static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { 10static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) {
11 int text_width, text_height; 11 int text_width, text_height;
12 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 12 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL,
13 swaynag->scale, true, "%s", swaynag->message); 13 1, true, "%s", swaynag->message);
14 14
15 int padding = swaynag->type->message_padding * swaynag->scale; 15 int padding = swaynag->type->message_padding;
16 16
17 uint32_t ideal_height = text_height + padding * 2; 17 uint32_t ideal_height = text_height + padding * 2;
18 uint32_t ideal_surface_height = ideal_height / swaynag->scale; 18 uint32_t ideal_surface_height = ideal_height;
19 if (swaynag->height < ideal_surface_height) { 19 if (swaynag->height < ideal_surface_height) {
20 return ideal_surface_height; 20 return ideal_surface_height;
21 } 21 }
22 22
23 cairo_set_source_u32(cairo, swaynag->type->text); 23 cairo_set_source_u32(cairo, swaynag->type->text);
24 cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); 24 cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2);
25 pango_printf(cairo, swaynag->type->font, swaynag->scale, false, 25 render_text(cairo, swaynag->type->font, 1, false,
26 "%s", swaynag->message); 26 "%s", swaynag->message);
27 27
28 return ideal_surface_height; 28 return ideal_surface_height;
@@ -32,10 +32,10 @@ static void render_details_scroll_button(cairo_t *cairo,
32 struct swaynag *swaynag, struct swaynag_button *button) { 32 struct swaynag *swaynag, struct swaynag_button *button) {
33 int text_width, text_height; 33 int text_width, text_height;
34 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 34 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL,
35 swaynag->scale, true, "%s", button->text); 35 1, true, "%s", button->text);
36 36
37 int border = swaynag->type->button_border_thickness * swaynag->scale; 37 int border = swaynag->type->button_border_thickness;
38 int padding = swaynag->type->button_padding * swaynag->scale; 38 int padding = swaynag->type->button_padding;
39 39
40 cairo_set_source_u32(cairo, swaynag->type->details_background); 40 cairo_set_source_u32(cairo, swaynag->type->details_background);
41 cairo_rectangle(cairo, button->x, button->y, 41 cairo_rectangle(cairo, button->x, button->y,
@@ -50,7 +50,7 @@ static void render_details_scroll_button(cairo_t *cairo,
50 cairo_set_source_u32(cairo, swaynag->type->button_text); 50 cairo_set_source_u32(cairo, swaynag->type->button_text);
51 cairo_move_to(cairo, button->x + border + padding, 51 cairo_move_to(cairo, button->x + border + padding,
52 button->y + border + (button->height - text_height) / 2); 52 button->y + border + (button->height - text_height) / 2);
53 pango_printf(cairo, swaynag->type->font, swaynag->scale, true, 53 render_text(cairo, swaynag->type->font, 1, true,
54 "%s", button->text); 54 "%s", button->text);
55} 55}
56 56
@@ -58,33 +58,33 @@ static int get_detailed_scroll_button_width(cairo_t *cairo,
58 struct swaynag *swaynag) { 58 struct swaynag *swaynag) {
59 int up_width, down_width, temp_height; 59 int up_width, down_width, temp_height;
60 get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, 60 get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL,
61 swaynag->scale, true, 61 1, true,
62 "%s", swaynag->details.button_up.text); 62 "%s", swaynag->details.button_up.text);
63 get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL, 63 get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL,
64 swaynag->scale, true, 64 1, true,
65 "%s", swaynag->details.button_down.text); 65 "%s", swaynag->details.button_down.text);
66 66
67 int text_width = up_width > down_width ? up_width : down_width; 67 int text_width = up_width > down_width ? up_width : down_width;
68 int border = swaynag->type->button_border_thickness * swaynag->scale; 68 int border = swaynag->type->button_border_thickness;
69 int padding = swaynag->type->button_padding * swaynag->scale; 69 int padding = swaynag->type->button_padding;
70 70
71 return text_width + border * 2 + padding * 2; 71 return text_width + border * 2 + padding * 2;
72} 72}
73 73
74static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, 74static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag,
75 uint32_t y) { 75 uint32_t y) {
76 uint32_t width = swaynag->width * swaynag->scale; 76 uint32_t width = swaynag->width;
77 77
78 int border = swaynag->type->details_border_thickness * swaynag->scale; 78 int border = swaynag->type->details_border_thickness;
79 int padding = swaynag->type->message_padding * swaynag->scale; 79 int padding = swaynag->type->message_padding;
80 int decor = padding + border; 80 int decor = padding + border;
81 81
82 swaynag->details.x = decor; 82 swaynag->details.x = decor;
83 swaynag->details.y = y * swaynag->scale + decor; 83 swaynag->details.y = y + decor;
84 swaynag->details.width = width - decor * 2; 84 swaynag->details.width = width - decor * 2;
85 85
86 PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, 86 PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font,
87 swaynag->details.message, swaynag->scale, false); 87 swaynag->details.message, 1, false);
88 pango_layout_set_width(layout, 88 pango_layout_set_width(layout,
89 (swaynag->details.width - padding * 2) * PANGO_SCALE); 89 (swaynag->details.width - padding * 2) * PANGO_SCALE);
90 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); 90 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
@@ -164,7 +164,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag,
164 pango_cairo_show_layout(cairo, layout); 164 pango_cairo_show_layout(cairo, layout);
165 g_object_unref(layout); 165 g_object_unref(layout);
166 166
167 return ideal_height / swaynag->scale; 167 return ideal_height;
168} 168}
169 169
170static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, 170static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
@@ -173,13 +173,13 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
173 173
174 int text_width, text_height; 174 int text_width, text_height;
175 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 175 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL,
176 swaynag->scale, true, "%s", button->text); 176 1, true, "%s", button->text);
177 177
178 int border = swaynag->type->button_border_thickness * swaynag->scale; 178 int border = swaynag->type->button_border_thickness;
179 int padding = swaynag->type->button_padding * swaynag->scale; 179 int padding = swaynag->type->button_padding;
180 180
181 uint32_t ideal_height = text_height + padding * 2 + border * 2; 181 uint32_t ideal_height = text_height + padding * 2 + border * 2;
182 uint32_t ideal_surface_height = ideal_height / swaynag->scale; 182 uint32_t ideal_surface_height = ideal_height;
183 if (swaynag->height < ideal_surface_height) { 183 if (swaynag->height < ideal_surface_height) {
184 return ideal_surface_height; 184 return ideal_surface_height;
185 } 185 }
@@ -201,7 +201,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
201 201
202 cairo_set_source_u32(cairo, swaynag->type->button_text); 202 cairo_set_source_u32(cairo, swaynag->type->button_text);
203 cairo_move_to(cairo, button->x + padding, button->y + padding); 203 cairo_move_to(cairo, button->x + padding, button->y + padding);
204 pango_printf(cairo, swaynag->type->font, swaynag->scale, true, 204 render_text(cairo, swaynag->type->font, 1, true,
205 "%s", button->text); 205 "%s", button->text);
206 206
207 *x = button->x - border; 207 *x = button->x - border;
@@ -220,13 +220,12 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) {
220 max_height = h > max_height ? h : max_height; 220 max_height = h > max_height ? h : max_height;
221 221
222 int x = swaynag->width - swaynag->type->button_margin_right; 222 int x = swaynag->width - swaynag->type->button_margin_right;
223 x *= swaynag->scale;
224 for (int i = 0; i < swaynag->buttons->length; i++) { 223 for (int i = 0; i < swaynag->buttons->length; i++) {
225 h = render_button(cairo, swaynag, i, &x); 224 h = render_button(cairo, swaynag, i, &x);
226 max_height = h > max_height ? h : max_height; 225 max_height = h > max_height ? h : max_height;
227 x -= swaynag->type->button_gap * swaynag->scale; 226 x -= swaynag->type->button_gap;
228 if (i == 0) { 227 if (i == 0) {
229 x -= swaynag->type->button_gap_close * swaynag->scale; 228 x -= swaynag->type->button_gap_close;
230 } 229 }
231 } 230 }
232 231
@@ -235,14 +234,14 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) {
235 max_height = h > max_height ? h : max_height; 234 max_height = h > max_height ? h : max_height;
236 } 235 }
237 236
238 int border = swaynag->type->bar_border_thickness * swaynag->scale; 237 int border = swaynag->type->bar_border_thickness;
239 if (max_height > swaynag->height) { 238 if (max_height > swaynag->height) {
240 max_height += border; 239 max_height += border;
241 } 240 }
242 cairo_set_source_u32(cairo, swaynag->type->border_bottom); 241 cairo_set_source_u32(cairo, swaynag->type->border_bottom);
243 cairo_rectangle(cairo, 0, 242 cairo_rectangle(cairo, 0,
244 swaynag->height * swaynag->scale - border, 243 swaynag->height - border,
245 swaynag->width * swaynag->scale, 244 swaynag->width,
246 border); 245 border);
247 cairo_fill(cairo); 246 cairo_fill(cairo);
248 247
@@ -257,6 +256,7 @@ void render_frame(struct swaynag *swaynag) {
257 cairo_surface_t *recorder = cairo_recording_surface_create( 256 cairo_surface_t *recorder = cairo_recording_surface_create(
258 CAIRO_CONTENT_COLOR_ALPHA, NULL); 257 CAIRO_CONTENT_COLOR_ALPHA, NULL);
259 cairo_t *cairo = cairo_create(recorder); 258 cairo_t *cairo = cairo_create(recorder);
259 cairo_scale(cairo, swaynag->scale, swaynag->scale);
260 cairo_save(cairo); 260 cairo_save(cairo);
261 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 261 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
262 cairo_paint(cairo); 262 cairo_paint(cairo);
diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd
index 4a03469e..1cc85db7 100644
--- a/swaynag/swaynag.1.scd
+++ b/swaynag/swaynag.1.scd
@@ -48,6 +48,9 @@ _swaynag_ [options...]
48*-e, --edge* top|bottom 48*-e, --edge* top|bottom
49 Set the edge to use. 49 Set the edge to use.
50 50
51*-y, --layer* overlay|top|bottom|background
52 Set the layer to use.
53
51*-f, --font* <font> 54*-f, --font* <font>
52 Set the font to use. 55 Set the font to use.
53 56
diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd
index a078a4d7..3c367d0f 100644
--- a/swaynag/swaynag.5.scd
+++ b/swaynag/swaynag.5.scd
@@ -53,7 +53,7 @@ The following sizing options can also be set:
53*message-padding=<padding>* 53*message-padding=<padding>*
54 Set the padding for the message. 54 Set the padding for the message.
55 55
56*details-gackground=<color>* 56*details-background=<color>*
57 The background color for the details. 57 The background color for the details.
58 58
59*details-border-size=<size>* 59*details-border-size=<size>*
@@ -79,6 +79,9 @@ Additionally, the following options can be assigned a default per-type:
79*edge=top|bottom* 79*edge=top|bottom*
80 Set the edge to use. 80 Set the edge to use.
81 81
82*layer=overlay|top|bottom|background*
83 Set the layer to use.
84
82*font=<font>* 85*font=<font>*
83 Set the font to use. 86 Set the font to use.
84 87
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index d9bec368..9b57d578 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -30,8 +30,8 @@ static bool terminal_execute(char *terminal, char *command) {
30 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); 30 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR);
31 char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1)); 31 char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1));
32 sprintf(cmd, "%s -e %s", terminal, fname); 32 sprintf(cmd, "%s -e %s", terminal, fname);
33 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 33 execlp("sh", "sh", "-c", cmd, NULL);
34 sway_log_errno(SWAY_ERROR, "Failed to run command, execl() returned."); 34 sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned.");
35 free(cmd); 35 free(cmd);
36 return false; 36 return false;
37} 37}
@@ -69,8 +69,8 @@ static void swaynag_button_execute(struct swaynag *swaynag,
69 sway_log(SWAY_DEBUG, 69 sway_log(SWAY_DEBUG,
70 "$TERMINAL not found. Running directly"); 70 "$TERMINAL not found. Running directly");
71 } 71 }
72 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); 72 execlp("sh", "sh", "-c", button->action, NULL);
73 sway_log_errno(SWAY_DEBUG, "execl failed"); 73 sway_log_errno(SWAY_DEBUG, "execlp failed");
74 _exit(EXIT_FAILURE); 74 _exit(EXIT_FAILURE);
75 } 75 }
76 } 76 }
@@ -103,7 +103,7 @@ static void layer_surface_closed(void *data,
103 swaynag_destroy(swaynag); 103 swaynag_destroy(swaynag);
104} 104}
105 105
106static struct zwlr_layer_surface_v1_listener layer_surface_listener = { 106static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
107 .configure = layer_surface_configure, 107 .configure = layer_surface_configure,
108 .closed = layer_surface_closed, 108 .closed = layer_surface_closed,
109}; 109};
@@ -124,7 +124,7 @@ static void surface_enter(void *data, struct wl_surface *surface,
124 }; 124 };
125} 125}
126 126
127static struct wl_surface_listener surface_listener = { 127static const struct wl_surface_listener surface_listener = {
128 .enter = surface_enter, 128 .enter = surface_enter,
129 .leave = nop, 129 .leave = nop,
130}; 130};
@@ -178,6 +178,8 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
178 wl_fixed_t surface_x, wl_fixed_t surface_y) { 178 wl_fixed_t surface_x, wl_fixed_t surface_y) {
179 struct swaynag_seat *seat = data; 179 struct swaynag_seat *seat = data;
180 struct swaynag_pointer *pointer = &seat->pointer; 180 struct swaynag_pointer *pointer = &seat->pointer;
181 pointer->x = wl_fixed_to_int(surface_x);
182 pointer->y = wl_fixed_to_int(surface_y);
181 pointer->serial = serial; 183 pointer->serial = serial;
182 update_cursor(seat); 184 update_cursor(seat);
183} 185}
@@ -198,8 +200,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
198 return; 200 return;
199 } 201 }
200 202
201 double x = seat->pointer.x * swaynag->scale; 203 double x = seat->pointer.x;
202 double y = seat->pointer.y * swaynag->scale; 204 double y = seat->pointer.y;
203 for (int i = 0; i < swaynag->buttons->length; i++) { 205 for (int i = 0; i < swaynag->buttons->length; i++) {
204 struct swaynag_button *nagbutton = swaynag->buttons->items[i]; 206 struct swaynag_button *nagbutton = swaynag->buttons->items[i];
205 if (x >= nagbutton->x 207 if (x >= nagbutton->x
@@ -263,7 +265,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
263 render_frame(swaynag); 265 render_frame(swaynag);
264} 266}
265 267
266static struct wl_pointer_listener pointer_listener = { 268static const struct wl_pointer_listener pointer_listener = {
267 .enter = wl_pointer_enter, 269 .enter = wl_pointer_enter,
268 .leave = nop, 270 .leave = nop,
269 .motion = wl_pointer_motion, 271 .motion = wl_pointer_motion,
@@ -289,7 +291,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
289 } 291 }
290} 292}
291 293
292const struct wl_seat_listener seat_listener = { 294static const struct wl_seat_listener seat_listener = {
293 .capabilities = seat_handle_capabilities, 295 .capabilities = seat_handle_capabilities,
294 .name = nop, 296 .name = nop,
295}; 297};
@@ -305,33 +307,25 @@ static void output_scale(void *data, struct wl_output *output,
305 } 307 }
306} 308}
307 309
308static struct wl_output_listener output_listener = { 310static void output_name(void *data, struct wl_output *output,
309 .geometry = nop, 311 const char *name) {
310 .mode = nop,
311 .done = nop,
312 .scale = output_scale,
313};
314
315static void xdg_output_handle_name(void *data,
316 struct zxdg_output_v1 *xdg_output, const char *name) {
317 struct swaynag_output *swaynag_output = data; 312 struct swaynag_output *swaynag_output = data;
318 char *outname = swaynag_output->swaynag->type->output; 313 swaynag_output->name = strdup(name);
319 sway_log(SWAY_DEBUG, "Checking against output %s for %s", name, outname); 314
320 if (!swaynag_output->swaynag->output && outname && name 315 const char *outname = swaynag_output->swaynag->type->output;
321 && strcmp(outname, name) == 0) { 316 if (!swaynag_output->swaynag->output && outname &&
317 strcmp(outname, name) == 0) {
322 sway_log(SWAY_DEBUG, "Using output %s", name); 318 sway_log(SWAY_DEBUG, "Using output %s", name);
323 swaynag_output->swaynag->output = swaynag_output; 319 swaynag_output->swaynag->output = swaynag_output;
324 } 320 }
325 swaynag_output->name = strdup(name);
326 zxdg_output_v1_destroy(xdg_output);
327 swaynag_output->swaynag->querying_outputs--;
328} 321}
329 322
330static struct zxdg_output_v1_listener xdg_output_listener = { 323static const struct wl_output_listener output_listener = {
331 .logical_position = nop, 324 .geometry = nop,
332 .logical_size = nop, 325 .mode = nop,
333 .done = nop, 326 .done = nop,
334 .name = xdg_output_handle_name, 327 .scale = output_scale,
328 .name = output_name,
335 .description = nop, 329 .description = nop,
336}; 330};
337 331
@@ -359,33 +353,21 @@ static void handle_global(void *data, struct wl_registry *registry,
359 } else if (strcmp(interface, wl_shm_interface.name) == 0) { 353 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
360 swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); 354 swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
361 } else if (strcmp(interface, wl_output_interface.name) == 0) { 355 } else if (strcmp(interface, wl_output_interface.name) == 0) {
362 if (!swaynag->output && swaynag->xdg_output_manager) { 356 if (!swaynag->output) {
363 swaynag->querying_outputs++;
364 struct swaynag_output *output = 357 struct swaynag_output *output =
365 calloc(1, sizeof(struct swaynag_output)); 358 calloc(1, sizeof(struct swaynag_output));
366 output->wl_output = wl_registry_bind(registry, name, 359 output->wl_output = wl_registry_bind(registry, name,
367 &wl_output_interface, 3); 360 &wl_output_interface, 4);
368 output->wl_name = name; 361 output->wl_name = name;
369 output->scale = 1; 362 output->scale = 1;
370 output->swaynag = swaynag; 363 output->swaynag = swaynag;
371 wl_list_insert(&swaynag->outputs, &output->link); 364 wl_list_insert(&swaynag->outputs, &output->link);
372 wl_output_add_listener(output->wl_output, 365 wl_output_add_listener(output->wl_output,
373 &output_listener, output); 366 &output_listener, output);
374
375 struct zxdg_output_v1 *xdg_output;
376 xdg_output = zxdg_output_manager_v1_get_xdg_output(
377 swaynag->xdg_output_manager, output->wl_output);
378 zxdg_output_v1_add_listener(xdg_output,
379 &xdg_output_listener, output);
380 } 367 }
381 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 368 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
382 swaynag->layer_shell = wl_registry_bind( 369 swaynag->layer_shell = wl_registry_bind(
383 registry, name, &zwlr_layer_shell_v1_interface, 1); 370 registry, name, &zwlr_layer_shell_v1_interface, 1);
384 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0
385 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
386 swaynag->xdg_output_manager = wl_registry_bind(registry, name,
387 &zxdg_output_manager_v1_interface,
388 ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
389 } 371 }
390} 372}
391 373
@@ -451,12 +433,11 @@ void swaynag_setup(struct swaynag *swaynag) {
451 433
452 assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); 434 assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm);
453 435
454 while (swaynag->querying_outputs > 0) { 436 // Second roundtrip to get wl_output properties
455 if (wl_display_roundtrip(swaynag->display) < 0) { 437 if (wl_display_roundtrip(swaynag->display) < 0) {
456 sway_log(SWAY_ERROR, "Error during outputs init."); 438 sway_log(SWAY_ERROR, "Error during outputs init.");
457 swaynag_destroy(swaynag); 439 swaynag_destroy(swaynag);
458 exit(EXIT_FAILURE); 440 exit(EXIT_FAILURE);
459 }
460 } 441 }
461 442
462 if (!swaynag->output && swaynag->type->output) { 443 if (!swaynag->output && swaynag->type->output) {
@@ -474,7 +455,8 @@ void swaynag_setup(struct swaynag *swaynag) {
474 swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( 455 swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
475 swaynag->layer_shell, swaynag->surface, 456 swaynag->layer_shell, swaynag->surface,
476 swaynag->output ? swaynag->output->wl_output : NULL, 457 swaynag->output ? swaynag->output->wl_output : NULL,
477 ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); 458 swaynag->type->layer,
459 "swaynag");
478 assert(swaynag->layer_surface); 460 assert(swaynag->layer_surface);
479 zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, 461 zwlr_layer_surface_v1_add_listener(swaynag->layer_surface,
480 &layer_surface_listener, swaynag); 462 &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;