aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ISSUE_TEMPLATE.md10
-rw-r--r--README.bg.md7
-rw-r--r--README.de.md9
-rw-r--r--README.el.md2
-rw-r--r--README.fr.md8
-rw-r--r--README.it.md9
-rw-r--r--README.ja.md3
-rw-r--r--README.md8
-rw-r--r--README.pt.md6
-rw-r--r--README.ru.md8
-rw-r--r--README.uk.md10
-rw-r--r--common/ipc-client.c11
-rw-r--r--common/list.c26
-rw-r--r--common/loop.c6
-rw-r--r--common/meson.build1
-rw-r--r--common/readline.c72
-rw-r--r--common/stringop.c35
-rw-r--r--common/util.c9
-rw-r--r--completions/bash/swaybar44
-rw-r--r--completions/bash/swayidle48
-rw-r--r--completions/fish/swayidle.fish3
-rw-r--r--completions/zsh/_sway4
-rw-r--r--completions/zsh/_swaybar13
-rw-r--r--completions/zsh/_swaylock40
-rw-r--r--completions/zsh/_swaymsg12
-rw-r--r--config.in2
-rw-r--r--include/ipc.h1
-rw-r--r--include/list.h7
-rw-r--r--include/readline.h10
-rw-r--r--include/stringop.h8
-rw-r--r--include/sway/commands.h22
-rw-r--r--include/sway/config.h39
-rw-r--r--include/sway/input/cursor.h37
-rw-r--r--include/sway/input/input-manager.h10
-rw-r--r--include/sway/input/seat.h88
-rw-r--r--include/sway/output.h12
-rw-r--r--include/sway/tree/container.h8
-rw-r--r--include/sway/tree/view.h7
-rw-r--r--include/swaybar/bar.h14
-rw-r--r--include/swaybar/config.h18
-rw-r--r--include/swaybar/i3bar.h3
-rw-r--r--include/swaybar/input.h12
-rw-r--r--include/swaybar/tray/host.h17
-rw-r--r--include/swaybar/tray/icon.h52
-rw-r--r--include/swaybar/tray/item.h49
-rw-r--r--include/swaybar/tray/sni.h82
-rw-r--r--include/swaybar/tray/tray.h40
-rw-r--r--include/swaybar/tray/watcher.h18
-rw-r--r--include/swaylock/swaylock.h5
-rw-r--r--include/swaynag/swaynag.h1
-rw-r--r--meson.build26
-rw-r--r--meson_options.txt1
-rw-r--r--sway/commands.c106
-rw-r--r--sway/commands/assign.c2
-rw-r--r--sway/commands/bar.c9
-rw-r--r--sway/commands/bar/activate_button.c8
-rw-r--r--sway/commands/bar/bind.c106
-rw-r--r--sway/commands/bar/bindsym.c69
-rw-r--r--sway/commands/bar/colors.c4
-rw-r--r--sway/commands/bar/context_button.c8
-rw-r--r--sway/commands/bar/gaps.c60
-rw-r--r--sway/commands/bar/hidden_state.c2
-rw-r--r--sway/commands/bar/icon_theme.c26
-rw-r--r--sway/commands/bar/id.c2
-rw-r--r--sway/commands/bar/mode.c2
-rw-r--r--sway/commands/bar/modifier.c5
-rw-r--r--sway/commands/bar/output.c2
-rw-r--r--sway/commands/bar/secondary_button.c8
-rw-r--r--sway/commands/bar/separator_symbol.c2
-rw-r--r--sway/commands/bar/status_edge_padding.c21
-rw-r--r--sway/commands/bar/status_padding.c21
-rw-r--r--sway/commands/bar/strip_workspace_name.c32
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c17
-rw-r--r--sway/commands/bar/tray_bindsym.c55
-rw-r--r--sway/commands/bar/tray_output.c40
-rw-r--r--sway/commands/bar/tray_padding.c37
-rw-r--r--sway/commands/bind.c167
-rw-r--r--sway/commands/exec_always.c2
-rw-r--r--sway/commands/focus.c5
-rw-r--r--sway/commands/for_window.c1
-rw-r--r--sway/commands/fullscreen.c4
-rw-r--r--sway/commands/input/events.c76
-rw-r--r--sway/commands/input/scroll_button.c34
-rw-r--r--sway/commands/input/xkb_layout.c2
-rw-r--r--sway/commands/input/xkb_model.c2
-rw-r--r--sway/commands/input/xkb_options.c2
-rw-r--r--sway/commands/input/xkb_rules.c2
-rw-r--r--sway/commands/input/xkb_variant.c2
-rw-r--r--sway/commands/mode.c2
-rw-r--r--sway/commands/move.c7
-rw-r--r--sway/commands/no_focus.c1
-rw-r--r--sway/commands/output/background.c7
-rw-r--r--sway/commands/output/transform.c2
-rw-r--r--sway/commands/reload.c9
-rw-r--r--sway/commands/rename.c9
-rw-r--r--sway/commands/resize.c28
-rw-r--r--sway/commands/seat.c16
-rw-r--r--sway/commands/seat/attach.c23
-rw-r--r--sway/commands/seat/cursor.c96
-rw-r--r--sway/commands/seat/fallback.c19
-rw-r--r--sway/commands/seat/hide_cursor.c29
-rw-r--r--sway/commands/set.c2
-rw-r--r--sway/commands/split.c4
-rw-r--r--sway/commands/swap.c2
-rw-r--r--sway/commands/tiling_drag_threshold.c22
-rw-r--r--sway/commands/title_align.c30
-rw-r--r--sway/commands/titlebar_border_thickness.c30
-rw-r--r--sway/commands/titlebar_padding.c42
-rw-r--r--sway/commands/workspace.c6
-rw-r--r--sway/config.c223
-rw-r--r--sway/config/bar.c22
-rw-r--r--sway/config/input.c2
-rw-r--r--sway/config/output.c114
-rw-r--r--sway/config/seat.c64
-rw-r--r--sway/criteria.c2
-rw-r--r--sway/desktop/output.c30
-rw-r--r--sway/desktop/render.c217
-rw-r--r--sway/desktop/transaction.c6
-rw-r--r--sway/desktop/xdg_shell.c4
-rw-r--r--sway/desktop/xdg_shell_v6.c4
-rw-r--r--sway/desktop/xwayland.c4
-rw-r--r--sway/input/cursor.c631
-rw-r--r--sway/input/input-manager.c227
-rw-r--r--sway/input/keyboard.c13
-rw-r--r--sway/input/seat.c297
-rw-r--r--sway/input/seatop_down.c77
-rw-r--r--sway/input/seatop_move_floating.c65
-rw-r--r--sway/input/seatop_move_tiling.c335
-rw-r--r--sway/input/seatop_resize_floating.c199
-rw-r--r--sway/input/seatop_resize_tiling.c92
-rw-r--r--sway/ipc-json.c359
-rw-r--r--sway/ipc-server.c68
-rw-r--r--sway/main.c83
-rw-r--r--sway/meson.build21
-rw-r--r--sway/security.c2
-rw-r--r--sway/server.c10
-rw-r--r--sway/sway-bar.5.scd64
-rw-r--r--sway/sway-input.5.scd37
-rw-r--r--sway/sway-output.5.scd22
-rw-r--r--sway/sway.1.scd10
-rw-r--r--sway/sway.5.scd64
-rw-r--r--sway/tree/container.c52
-rw-r--r--sway/tree/output.c18
-rw-r--r--sway/tree/root.c46
-rw-r--r--sway/tree/view.c109
-rw-r--r--sway/tree/workspace.c44
-rw-r--r--swaybar/bar.c83
-rw-r--r--swaybar/config.c23
-rw-r--r--swaybar/i3bar.c36
-rw-r--r--swaybar/input.c84
-rw-r--r--swaybar/ipc.c115
-rw-r--r--swaybar/main.c4
-rw-r--r--swaybar/meson.build44
-rw-r--r--swaybar/render.c190
-rw-r--r--swaybar/status_line.c2
-rw-r--r--swaybar/tray/host.c210
-rw-r--r--swaybar/tray/icon.c462
-rw-r--r--swaybar/tray/item.c472
-rw-r--r--swaybar/tray/tray.c131
-rw-r--r--swaybar/tray/watcher.c212
-rw-r--r--swayidle/main.c442
-rw-r--r--swayidle/meson.build27
-rw-r--r--swayidle/swayidle.1.scd63
-rw-r--r--swaylock/main.c192
-rw-r--r--swaylock/pam.c2
-rw-r--r--swaylock/password.c96
-rw-r--r--swaylock/render.c28
-rw-r--r--swaylock/seat.c6
-rw-r--r--swaylock/shadow.c2
-rw-r--r--swaylock/swaylock.1.scd27
-rw-r--r--swaymsg/main.c82
-rw-r--r--swaymsg/swaymsg.1.scd11
-rw-r--r--swaynag/config.c76
-rw-r--r--swaynag/main.c3
-rw-r--r--swaynag/swaynag.1.scd9
-rw-r--r--swaynag/swaynag.c19
-rw-r--r--swaynag/types.c8
177 files changed, 6286 insertions, 2834 deletions
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 5f3bb6bb..7caa5d1e 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -5,7 +5,7 @@ If you are using the nvidia proprietary driver for any reason, you have two choi
5 5
6If `lsmod | grep nvidia | wc -l` shows anything other than zero, your bug report is not welcome here. 6If `lsmod | grep nvidia | wc -l` shows anything other than zero, your bug report is not welcome here.
7 7
8Otherwise, please include the following four components in your bug report: sway version, debug log, configuration (if applicable), and an explanation of steps taken to reproduce the issue. 8Otherwise, please include the following four components in your bug report: sway version, debug log, configuration (if applicable), and an explanation of steps taken to reproduce the issue. If sway crashes, also include a stack trace.
9 9
10Obtain your version like so: 10Obtain your version like so:
11 11
@@ -32,3 +32,11 @@ You should try to reproduce the issue with the default configuration. If you can
32* Configuration File: 32* Configuration File:
33 33
34Finally, explain the steps you took in plain English to reproduce the problem below. 34Finally, explain the steps you took in plain English to reproduce the problem below.
35
36* Stack Trace, if sway crashes:
37
38If you use systemd, you should be able to open the coredump of the most recent crash with GDB like so:
39
40 coredumpctl gdb sway
41
42And then type `bt full` to obtain the stack trace.
diff --git a/README.bg.md b/README.bg.md
index efc99f15..87a15bed 100644
--- a/README.bg.md
+++ b/README.bg.md
@@ -1,8 +1,9 @@
1# sway 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" е в процес на разработка, съвместим с i3, [Wayland](http://wayland.freedesktop.org/) композитор. 3Sway е в процес на разработка, съвместим с i3,
4Прочетете [FAQ](https://github.com/swaywm/sway/wiki). Присъединете се в 4[Wayland](http://wayland.freedesktop.org/) композитор. Прочетете
5[IRC канала](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 5[FAQ](https://github.com/swaywm/sway/wiki). Присъединете се в [IRC
6канала](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на
6irc.freenode.net). 7irc.freenode.net).
7 8
8[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
diff --git a/README.de.md b/README.de.md
index 2c2e14e8..bf92b196 100644
--- a/README.de.md
+++ b/README.de.md
@@ -3,10 +3,11 @@
3Der Fortschritt dieser Übersetzung kann [hier](https://github.com/swaywm/sway/issues/1318) 3Der Fortschritt dieser Übersetzung kann [hier](https://github.com/swaywm/sway/issues/1318)
4eingesehen werden. 4eingesehen werden.
5 5
6"**S**irCmpwn's **Way**land compositor" ist ein i3-kompatibler 6Sway ist ein i3-kompatibler
7[Wayland](http://wayland.freedesktop.org/)-Kompositor. Lies die 7[Wayland](http://wayland.freedesktop.org/)-Kompositor. Lies die
8[FAQ](https://github.com/swaywm/sway/wiki#faq). Tritt dem 8[FAQ](https://github.com/swaywm/sway/wiki#faq). Tritt dem
9[IRC-Channel](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway in irc.freenode.net). 9[IRC-Channel](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway in
10irc.freenode.net).
10 11
11[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 12[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
12 13
diff --git a/README.el.md b/README.el.md
index 3c9d65c9..79acd422 100644
--- a/README.el.md
+++ b/README.el.md
@@ -1,6 +1,6 @@
1# sway 1# sway
2 2
3"Ο Sway (**S**irCmpwn's **Way**land) είναι ένας **υπό ανάπτυξη** [Wayland](http://wayland.freedesktop.org/) διαχειριστής παραθύρων συμβατός με τον αντίστοιχο διαχειριστή παραθύρων i3 για τον X11. 3Sway είναι ένας **υπό ανάπτυξη** [Wayland](http://wayland.freedesktop.org/) διαχειριστής παραθύρων συμβατός με τον αντίστοιχο διαχειριστή παραθύρων i3 για τον X11.
4Διαβάστε τις [Συνήθεις Ερωτήσεις](https://github.com/swaywm/sway/wiki). Συνδεθείτε στο [κανάλι μας στο IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway στο 4Διαβάστε τις [Συνήθεις Ερωτήσεις](https://github.com/swaywm/sway/wiki). Συνδεθείτε στο [κανάλι μας στο IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway στο
5irc.freenode.net). 5irc.freenode.net).
6 6
diff --git a/README.fr.md b/README.fr.md
index 935d288f..8ad4d3b6 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -1,9 +1,9 @@
1# sway 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" est un compositeur [Wayland](http://wayland.freedesktop.org/) 3Sway est un compositeur [Wayland](http://wayland.freedesktop.org/) compatible
4compatible avec i3, **en cours de développement**. 4avec i3, **en cours de développement**. Lisez la
5Lisez la [FAQ](https://github.com/swaywm/sway/wiki). Rejoignez le 5[FAQ](https://github.com/swaywm/sway/wiki). Rejoignez le [canal
6[canal IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur 6IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur
7irc.freenode.net). 7irc.freenode.net).
8 8
9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
diff --git a/README.it.md b/README.it.md
index af986844..8a83bc78 100644
--- a/README.it.md
+++ b/README.it.md
@@ -1,10 +1,9 @@
1# sway 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" è un compositor 3Sway è un compositor [Wayland](http://wayland.freedesktop.org/) **in via di
4[Wayland](http://wayland.freedesktop.org/) **in via di sviluppo** 4sviluppo** compatibile con i3. Leggi le [FAQ (in
5compatibile con i3. 5Inglese)](https://github.com/swaywm/sway/wiki). Unisciti al [canale IRC (in
6Leggi le [FAQ (in Inglese)](https://github.com/swaywm/sway/wiki). Unisciti al 6Inglese)](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
7[canale IRC (in Inglese)](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
8irc.freenode.net). 7irc.freenode.net).
9 8
10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
diff --git a/README.ja.md b/README.ja.md
index b9e541f0..d82e78b2 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,7 +1,6 @@
1# sway 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor"は**開発中**の 3Swayは**開発中**のi3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。
4i3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。
5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 4[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。
6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 5[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。
7 6
diff --git a/README.md b/README.md
index 6cb212fc..1deb9a4c 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,10 @@
3[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - 3[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) -
4[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) 4[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--)
5 5
6"**S**irCmpwn's **Way**land compositor" is a **work in progress** 6sway is a **work in progress** i3-compatible
7i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. 7[Wayland](http://wayland.freedesktop.org/) compositor. Read the
8Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the 8[FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC
9[IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 9channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
10irc.freenode.net). 10irc.freenode.net).
11 11
12If you'd like to support sway development, please contribute to [SirCmpwn's 12If you'd like to support sway development, please contribute to [SirCmpwn's
diff --git a/README.pt.md b/README.pt.md
index 91e709c1..8cdfa548 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -1,8 +1,8 @@
1# sway 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" é um compositor [Wayland](http://wayland.freedesktop.org/) 3Sway é um compositor [Wayland](http://wayland.freedesktop.org/) compatível com o
4compatível com o i3. Leia o [FAQ](https://github.com/swaywm/sway/wiki). Participe do 4i3. Leia o [FAQ](https://github.com/swaywm/sway/wiki). Participe do [canal
5[canal IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway no 5IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway no
6irc.freenode.net). 6irc.freenode.net).
7 7
8[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 8[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
diff --git a/README.ru.md b/README.ru.md
index 4b34dc2d..255e36aa 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -1,9 +1,9 @@
1# sway 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" на данный момент **(в разработке)** 3Sway на данный момент **(в разработке)** i3-совместимый
4i3-совместимый [Wayland](http://wayland.freedesktop.org/) композитор. 4[Wayland](http://wayland.freedesktop.org/) композитор. Прочитайте
5Прочитайте [FAQ](https://github.com/swaywm/sway/wiki). Присоединяйтесь к 5[FAQ](https://github.com/swaywm/sway/wiki). Присоединяйтесь к [IRC
6[IRC каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 6каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на
7irc.freenode.net). 7irc.freenode.net).
8 8
9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
diff --git a/README.uk.md b/README.uk.md
index 9ae7425a..5e9345b3 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -1,10 +1,10 @@
1# sway 1# sway
2 2
3**Sway** ("**S**irCmpwn's **Way**land compositor") це сумісний з i3 композитор 3**Sway** це сумісний з i3 композитор [Wayland](http://wayland.freedesktop.org/)
4[Wayland](http://wayland.freedesktop.org/) (**у стані розробки**). 4(**у стані розробки**). Ознайомтесь з
5Ознайомтесь з [ЧаПами](https://github.com/swaywm/sway/wiki). 5[ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в
6Приєднуйтесь до [спільноти в IRC](http://webchat.freenode.net/?channels=sway&uio=d4) 6IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на
7(#sway на irc.freenode.net). 7irc.freenode.net).
8 8
9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
10 10
diff --git a/common/ipc-client.c b/common/ipc-client.c
index 496fd131..3515ef0a 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -7,7 +7,6 @@
7#include <sys/un.h> 7#include <sys/un.h>
8#include <unistd.h> 8#include <unistd.h>
9#include "ipc-client.h" 9#include "ipc-client.h"
10#include "readline.h"
11#include "log.h" 10#include "log.h"
12 11
13static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; 12static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
@@ -18,28 +17,30 @@ char *get_socketpath(void) {
18 if (swaysock) { 17 if (swaysock) {
19 return strdup(swaysock); 18 return strdup(swaysock);
20 } 19 }
20 char *line = NULL;
21 size_t line_size = 0;
21 FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r"); 22 FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r");
22 if (fp) { 23 if (fp) {
23 char *line = read_line(fp); 24 getline(&line, &line_size, fp);
24 pclose(fp); 25 pclose(fp);
25 if (line && *line) { 26 if (line && *line) {
26 return line; 27 return line;
27 } 28 }
28 free(line);
29 } 29 }
30 const char *i3sock = getenv("I3SOCK"); 30 const char *i3sock = getenv("I3SOCK");
31 if (i3sock) { 31 if (i3sock) {
32 free(line);
32 return strdup(i3sock); 33 return strdup(i3sock);
33 } 34 }
34 fp = popen("i3 --get-socketpath 2>/dev/null", "r"); 35 fp = popen("i3 --get-socketpath 2>/dev/null", "r");
35 if (fp) { 36 if (fp) {
36 char *line = read_line(fp); 37 getline(&line, &line_size, fp);
37 pclose(fp); 38 pclose(fp);
38 if (line && *line) { 39 if (line && *line) {
39 return line; 40 return line;
40 } 41 }
41 free(line);
42 } 42 }
43 free(line);
43 return NULL; 44 return NULL;
44} 45}
45 46
diff --git a/common/list.c b/common/list.c
index ee268c9a..c3e2fe20 100644
--- a/common/list.c
+++ b/common/list.c
@@ -17,7 +17,7 @@ list_t *create_list(void) {
17 17
18static void list_resize(list_t *list) { 18static void list_resize(list_t *list) {
19 if (list->length == list->capacity) { 19 if (list->length == list->capacity) {
20 list->capacity += 10; 20 list->capacity *= 2;
21 list->items = realloc(list->items, sizeof(void*) * list->capacity); 21 list->items = realloc(list->items, sizeof(void*) * list->capacity);
22 } 22 }
23} 23}
@@ -30,15 +30,6 @@ void list_free(list_t *list) {
30 free(list); 30 free(list);
31} 31}
32 32
33void list_foreach(list_t *list, void (*callback)(void *item)) {
34 if (list == NULL || callback == NULL) {
35 return;
36 }
37 for (int i = 0; i < list->length; i++) {
38 callback(list->items[i]);
39 }
40}
41
42void list_add(list_t *list, void *item) { 33void list_add(list_t *list, void *item) {
43 list_resize(list); 34 list_resize(list);
44 list->items[list->length++] = item; 35 list->items[list->length++] = item;
@@ -57,8 +48,7 @@ void list_del(list_t *list, int index) {
57} 48}
58 49
59void list_cat(list_t *list, list_t *source) { 50void list_cat(list_t *list, list_t *source) {
60 int i; 51 for (int i = 0; i < source->length; ++i) {
61 for (i = 0; i < source->length; ++i) {
62 list_add(list, source->items[i]); 52 list_add(list, source->items[i]);
63 } 53 }
64} 54}
@@ -156,3 +146,15 @@ void list_stable_sort(list_t *list, int compare(const void *a, const void *b)) {
156 list_inplace_sort(list, 0, list->length - 1, compare); 146 list_inplace_sort(list, 0, list->length - 1, compare);
157 } 147 }
158} 148}
149
150void list_free_items_and_destroy(list_t *list) {
151 if (!list) {
152 return;
153 }
154
155 for (int i = 0; i < list->length; ++i) {
156 free(list->items[i]);
157 }
158 list_free(list);
159}
160
diff --git a/common/loop.c b/common/loop.c
index 82b80017..295f3a30 100644
--- a/common/loop.c
+++ b/common/loop.c
@@ -45,10 +45,8 @@ struct loop *loop_create(void) {
45} 45}
46 46
47void loop_destroy(struct loop *loop) { 47void loop_destroy(struct loop *loop) {
48 list_foreach(loop->fd_events, free); 48 list_free_items_and_destroy(loop->fd_events);
49 list_foreach(loop->timers, free); 49 list_free_items_and_destroy(loop->timers);
50 list_free(loop->fd_events);
51 list_free(loop->timers);
52 free(loop->fds); 50 free(loop->fds);
53 free(loop); 51 free(loop);
54} 52}
diff --git a/common/meson.build b/common/meson.build
index 224a9c3f..4ad872d1 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -8,7 +8,6 @@ lib_sway_common = static_library(
8 'loop.c', 8 'loop.c',
9 'list.c', 9 'list.c',
10 'pango.c', 10 'pango.c',
11 'readline.c',
12 'stringop.c', 11 'stringop.c',
13 'unicode.c', 12 'unicode.c',
14 'util.c' 13 'util.c'
diff --git a/common/readline.c b/common/readline.c
deleted file mode 100644
index 58652429..00000000
--- a/common/readline.c
+++ /dev/null
@@ -1,72 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include "readline.h"
3#include "log.h"
4#include <stdlib.h>
5#include <stdio.h>
6
7char *read_line(FILE *file) {
8 size_t length = 0, size = 128;
9 char *string = malloc(size);
10 char lastChar = '\0';
11 if (!string) {
12 wlr_log(WLR_ERROR, "Unable to allocate memory for read_line");
13 return NULL;
14 }
15 while (1) {
16 int c = getc(file);
17 if (c == '\n' && lastChar == '\\'){
18 --length; // Ignore last character.
19 lastChar = '\0';
20 continue;
21 }
22 if (c == EOF || c == '\n' || c == '\0') {
23 break;
24 }
25 if (c == '\r') {
26 continue;
27 }
28 lastChar = c;
29 if (length == size) {
30 char *new_string = realloc(string, size *= 2);
31 if (!new_string) {
32 free(string);
33 wlr_log(WLR_ERROR, "Unable to allocate memory for read_line");
34 return NULL;
35 }
36 string = new_string;
37 }
38 string[length++] = c;
39 }
40 if (length + 1 == size) {
41 char *new_string = realloc(string, length + 1);
42 if (!new_string) {
43 free(string);
44 return NULL;
45 }
46 string = new_string;
47 }
48 string[length] = '\0';
49 return string;
50}
51
52char *peek_line(FILE *file, int line_offset, long *position) {
53 long pos = ftell(file);
54 size_t length = 0;
55 char *line = NULL;
56 for (int i = 0; i <= line_offset; i++) {
57 ssize_t read = getline(&line, &length, file);
58 if (read < 0) {
59 free(line);
60 line = NULL;
61 break;
62 }
63 if (read > 0 && line[read - 1] == '\n') {
64 line[read - 1] = '\0';
65 }
66 }
67 if (position) {
68 *position = ftell(file);
69 }
70 fseek(file, pos, SEEK_SET);
71 return line;
72}
diff --git a/common/stringop.c b/common/stringop.c
index d2c91c24..8af0d60f 100644
--- a/common/stringop.c
+++ b/common/stringop.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <stdio.h> 3#include <stdio.h>
4#include <string.h> 4#include <string.h>
@@ -9,24 +9,17 @@
9#include "string.h" 9#include "string.h"
10#include "list.h" 10#include "list.h"
11 11
12const char whitespace[] = " \f\n\r\t\v"; 12static const char whitespace[] = " \f\n\r\t\v";
13 13
14char *strip_whitespace(char *_str) { 14void strip_whitespace(char *str) {
15 if (*_str == '\0') 15 size_t len = strlen(str);
16 return _str; 16 size_t start = strspn(str, whitespace);
17 char *strold = _str; 17 memmove(str, &str[start], len + 1 - start);
18 while (*_str == ' ' || *_str == '\t') { 18
19 _str++; 19 if (*str) {
20 for (len -= start + 1; isspace(str[len]); --len) {}
21 str[len + 1] = '\0';
20 } 22 }
21 char *str = strdup(_str);
22 free(strold);
23 int i;
24 for (i = 0; str[i] != '\0'; ++i);
25 do {
26 i--;
27 } while (i >= 0 && (str[i] == ' ' || str[i] == '\t'));
28 str[i + 1] = '\0';
29 return str;
30} 23}
31 24
32void strip_quotes(char *str) { 25void strip_quotes(char *str) {
@@ -97,14 +90,6 @@ list_t *split_string(const char *str, const char *delims) {
97 return res; 90 return res;
98} 91}
99 92
100void free_flat_list(list_t *list) {
101 int i;
102 for (i = 0; i < list->length; ++i) {
103 free(list->items[i]);
104 }
105 list_free(list);
106}
107
108char **split_args(const char *start, int *argc) { 93char **split_args(const char *start, int *argc) {
109 *argc = 0; 94 *argc = 0;
110 int alloc = 2; 95 int alloc = 2;
diff --git a/common/util.c b/common/util.c
index 0caafb39..d66058a6 100644
--- a/common/util.c
+++ b/common/util.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <sys/types.h> 3#include <sys/types.h>
4#include <sys/stat.h> 4#include <sys/stat.h>
@@ -13,7 +13,6 @@
13#include <xkbcommon/xkbcommon-names.h> 13#include <xkbcommon/xkbcommon-names.h>
14#include <wlr/types/wlr_keyboard.h> 14#include <wlr/types/wlr_keyboard.h>
15#include "log.h" 15#include "log.h"
16#include "readline.h"
17#include "util.h" 16#include "util.h"
18 17
19int wrap(int i, int max) { 18int wrap(int i, int max) {
@@ -24,7 +23,8 @@ int numlen(int n) {
24 if (n == 0) { 23 if (n == 0) {
25 return 1; 24 return 1;
26 } 25 }
27 return log10(n) + 1; 26 // Account for the '-' in negative numbers.
27 return log10(abs(n)) + (n > 0 ? 1 : 2);
28} 28}
29 29
30static struct modifier_key { 30static struct modifier_key {
@@ -86,11 +86,12 @@ pid_t get_parent_pid(pid_t child) {
86 char *token = NULL; 86 char *token = NULL;
87 const char *sep = " "; 87 const char *sep = " ";
88 FILE *stat = NULL; 88 FILE *stat = NULL;
89 size_t buf_size = 0;
89 90
90 sprintf(file_name, "/proc/%d/stat", child); 91 sprintf(file_name, "/proc/%d/stat", child);
91 92
92 if ((stat = fopen(file_name, "r"))) { 93 if ((stat = fopen(file_name, "r"))) {
93 if ((buffer = read_line(stat))) { 94 if (getline(&buffer, &buf_size, stat) != -1) {
94 token = strtok(buffer, sep); // pid 95 token = strtok(buffer, sep); // pid
95 token = strtok(NULL, sep); // executable name 96 token = strtok(NULL, sep); // executable name
96 token = strtok(NULL, sep); // state 97 token = strtok(NULL, sep); // state
diff --git a/completions/bash/swaybar b/completions/bash/swaybar
new file mode 100644
index 00000000..1e085c65
--- /dev/null
+++ b/completions/bash/swaybar
@@ -0,0 +1,44 @@
1# swaybar(1) completion
2
3_swaybar()
4{
5 local cur prev
6 _get_comp_words_by_ref cur prev
7
8 short=(
9 -h
10 -v
11 -s
12 -b
13 -d
14 )
15
16 long=(
17 --help
18 --version
19 --socket
20 --bar_id
21 --debug
22 )
23
24 case $prev in
25 -s|--socket)
26 _filedir
27 return
28 ;;
29 -b|--bar_id)
30 bars=($(swaymsg -t get_bar_config | jq -r '.[]'))
31 COMPREPLY=($(compgen -W "${bars[*]}" -- "$cur"))
32 return
33 ;;
34 esac
35
36 if [[ $cur == --* ]]; then
37 COMPREPLY=($(compgen -W "${long[*]}" -- "$cur"))
38 else
39 COMPREPLY=($(compgen -W "${short[*]}" -- "$cur"))
40 COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur"))
41 fi
42
43} &&
44complete -F _swaybar swaybar
diff --git a/completions/bash/swayidle b/completions/bash/swayidle
deleted file mode 100644
index a0cdc8b2..00000000
--- a/completions/bash/swayidle
+++ /dev/null
@@ -1,48 +0,0 @@
1# swaymsg(1) completion
2
3_swayidle()
4{
5 local cur prev
6 _get_comp_words_by_ref -n : cur prev
7 local prev2=${COMP_WORDS[COMP_CWORD-2]}
8 local prev3=${COMP_WORDS[COMP_CWORD-3]}
9
10 events=(
11 'timeout'
12 'before-sleep'
13 )
14
15 short=(
16 -h
17 -d
18 )
19
20 if [ "$prev" = timeout ]; then
21 # timeout <timeout>
22 return
23 elif [ "$prev2" = timeout ]; then
24 # timeout <timeout> <timeout command>
25 COMPREPLY=($(compgen -c -- "$cur"))
26 return
27 elif [ "$prev3" = timeout ]; then
28 # timeout <timeout> <timeout command> [resume <resume command>]
29 COMPREPLY=(resume)
30 # optional argument; no return here as user may skip 'resume'
31 fi
32
33 case "$prev" in
34 resume)
35 COMPREPLY=($(compgen -c -- "$cur"))
36 return
37 ;;
38 before-sleep)
39 COMPREPLY=($(compgen -c -- "$cur"))
40 return
41 ;;
42 esac
43
44 COMPREPLY+=($(compgen -W "${events[*]}" -- "$cur"))
45 COMPREPLY+=($(compgen -W "${short[*]}" -- "$cur"))
46
47} &&
48complete -F _swayidle swayidle
diff --git a/completions/fish/swayidle.fish b/completions/fish/swayidle.fish
deleted file mode 100644
index 71279925..00000000
--- a/completions/fish/swayidle.fish
+++ /dev/null
@@ -1,3 +0,0 @@
1# swayidle
2complete -c swayidle -s h --description 'show help'
3complete -c swayidle -s d --description 'debug'
diff --git a/completions/zsh/_sway b/completions/zsh/_sway
index 05112002..a7f55cc5 100644
--- a/completions/zsh/_sway
+++ b/completions/zsh/_sway
@@ -13,8 +13,8 @@
13# 13#
14# ------------------------------- 14# -------------------------------
15_arguments -s \ 15_arguments -s \
16 '(-v --version)'{-v,--version}'[shows version]' \ 16 '(-v --version)'{-v,--version}'[Show the version number and quit]' \
17 '(-h --help)'{-h,--help}'[shows help message]' \ 17 '(-h --help)'{-h,--help}'[Show help message and quit]' \
18 '(-c --config)'{-c,--config}'[Specify a config file]:files:_files' \ 18 '(-c --config)'{-c,--config}'[Specify a config file]:files:_files' \
19 '(-C --validate)'{-C,--validate}'[Check validity of the config file, then exit]' \ 19 '(-C --validate)'{-C,--validate}'[Check validity of the config file, then exit]' \
20 '(-d --debug)'{-d,--debug}'[Enables full logging, including debug information]' \ 20 '(-d --debug)'{-d,--debug}'[Enables full logging, including debug information]' \
diff --git a/completions/zsh/_swaybar b/completions/zsh/_swaybar
new file mode 100644
index 00000000..af2cee95
--- /dev/null
+++ b/completions/zsh/_swaybar
@@ -0,0 +1,13 @@
1#compdef swaybar
2#
3# Completion script for swaybar
4#
5
6local bars=($(swaymsg -t get_bar_config | jq -r '.[]'))
7
8_arguments -s \
9 '(-h --help)'{-h,--help}'[Show help message and quit]' \
10 '(-v --version)'{-v,--version}'[Show version and quit]' \
11 '(-s --socket)'{-s,--socket}'[Connect to sway via socket]:filename:_files' \
12 '(-b --bar_id)'{-b,--bar-id}'[Bar ID for which to get the configuration]:filename:($bars)'\
13 '(-d --debug)'{-d,--debug}'[Enable debugging]'
diff --git a/completions/zsh/_swaylock b/completions/zsh/_swaylock
index 8fb4834c..9bc84ec9 100644
--- a/completions/zsh/_swaylock
+++ b/completions/zsh/_swaylock
@@ -4,11 +4,37 @@
4# 4#
5 5
6_arguments -s \ 6_arguments -s \
7 '(-v --version)'{-v,--version}'[Show the version number and quit]' \ 7 '(-C --config)'{-C,--config}'[Path to the config file]:filename:_files' \
8 '(-h --help)'{-h,--help}'[Show help message and quit]' \ 8 '(-c --color)'{-c,--color}'[Turn the screen into the given color instead of white]:color:' \
9 '(-f --daemonize)'{-f,--daemonize}'[Detach from the controlling terminal]' \ 9 '(-e --ignore-empty-password)'{-e,--ignore-empty-password}'[When an empty password is provided, do not validate it]' \
10 '(-c --color)'{-c,--color}'[Specify a color (rrggbb)]' \ 10 '(-f --daemonize)'{-f,--daemonize}'[Detach from the controlling terminal after locking]' \
11 '(-i --image)'{-i,--image}'[Display an image]:files:_files' \ 11 '(-h --help)'{-h,--help}'[Show help message and quit]' \
12 '(-s --scaling)'{-s,--scaling}'[Scaling mode]:mode:(stretch fill fit center tile)' \ 12 '(-i --image)'{-i,--image}'[Display an image]:filename:_files' \
13 '(-s --scaling)'{-s,--scaling}'[Scaling mode]:mode:(stretch fill fit center tile)' \
14 '(-t --tiling)'{-t,--tiling}'[Same as --scaling=tile]' \
13 '(-u --no-unlock-indicator)'{-u,--no-unlock-indicator}'[Disable the unlock indicator]' \ 15 '(-u --no-unlock-indicator)'{-u,--no-unlock-indicator}'[Disable the unlock indicator]' \
14 '(--socket)'--socket'[Use the specified socket path.]:files:_files' \ 16 '(-v --version)'{-v,--version}'[Show the version number and quit]' \
17 '(--bs-hl-color)'--bs-hl-color'[Sets the color of backspace highlights segments]:color:' \
18 '(--font)'--font'[Sets the font of the text]:font:' \
19 '(--indicator-radius)'--indicator-radius'[Sets the indicator radius]:radius:' \
20 '(--indicator-thickness)'--indicator-thickness'[Sets the indicator thickness]:thickness:' \
21 '(--inside-color)'--inside-color'[Sets the color of the inside of the indicator]:color:' \
22 '(--inside-clear-color)'--inside-clear-color'[Sets the color of the inside of the indicator when cleared]:color:' \
23 '(--inside-clear-color)'--inside-clear-color'[Sets the color of the inside of the indicator when verifying]:color:' \
24 '(--inside-wrong-color)'--inside-wrong-color'[Sets the color of the inside of the indicator when invalid]:color:' \
25 '(--key-hl-color)'--key-hl-color'[Sets the color of the key press highlight segments]:color:' \
26 '(--line-color)'--line-color'[Sets the color of the line between the inside and ring]:color:' \
27 '(--line-clear-color)'--line-clear-color'[Sets the color of the line between the inside and ring when cleared]:color:' \
28 '(--line-ver-color)'--line-ver-color'[Sets the color of the line between the inside and ring when verifying]:color:' \
29 '(--line-wrong-color)'--line-wrong-color'[Sets the color of the line between the inside and ring when invalid]:color:' \
30 '(-n --line-uses-inside)'{-n,--line-uses-inside}'[Use the inside color for the line between the inside and ring]' \
31 '(-r --line-uses-ring)'{-r,--line--uses-ring}'[Use the ring color for the line between the inside and ring]' \
32 '(--ring-color)'--ring-color'[Sets the color of the ring of the indicator]:color:' \
33 '(--ring-clear-color)'--ring-clear-color'[Sets the color of the ring of the indicator when cleared]:color:' \
34 '(--ring-ver-color)'--ring-ver-color'[Sets the color of the ring of the indicator when verifying]:color:' \
35 '(--ring-wrong-color)'--ring-wrong-color'[Sets the color of the ring of the indicator when invalid]:color:' \
36 '(--separator-color)'--separator-color'[Sets the color of the lines that separate highlight segments]:color:' \
37 '(--text-color)'--text-color'[Sets the color of the text]:color:' \
38 '(--text-clear-color)'--text-clear-color'[Sets the color of the text when cleared]:color:' \
39 '(--text-ver-color)'--text-ver-color'[Sets the color of the text when verifying]:color:' \
40 '(--text-wrong-color)'--text-wrong-color'[Sets the color of the text when invalid]:color:' \ No newline at end of file
diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg
index a7a1c8e0..0ba45d4a 100644
--- a/completions/zsh/_swaymsg
+++ b/completions/zsh/_swaymsg
@@ -28,8 +28,10 @@ types=(
28) 28)
29 29
30_arguments -s \ 30_arguments -s \
31 '(-v --version)'{-v,--version}'[Print the version (of swaymsg) and quit]' \ 31 '(-v --version)'{-v,--version}'[Show the version number and quit]' \
32 '(-h --help)'{-h,--help}'[Shows help message]' \ 32 '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \
33 '(-q --quiet)'{-q,--quiet}'[Sends the IPC message but does not print the response from sway]' \ 33 '(-h --help)'{-h,--help}'[Show help message and quit]' \
34 '(-s --socket)'{-s,--socket}'[Use the specified socket path.]:files:_files' \ 34 '(-q --quiet)'{-q,--quiet}'[Be quiet]' \
35 '(-t --type)'{-t,--type}'[Specify the type of IPC message.]:type:{_describe "type" types}' 35 '(-r --raw)'{-r,--raw}'[Use raw output even if using a tty]' \
36 '(-s --socket)'{-s,--socket}'[Use the specified socket path]:files:_files' \
37 '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}'
diff --git a/config.in b/config.in
index 9c751179..bb9e35c7 100644
--- a/config.in
+++ b/config.in
@@ -41,7 +41,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
41# before-sleep 'swaylock -c 000000' 41# before-sleep 'swaylock -c 000000'
42# 42#
43# This will lock your screen after 300 seconds of inactivity, then turn off 43# This will lock your screen after 300 seconds of inactivity, then turn off
44# your displays after another 600 seconds, and turn your screens back on when 44# your displays after another 300 seconds, and turn your screens back on when
45# resumed. It will also lock your screen before your computer goes to sleep. 45# resumed. It will also lock your screen before your computer goes to sleep.
46 46
47### Input configuration 47### Input configuration
diff --git a/include/ipc.h b/include/ipc.h
index 9063b933..6063f69c 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -16,6 +16,7 @@ enum ipc_command_type {
16 IPC_GET_BINDING_MODES = 8, 16 IPC_GET_BINDING_MODES = 8,
17 IPC_GET_CONFIG = 9, 17 IPC_GET_CONFIG = 9,
18 IPC_SEND_TICK = 10, 18 IPC_SEND_TICK = 10,
19 IPC_SYNC = 11,
19 20
20 // sway-specific command types 21 // sway-specific command types
21 IPC_GET_INPUTS = 100, 22 IPC_GET_INPUTS = 100,
diff --git a/include/list.h b/include/list.h
index 03851a82..895f6cc0 100644
--- a/include/list.h
+++ b/include/list.h
@@ -9,7 +9,6 @@ typedef struct {
9 9
10list_t *create_list(void); 10list_t *create_list(void);
11void list_free(list_t *list); 11void list_free(list_t *list);
12void list_foreach(list_t *list, void (*callback)(void* item));
13void list_add(list_t *list, void *item); 12void list_add(list_t *list, void *item);
14void list_insert(list_t *list, int index, void *item); 13void list_insert(list_t *list, int index, void *item);
15void list_del(list_t *list, int index); 14void list_del(list_t *list, int index);
@@ -27,4 +26,10 @@ void list_stable_sort(list_t *list, int compare(const void *a, const void *b));
27void list_swap(list_t *list, int src, int dest); 26void list_swap(list_t *list, int src, int dest);
28// move item to end of list 27// move item to end of list
29void list_move_to_end(list_t *list, void *item); 28void list_move_to_end(list_t *list, void *item);
29
30/* Calls `free` for each item in the list, then frees the list.
31 * Do not use this to free lists of primitives or items that require more
32 * complicated deallocation code.
33 */
34void list_free_items_and_destroy(list_t *list);
30#endif 35#endif
diff --git a/include/readline.h b/include/readline.h
deleted file mode 100644
index ee2eba5d..00000000
--- a/include/readline.h
+++ /dev/null
@@ -1,10 +0,0 @@
1#ifndef _SWAY_READLINE_H
2#define _SWAY_READLINE_H
3
4#include <stdio.h>
5
6char *read_line(FILE *file);
7char *peek_line(FILE *file, int line_offset, long *position);
8char *read_line_buffer(FILE *file, char *string, size_t string_len);
9
10#endif
diff --git a/include/stringop.h b/include/stringop.h
index 919e605c..f7ca60a5 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -3,10 +3,7 @@
3 3
4#include "list.h" 4#include "list.h"
5 5
6// array of whitespace characters to use for delims 6void strip_whitespace(char *str);
7extern const char whitespace[];
8
9char *strip_whitespace(char *str);
10char *strip_comments(char *str); 7char *strip_comments(char *str);
11void strip_quotes(char *str); 8void strip_quotes(char *str);
12 9
@@ -17,9 +14,8 @@ char *lenient_strncat(char *dest, const char *src, size_t len);
17// strcmp that also handles null pointers. 14// strcmp that also handles null pointers.
18int lenient_strcmp(char *a, char *b); 15int lenient_strcmp(char *a, char *b);
19 16
20// Simply split a string with delims, free with `free_flat_list` 17// Simply split a string with delims, free with `list_free_items_and_destroy`
21list_t *split_string(const char *str, const char *delims); 18list_t *split_string(const char *str, const char *delims);
22void free_flat_list(list_t *list);
23 19
24// Splits an argument string, keeping quotes intact 20// Splits an argument string, keeping quotes intact
25char **split_args(const char *str, int *argc); 21char **split_args(const char *str, int *argc);
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 32925369..5d45d78b 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -56,7 +56,7 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
56 * all matching containers. Otherwise, it'll run on the `con` container. If 56 * all matching containers. Otherwise, it'll run on the `con` container. If
57 * `con` is NULL then it'll run on the currently focused container. 57 * `con` is NULL then it'll run on the currently focused container.
58 */ 58 */
59struct cmd_results *execute_command(char *command, struct sway_seat *seat, 59list_t *execute_command(char *command, struct sway_seat *seat,
60 struct sway_container *con); 60 struct sway_container *con);
61/** 61/**
62 * Parse and handles a command during config file loading. 62 * Parse and handles a command during config file loading.
@@ -82,11 +82,11 @@ struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, c
82 */ 82 */
83void free_cmd_results(struct cmd_results *results); 83void free_cmd_results(struct cmd_results *results);
84/** 84/**
85 * Serializes cmd_results to a JSON string. 85 * Serializes a list of cmd_results to a JSON string.
86 * 86 *
87 * Free the JSON string later on. 87 * Free the JSON string later on.
88 */ 88 */
89char *cmd_results_to_json(struct cmd_results *results); 89char *cmd_results_to_json(list_t *res_list);
90 90
91struct cmd_results *add_color(const char *name, 91struct cmd_results *add_color(const char *name,
92 char *buffer, const char *color); 92 char *buffer, const char *color);
@@ -172,7 +172,11 @@ sway_cmd cmd_swaybg_command;
172sway_cmd cmd_swaynag_command; 172sway_cmd cmd_swaynag_command;
173sway_cmd cmd_swap; 173sway_cmd cmd_swap;
174sway_cmd cmd_tiling_drag; 174sway_cmd cmd_tiling_drag;
175sway_cmd cmd_tiling_drag_threshold;
176sway_cmd cmd_title_align;
175sway_cmd cmd_title_format; 177sway_cmd cmd_title_format;
178sway_cmd cmd_titlebar_border_thickness;
179sway_cmd cmd_titlebar_padding;
176sway_cmd cmd_unmark; 180sway_cmd cmd_unmark;
177sway_cmd cmd_urgent; 181sway_cmd cmd_urgent;
178sway_cmd cmd_workspace; 182sway_cmd cmd_workspace;
@@ -180,12 +184,12 @@ sway_cmd cmd_workspace_layout;
180sway_cmd cmd_ws_auto_back_and_forth; 184sway_cmd cmd_ws_auto_back_and_forth;
181sway_cmd cmd_xwayland; 185sway_cmd cmd_xwayland;
182 186
183sway_cmd bar_cmd_activate_button; 187sway_cmd bar_cmd_bindcode;
184sway_cmd bar_cmd_binding_mode_indicator; 188sway_cmd bar_cmd_binding_mode_indicator;
185sway_cmd bar_cmd_bindsym; 189sway_cmd bar_cmd_bindsym;
186sway_cmd bar_cmd_colors; 190sway_cmd bar_cmd_colors;
187sway_cmd bar_cmd_context_button;
188sway_cmd bar_cmd_font; 191sway_cmd bar_cmd_font;
192sway_cmd bar_cmd_gaps;
189sway_cmd bar_cmd_mode; 193sway_cmd bar_cmd_mode;
190sway_cmd bar_cmd_modifier; 194sway_cmd bar_cmd_modifier;
191sway_cmd bar_cmd_output; 195sway_cmd bar_cmd_output;
@@ -194,12 +198,15 @@ sway_cmd bar_cmd_hidden_state;
194sway_cmd bar_cmd_icon_theme; 198sway_cmd bar_cmd_icon_theme;
195sway_cmd bar_cmd_id; 199sway_cmd bar_cmd_id;
196sway_cmd bar_cmd_position; 200sway_cmd bar_cmd_position;
197sway_cmd bar_cmd_secondary_button;
198sway_cmd bar_cmd_separator_symbol; 201sway_cmd bar_cmd_separator_symbol;
199sway_cmd bar_cmd_status_command; 202sway_cmd bar_cmd_status_command;
203sway_cmd bar_cmd_status_edge_padding;
204sway_cmd bar_cmd_status_padding;
200sway_cmd bar_cmd_pango_markup; 205sway_cmd bar_cmd_pango_markup;
201sway_cmd bar_cmd_strip_workspace_numbers; 206sway_cmd bar_cmd_strip_workspace_numbers;
207sway_cmd bar_cmd_strip_workspace_name;
202sway_cmd bar_cmd_swaybar_command; 208sway_cmd bar_cmd_swaybar_command;
209sway_cmd bar_cmd_tray_bindsym;
203sway_cmd bar_cmd_tray_output; 210sway_cmd bar_cmd_tray_output;
204sway_cmd bar_cmd_tray_padding; 211sway_cmd bar_cmd_tray_padding;
205sway_cmd bar_cmd_wrap_scroll; 212sway_cmd bar_cmd_wrap_scroll;
@@ -255,8 +262,9 @@ sway_cmd output_cmd_scale;
255sway_cmd output_cmd_transform; 262sway_cmd output_cmd_transform;
256 263
257sway_cmd seat_cmd_attach; 264sway_cmd seat_cmd_attach;
258sway_cmd seat_cmd_fallback;
259sway_cmd seat_cmd_cursor; 265sway_cmd seat_cmd_cursor;
266sway_cmd seat_cmd_fallback;
267sway_cmd seat_cmd_hide_cursor;
260 268
261sway_cmd cmd_ipc_cmd; 269sway_cmd cmd_ipc_cmd;
262sway_cmd cmd_ipc_events; 270sway_cmd cmd_ipc_events;
diff --git a/include/sway/config.h b/include/sway/config.h
index c44533ee..96fe899b 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -6,6 +6,7 @@
6#include <time.h> 6#include <time.h>
7#include <wlr/types/wlr_box.h> 7#include <wlr/types/wlr_box.h>
8#include <xkbcommon/xkbcommon.h> 8#include <xkbcommon/xkbcommon.h>
9#include "../include/config.h"
9#include "list.h" 10#include "list.h"
10#include "swaynag.h" 11#include "swaynag.h"
11#include "tree/container.h" 12#include "tree/container.h"
@@ -26,7 +27,8 @@ struct sway_variable {
26enum binding_input_type { 27enum binding_input_type {
27 BINDING_KEYCODE, 28 BINDING_KEYCODE,
28 BINDING_KEYSYM, 29 BINDING_KEYSYM,
29 BINDING_MOUSE, 30 BINDING_MOUSECODE,
31 BINDING_MOUSESYM,
30}; 32};
31 33
32enum binding_flags { 34enum binding_flags {
@@ -140,6 +142,7 @@ struct seat_config {
140 char *name; 142 char *name;
141 int fallback; // -1 means not set 143 int fallback; // -1 means not set
142 list_t *attachments; // list of seat_attachment configs 144 list_t *attachments; // list of seat_attachment configs
145 int hide_cursor_timeout;
143}; 146};
144 147
145enum config_dpms { 148enum config_dpms {
@@ -224,9 +227,13 @@ struct bar_config {
224 bool wrap_scroll; 227 bool wrap_scroll;
225 char *separator_symbol; 228 char *separator_symbol;
226 bool strip_workspace_numbers; 229 bool strip_workspace_numbers;
230 bool strip_workspace_name;
227 bool binding_mode_indicator; 231 bool binding_mode_indicator;
228 bool verbose; 232 bool verbose;
233 struct side_gaps gaps;
229 pid_t pid; 234 pid_t pid;
235 int status_padding;
236 int status_edge_padding;
230 struct { 237 struct {
231 char *background; 238 char *background;
232 char *statusline; 239 char *statusline;
@@ -250,6 +257,13 @@ struct bar_config {
250 char *binding_mode_bg; 257 char *binding_mode_bg;
251 char *binding_mode_text; 258 char *binding_mode_text;
252 } colors; 259 } colors;
260
261#if HAVE_TRAY
262 char *icon_theme;
263 const char *tray_bindings[10]; // mouse buttons 0-9
264 list_t *tray_outputs; // char *
265 int tray_padding;
266#endif
253}; 267};
254 268
255struct bar_binding { 269struct bar_binding {
@@ -356,6 +370,12 @@ enum mouse_warping_mode {
356 WARP_CONTAINER 370 WARP_CONTAINER
357}; 371};
358 372
373enum alignment {
374 ALIGN_LEFT,
375 ALIGN_CENTER,
376 ALIGN_RIGHT
377};
378
359/** 379/**
360 * The configuration struct. The result of loading a config file. 380 * The configuration struct. The result of loading a config file.
361 */ 381 */
@@ -390,6 +410,9 @@ struct sway_config {
390 size_t font_height; 410 size_t font_height;
391 size_t font_baseline; 411 size_t font_baseline;
392 bool pango_markup; 412 bool pango_markup;
413 int titlebar_border_thickness;
414 int titlebar_h_padding;
415 int titlebar_v_padding;
393 size_t urgent_timeout; 416 size_t urgent_timeout;
394 enum sway_fowa focus_on_window_activation; 417 enum sway_fowa focus_on_window_activation;
395 enum sway_popup_during_fullscreen popup_during_fullscreen; 418 enum sway_popup_during_fullscreen popup_during_fullscreen;
@@ -406,7 +429,10 @@ struct sway_config {
406 bool validating; 429 bool validating;
407 bool auto_back_and_forth; 430 bool auto_back_and_forth;
408 bool show_marks; 431 bool show_marks;
432 enum alignment title_align;
433
409 bool tiling_drag; 434 bool tiling_drag;
435 int tiling_drag_threshold;
410 436
411 bool smart_gaps; 437 bool smart_gaps;
412 int gaps_inner; 438 int gaps_inner;
@@ -415,6 +441,8 @@ struct sway_config {
415 list_t *config_chain; 441 list_t *config_chain;
416 const char *current_config_path; 442 const char *current_config_path;
417 const char *current_config; 443 const char *current_config;
444 int current_config_line_number;
445 char *current_config_line;
418 446
419 enum sway_container_border border; 447 enum sway_container_border border;
420 enum sway_container_border floating_border; 448 enum sway_container_border floating_border;
@@ -480,6 +508,11 @@ bool read_config(FILE *file, struct sway_config *config,
480 struct swaynag_instance *swaynag); 508 struct swaynag_instance *swaynag);
481 509
482/** 510/**
511 * Adds a warning entry to the swaynag instance used for errors.
512 */
513void config_add_swaynag_warning(char *fmt, ...);
514
515/**
483 * Free config struct 516 * Free config struct
484 */ 517 */
485void free_config(struct sway_config *config); 518void free_config(struct sway_config *config);
@@ -516,7 +549,7 @@ struct seat_attachment_config *seat_attachment_config_new(void);
516struct seat_attachment_config *seat_config_get_attachment( 549struct seat_attachment_config *seat_config_get_attachment(
517 struct seat_config *seat_config, char *identifier); 550 struct seat_config *seat_config, char *identifier);
518 551
519void apply_seat_config(struct seat_config *seat); 552struct seat_config *store_seat_config(struct seat_config *seat);
520 553
521int output_name_cmp(const void *item, const void *data); 554int output_name_cmp(const void *item, const void *data);
522 555
@@ -535,8 +568,6 @@ void apply_output_config_to_outputs(struct output_config *oc);
535 568
536void free_output_config(struct output_config *oc); 569void free_output_config(struct output_config *oc);
537 570
538void create_default_output_configs(void);
539
540int workspace_output_cmp_workspace(const void *a, const void *b); 571int workspace_output_cmp_workspace(const void *a, const void *b);
541 572
542int sway_binding_cmp(const void *a, const void *b); 573int sway_binding_cmp(const void *a, const void *b);
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 50ac453b..77aa0ea1 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -1,10 +1,17 @@
1#ifndef _SWAY_INPUT_CURSOR_H 1#ifndef _SWAY_INPUT_CURSOR_H
2#define _SWAY_INPUT_CURSOR_H 2#define _SWAY_INPUT_CURSOR_H
3#include <stdbool.h>
3#include <stdint.h> 4#include <stdint.h>
5#include <wlr/types/wlr_surface.h>
4#include "sway/input/seat.h" 6#include "sway/input/seat.h"
5 7
6#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32 8#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32
7 9
10#define SWAY_SCROLL_UP KEY_MAX + 1
11#define SWAY_SCROLL_DOWN KEY_MAX + 2
12#define SWAY_SCROLL_LEFT KEY_MAX + 3
13#define SWAY_SCROLL_RIGHT KEY_MAX + 4
14
8struct sway_cursor { 15struct sway_cursor {
9 struct sway_seat *seat; 16 struct sway_seat *seat;
10 struct wlr_cursor *cursor; 17 struct wlr_cursor *cursor;
@@ -16,6 +23,8 @@ struct sway_cursor {
16 23
17 const char *image; 24 const char *image;
18 struct wl_client *image_client; 25 struct wl_client *image_client;
26 struct wlr_surface *image_surface;
27 int hotspot_x, hotspot_y;
19 28
20 struct wl_listener motion; 29 struct wl_listener motion;
21 struct wl_listener motion_absolute; 30 struct wl_listener motion_absolute;
@@ -33,11 +42,20 @@ struct sway_cursor {
33 42
34 struct wl_listener request_set_cursor; 43 struct wl_listener request_set_cursor;
35 44
45 struct wl_event_source *hide_source;
46 bool hidden;
47
36 // Mouse binding state 48 // Mouse binding state
37 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 49 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
38 size_t pressed_button_count; 50 size_t pressed_button_count;
39}; 51};
40 52
53struct sway_node;
54
55struct sway_node *node_at_coords(
56 struct sway_seat *seat, double lx, double ly,
57 struct wlr_surface **surface, double *sx, double *sy);
58
41void sway_cursor_destroy(struct sway_cursor *cursor); 59void sway_cursor_destroy(struct sway_cursor *cursor);
42struct sway_cursor *sway_cursor_create(struct sway_seat *seat); 60struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
43 61
@@ -48,6 +66,10 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
48 */ 66 */
49void cursor_rebase(struct sway_cursor *cursor); 67void cursor_rebase(struct sway_cursor *cursor);
50 68
69void cursor_handle_activity(struct sway_cursor *cursor);
70void cursor_unhide(struct sway_cursor *cursor);
71int cursor_get_timeout(struct sway_cursor *cursor);
72
51/** 73/**
52 * Like cursor_rebase, but also allows focus to change when the cursor enters a 74 * Like cursor_rebase, but also allows focus to change when the cursor enters a
53 * new container. 75 * new container.
@@ -58,12 +80,27 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
58 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 80 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
59 enum wlr_button_state state); 81 enum wlr_button_state state);
60 82
83void dispatch_cursor_axis(struct sway_cursor *cursor,
84 struct wlr_event_pointer_axis *event);
85
61void cursor_set_image(struct sway_cursor *cursor, const char *image, 86void cursor_set_image(struct sway_cursor *cursor, const char *image,
62 struct wl_client *client); 87 struct wl_client *client);
63 88
89void cursor_set_image_surface(struct sway_cursor *cursor,
90 struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y,
91 struct wl_client *client);
92
64void cursor_warp_to_container(struct sway_cursor *cursor, 93void cursor_warp_to_container(struct sway_cursor *cursor,
65 struct sway_container *container); 94 struct sway_container *container);
66 95
67void cursor_warp_to_workspace(struct sway_cursor *cursor, 96void cursor_warp_to_workspace(struct sway_cursor *cursor,
68 struct sway_workspace *workspace); 97 struct sway_workspace *workspace);
98
99uint32_t get_mouse_bindsym(const char *name, char **error);
100
101uint32_t get_mouse_bindcode(const char *name, char **error);
102
103// Considers both bindsym and bindcode
104uint32_t get_mouse_button(const char *name, char **error);
105
69#endif 106#endif
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 219aa9ba..8e8bf1f2 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -37,6 +37,10 @@ void input_manager_configure_xcursor(void);
37 37
38void input_manager_apply_input_config(struct input_config *input_config); 38void input_manager_apply_input_config(struct input_config *input_config);
39 39
40void input_manager_reset_input(struct sway_input_device *input_device);
41
42void input_manager_reset_all_inputs();
43
40void input_manager_apply_seat_config(struct seat_config *seat_config); 44void input_manager_apply_seat_config(struct seat_config *seat_config);
41 45
42struct sway_seat *input_manager_get_default_seat(void); 46struct sway_seat *input_manager_get_default_seat(void);
@@ -44,6 +48,12 @@ struct sway_seat *input_manager_get_default_seat(void);
44struct sway_seat *input_manager_get_seat(const char *seat_name); 48struct sway_seat *input_manager_get_seat(const char *seat_name);
45 49
46/** 50/**
51 * If none of the seat configs have a fallback setting (either true or false),
52 * create the default seat (if needed) and set it as the fallback
53 */
54void input_manager_verify_fallback_seat(void);
55
56/**
47 * Gets the last seat the user interacted with 57 * Gets the last seat the user interacted with
48 */ 58 */
49struct sway_seat *input_manager_current_seat(void); 59struct sway_seat *input_manager_current_seat(void);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index bef2af77..d2f14895 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -6,6 +6,17 @@
6#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
7#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
8 8
9struct sway_seat;
10
11struct sway_seatop_impl {
12 void (*motion)(struct sway_seat *seat, uint32_t time_msec);
13 void (*finish)(struct sway_seat *seat);
14 void (*abort)(struct sway_seat *seat);
15 void (*unref)(struct sway_seat *seat, struct sway_container *con);
16 void (*render)(struct sway_seat *seat, struct sway_output *output,
17 pixman_region32_t *damage);
18};
19
9struct sway_seat_device { 20struct sway_seat_device {
10 struct sway_seat *sway_seat; 21 struct sway_seat *sway_seat;
11 struct sway_input_device *input_device; 22 struct sway_input_device *input_device;
@@ -35,15 +46,6 @@ struct sway_drag_icon {
35 struct wl_listener destroy; 46 struct wl_listener destroy;
36}; 47};
37 48
38enum sway_seat_operation {
39 OP_NONE,
40 OP_DOWN,
41 OP_MOVE_FLOATING,
42 OP_MOVE_TILING,
43 OP_RESIZE_FLOATING,
44 OP_RESIZE_TILING,
45};
46
47struct sway_seat { 49struct sway_seat {
48 struct wlr_seat *wlr_seat; 50 struct wlr_seat *wlr_seat;
49 struct sway_cursor *cursor; 51 struct sway_cursor *cursor;
@@ -63,19 +65,10 @@ struct sway_seat {
63 int32_t touch_id; 65 int32_t touch_id;
64 double touch_x, touch_y; 66 double touch_x, touch_y;
65 67
66 // Operations (drag and resize) 68 // Seat operations (drag and resize)
67 enum sway_seat_operation operation; 69 const struct sway_seatop_impl *seatop_impl;
68 struct sway_container *op_container; 70 void *seatop_data;
69 struct sway_node *op_target_node; // target for tiling move 71 uint32_t seatop_button;
70 enum wlr_edges op_target_edge;
71 struct wlr_box op_drop_box;
72 enum wlr_edges op_resize_edge;
73 uint32_t op_button;
74 bool op_resize_preserve_ratio;
75 double op_ref_lx, op_ref_ly; // cursor's x/y at start of op
76 double op_ref_width, op_ref_height; // container's size at start of op
77 double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op
78 bool op_moved; // if the mouse moved during a down op
79 72
80 uint32_t last_button; 73 uint32_t last_button;
81 uint32_t last_button_serial; 74 uint32_t last_button_serial;
@@ -99,6 +92,9 @@ void seat_add_device(struct sway_seat *seat,
99void seat_configure_device(struct sway_seat *seat, 92void seat_configure_device(struct sway_seat *seat,
100 struct sway_input_device *device); 93 struct sway_input_device *device);
101 94
95void seat_reset_device(struct sway_seat *seat,
96 struct sway_input_device *input_device);
97
102void seat_remove_device(struct sway_seat *seat, 98void seat_remove_device(struct sway_seat *seat,
103 struct sway_input_device *device); 99 struct sway_input_device *device);
104 100
@@ -174,33 +170,65 @@ void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
174 170
175struct seat_config *seat_get_config(struct sway_seat *seat); 171struct seat_config *seat_get_config(struct sway_seat *seat);
176 172
173struct seat_config *seat_get_config_by_name(const char *name);
174
177bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); 175bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
178 176
179void drag_icon_update_position(struct sway_drag_icon *icon); 177void drag_icon_update_position(struct sway_drag_icon *icon);
180 178
181void seat_begin_down(struct sway_seat *seat, struct sway_container *con, 179void seatop_begin_down(struct sway_seat *seat,
182 uint32_t button, double sx, double sy); 180 struct sway_container *con, uint32_t button, int sx, int sy);
183 181
184void seat_begin_move_floating(struct sway_seat *seat, 182void seatop_begin_move_floating(struct sway_seat *seat,
185 struct sway_container *con, uint32_t button); 183 struct sway_container *con, uint32_t button);
186 184
187void seat_begin_move_tiling(struct sway_seat *seat, 185void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
188 struct sway_container *con, uint32_t button); 186 struct sway_container *con, uint32_t button);
189 187
190void seat_begin_resize_floating(struct sway_seat *seat, 188void seatop_begin_move_tiling(struct sway_seat *seat,
189 struct sway_container *con, uint32_t button);
190
191void seatop_begin_resize_floating(struct sway_seat *seat,
191 struct sway_container *con, uint32_t button, enum wlr_edges edge); 192 struct sway_container *con, uint32_t button, enum wlr_edges edge);
192 193
193void seat_begin_resize_tiling(struct sway_seat *seat, 194void seatop_begin_resize_tiling(struct sway_seat *seat,
194 struct sway_container *con, uint32_t button, enum wlr_edges edge); 195 struct sway_container *con, uint32_t button, enum wlr_edges edge);
195 196
196struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, 197struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
197 struct sway_workspace *workspace); 198 struct sway_workspace *workspace);
198 199
199void seat_end_mouse_operation(struct sway_seat *seat);
200
201void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 200void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
202 uint32_t button, enum wlr_button_state state); 201 uint32_t button, enum wlr_button_state state);
203 202
204void seat_consider_warp_to_focus(struct sway_seat *seat); 203void seat_consider_warp_to_focus(struct sway_seat *seat);
205 204
205bool seat_doing_seatop(struct sway_seat *seat);
206
207void seatop_motion(struct sway_seat *seat, uint32_t time_msec);
208
209/**
210 * End a seatop and apply the affects.
211 */
212void seatop_finish(struct sway_seat *seat);
213
214/**
215 * End a seatop without applying the affects.
216 */
217void seatop_abort(struct sway_seat *seat);
218
219/**
220 * Instructs the seatop implementation to drop any references to the given
221 * container (eg. because the container is destroying).
222 * The seatop may choose to abort itself in response to this.
223 */
224void seatop_unref(struct sway_seat *seat, struct sway_container *con);
225
226/**
227 * Instructs a seatop to render anything that it needs to render
228 * (eg. dropzone for move-tiling)
229 */
230void seatop_render(struct sway_seat *seat, struct sway_output *output,
231 pixman_region32_t *damage);
232
233
206#endif 234#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index 43c1ab96..bdf9614d 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -84,9 +84,7 @@ void output_damage_box(struct sway_output *output, struct wlr_box *box);
84void output_damage_whole_container(struct sway_output *output, 84void output_damage_whole_container(struct sway_output *output,
85 struct sway_container *con); 85 struct sway_container *con);
86 86
87struct sway_output *output_by_name(const char *name); 87struct sway_output *output_by_name_or_id(const char *name_or_id);
88
89struct sway_output *output_by_identifier(const char *identifier);
90 88
91void output_sort_workspaces(struct sway_output *output); 89void output_sort_workspaces(struct sway_output *output);
92 90
@@ -148,4 +146,12 @@ enum sway_container_layout output_get_default_layout(
148 146
149void output_add_listeners(struct sway_output *output); 147void output_add_listeners(struct sway_output *output);
150 148
149void render_rect(struct wlr_output *wlr_output,
150 pixman_region32_t *output_damage, const struct wlr_box *_box,
151 float color[static 4]);
152
153void premultiply_alpha(float color[4], float opacity);
154
155void scale_box(struct wlr_box *box, float scale);
156
151#endif 157#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index f907aad2..9a432cb2 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -10,12 +10,6 @@
10struct sway_view; 10struct sway_view;
11struct sway_seat; 11struct sway_seat;
12 12
13#define TITLEBAR_BORDER_THICKNESS 1
14
15// Padding includes titlebar border
16#define TITLEBAR_H_PADDING 3
17#define TITLEBAR_V_PADDING 4
18
19enum sway_container_layout { 13enum sway_container_layout {
20 L_NONE, 14 L_NONE,
21 L_HORIZ, 15 L_HORIZ,
@@ -339,4 +333,6 @@ void container_add_mark(struct sway_container *container, char *mark);
339 333
340void container_update_marks_textures(struct sway_container *container); 334void container_update_marks_textures(struct sway_container *container);
341 335
336void container_raise_floating(struct sway_container *con);
337
342#endif 338#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 4716c688..5cc9777b 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -195,6 +195,7 @@ struct sway_view_child {
195 195
196 struct sway_view *view; 196 struct sway_view *view;
197 struct wlr_surface *surface; 197 struct wlr_surface *surface;
198 bool mapped;
198 199
199 struct wl_listener surface_commit; 200 struct wl_listener surface_commit;
200 struct wl_listener surface_new_subsurface; 201 struct wl_listener surface_new_subsurface;
@@ -203,6 +204,12 @@ struct sway_view_child {
203 struct wl_listener surface_destroy; 204 struct wl_listener surface_destroy;
204}; 205};
205 206
207struct sway_subsurface {
208 struct sway_view_child child;
209
210 struct wl_listener destroy;
211};
212
206struct sway_xdg_popup_v6 { 213struct sway_xdg_popup_v6 {
207 struct sway_view_child child; 214 struct sway_view_child child;
208 215
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 95b20510..2d9ba0d9 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -1,6 +1,7 @@
1#ifndef _SWAYBAR_BAR_H 1#ifndef _SWAYBAR_BAR_H
2#define _SWAYBAR_BAR_H 2#define _SWAYBAR_BAR_H
3#include <wayland-client.h> 3#include <wayland-client.h>
4#include "config.h"
4#include "input.h" 5#include "input.h"
5#include "pool-buffer.h" 6#include "pool-buffer.h"
6#include "wlr-layer-shell-unstable-v1-client-protocol.h" 7#include "wlr-layer-shell-unstable-v1-client-protocol.h"
@@ -8,6 +9,9 @@
8 9
9struct swaybar_config; 10struct swaybar_config;
10struct swaybar_output; 11struct swaybar_output;
12#if HAVE_TRAY
13struct swaybar_tray;
14#endif
11struct swaybar_workspace; 15struct swaybar_workspace;
12struct loop; 16struct loop;
13 17
@@ -38,6 +42,10 @@ struct swaybar {
38 int ipc_socketfd; 42 int ipc_socketfd;
39 43
40 struct wl_list outputs; // swaybar_output::link 44 struct wl_list outputs; // swaybar_output::link
45
46#if HAVE_TRAY
47 struct swaybar_tray *tray;
48#endif
41}; 49};
42 50
43struct swaybar_output { 51struct swaybar_output {
@@ -53,6 +61,7 @@ struct swaybar_output {
53 struct wl_list hotspots; // swaybar_hotspot::link 61 struct wl_list hotspots; // swaybar_hotspot::link
54 62
55 char *name; 63 char *name;
64 char *identifier;
56 bool focused; 65 bool focused;
57 66
58 uint32_t width, height; 67 uint32_t width, height;
@@ -62,12 +71,15 @@ struct swaybar_output {
62 struct pool_buffer *current_buffer; 71 struct pool_buffer *current_buffer;
63 bool dirty; 72 bool dirty;
64 bool frame_scheduled; 73 bool frame_scheduled;
74
75 uint32_t output_height, output_width, output_x, output_y;
65}; 76};
66 77
67struct swaybar_workspace { 78struct swaybar_workspace {
68 struct wl_list link; // swaybar_output::workspaces 79 struct wl_list link; // swaybar_output::workspaces
69 int num; 80 int num;
70 char *name; 81 char *name;
82 char *label;
71 bool focused; 83 bool focused;
72 bool visible; 84 bool visible;
73 bool urgent; 85 bool urgent;
@@ -77,6 +89,8 @@ bool bar_setup(struct swaybar *bar, const char *socket_path);
77void bar_run(struct swaybar *bar); 89void bar_run(struct swaybar *bar);
78void bar_teardown(struct swaybar *bar); 90void bar_teardown(struct swaybar *bar);
79 91
92void set_bar_dirty(struct swaybar *bar);
93
80/* 94/*
81 * Determines whether the bar should be visible and changes it to be so. 95 * Determines whether the bar should be visible and changes it to be so.
82 * If the current visibility of the bar is the different to what it should be, 96 * If the current visibility of the bar is the different to what it should be,
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index 5d40790a..add0a1cf 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -3,6 +3,7 @@
3#include <stdbool.h> 3#include <stdbool.h>
4#include <stdint.h> 4#include <stdint.h>
5#include <wayland-client.h> 5#include <wayland-client.h>
6#include "../include/config.h"
6#include "list.h" 7#include "list.h"
7#include "util.h" 8#include "util.h"
8 9
@@ -34,6 +35,7 @@ struct swaybar_config {
34 char *hidden_state; 35 char *hidden_state;
35 char *modifier; 36 char *modifier;
36 bool strip_workspace_numbers; 37 bool strip_workspace_numbers;
38 bool strip_workspace_name;
37 bool binding_mode_indicator; 39 bool binding_mode_indicator;
38 bool wrap_scroll; 40 bool wrap_scroll;
39 bool workspace_buttons; 41 bool workspace_buttons;
@@ -41,6 +43,14 @@ struct swaybar_config {
41 struct wl_list outputs; // config_output::link 43 struct wl_list outputs; // config_output::link
42 bool all_outputs; 44 bool all_outputs;
43 int height; 45 int height;
46 int status_padding;
47 int status_edge_padding;
48 struct {
49 int top;
50 int right;
51 int bottom;
52 int left;
53 } gaps;
44 54
45 struct { 55 struct {
46 uint32_t background; 56 uint32_t background;
@@ -57,6 +67,14 @@ struct swaybar_config {
57 struct box_colors urgent_workspace; 67 struct box_colors urgent_workspace;
58 struct box_colors binding_mode; 68 struct box_colors binding_mode;
59 } colors; 69 } colors;
70
71#if HAVE_TRAY
72 char *icon_theme;
73 char *tray_bindings[10]; // mouse buttons 0-9
74 bool tray_hidden;
75 list_t *tray_outputs; // char *
76 int tray_padding;
77#endif
60}; 78};
61 79
62struct swaybar_config *init_config(void); 80struct swaybar_config *init_config(void);
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h
index 3f1ecc25..aa4415ff 100644
--- a/include/swaybar/i3bar.h
+++ b/include/swaybar/i3bar.h
@@ -27,6 +27,7 @@ struct i3bar_block {
27void i3bar_block_unref(struct i3bar_block *block); 27void i3bar_block_unref(struct i3bar_block *block);
28bool i3bar_handle_readable(struct status_line *status); 28bool i3bar_handle_readable(struct status_line *status);
29enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 29enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
30 struct i3bar_block *block, int x, int y, enum x11_button button); 30 struct i3bar_block *block, int x, int y, int rx, int ry, int w, int h,
31 uint32_t button);
31 32
32#endif 33#endif
diff --git a/include/swaybar/input.h b/include/swaybar/input.h
index a552e7ac..4b46b0de 100644
--- a/include/swaybar/input.h
+++ b/include/swaybar/input.h
@@ -4,6 +4,12 @@
4#include <wayland-client.h> 4#include <wayland-client.h>
5#include "list.h" 5#include "list.h"
6 6
7#define SWAY_SCROLL_UP KEY_MAX + 1
8#define SWAY_SCROLL_DOWN KEY_MAX + 2
9#define SWAY_SCROLL_LEFT KEY_MAX + 3
10#define SWAY_SCROLL_RIGHT KEY_MAX + 4
11
12struct swaybar;
7struct swaybar_output; 13struct swaybar_output;
8 14
9struct swaybar_pointer { 15struct swaybar_pointer {
@@ -13,6 +19,7 @@ struct swaybar_pointer {
13 struct wl_surface *cursor_surface; 19 struct wl_surface *cursor_surface;
14 struct swaybar_output *current; 20 struct swaybar_output *current;
15 int x, y; 21 int x, y;
22 uint32_t serial;
16}; 23};
17 24
18enum x11_button { 25enum x11_button {
@@ -37,13 +44,16 @@ struct swaybar_hotspot {
37 struct wl_list link; // swaybar_output::hotspots 44 struct wl_list link; // swaybar_output::hotspots
38 int x, y, width, height; 45 int x, y, width, height;
39 enum hotspot_event_handling (*callback)(struct swaybar_output *output, 46 enum hotspot_event_handling (*callback)(struct swaybar_output *output,
40 int x, int y, enum x11_button button, void *data); 47 struct swaybar_hotspot *hotspot, int x, int y, uint32_t button,
48 void *data);
41 void (*destroy)(void *data); 49 void (*destroy)(void *data);
42 void *data; 50 void *data;
43}; 51};
44 52
45extern const struct wl_seat_listener seat_listener; 53extern const struct wl_seat_listener seat_listener;
46 54
55void update_cursor(struct swaybar *bar);
56
47void free_hotspots(struct wl_list *list); 57void free_hotspots(struct wl_list *list);
48 58
49#endif 59#endif
diff --git a/include/swaybar/tray/host.h b/include/swaybar/tray/host.h
new file mode 100644
index 00000000..2d4cf82b
--- /dev/null
+++ b/include/swaybar/tray/host.h
@@ -0,0 +1,17 @@
1#ifndef _SWAYBAR_TRAY_HOST_H
2#define _SWAYBAR_TRAY_HOST_H
3
4#include <stdbool.h>
5
6struct swaybar_tray;
7
8struct swaybar_host {
9 struct swaybar_tray *tray;
10 char *service;
11 char *watcher_interface;
12};
13
14bool init_host(struct swaybar_host *host, char *protocol, struct swaybar_tray *tray);
15void finish_host(struct swaybar_host *host);
16
17#endif
diff --git a/include/swaybar/tray/icon.h b/include/swaybar/tray/icon.h
index 1cc6ff9c..7a6c400c 100644
--- a/include/swaybar/tray/icon.h
+++ b/include/swaybar/tray/icon.h
@@ -1,16 +1,44 @@
1#ifndef _SWAYBAR_ICON_H 1#ifndef _SWAYBAR_TRAY_ICON_H
2#define _SWAYBAR_ICON_H 2#define _SWAYBAR_TRAY_ICON_H
3 3
4#include <stdint.h> 4#include "list.h"
5#include <stdbool.h>
6#include <client/cairo.h>
7 5
8/** 6enum subdir_type {
9 * Returns the image found by `name` that is closest to `size` 7 THRESHOLD,
10 */ 8 SCALABLE,
11cairo_surface_t *find_icon(const char *name, int size); 9 FIXED
10};
11
12struct icon_theme_subdir {
13 char *name;
14 int size;
15 enum subdir_type type;
16 int max_size;
17 int min_size;
18 int threshold;
19};
20
21struct icon_theme {
22 char *name;
23 char *comment;
24 char *inherits;
25 list_t *directories; // char *
12 26
13/* Struct used internally only */ 27 char *dir;
14struct subdir; 28 list_t *subdirs; // struct icon_theme_subdir *
29};
30
31void init_themes(list_t **themes, list_t **basedirs);
32void finish_themes(list_t *themes, list_t *basedirs);
33
34/*
35 * Finds an icon of a specified size given a list of themes and base directories.
36 * If the icon is found, the pointers min_size & max_size are set to minimum &
37 * maximum size that the icon can be scaled to, respectively.
38 * Returns: path of icon (which should be freed), or NULL if the icon is not found.
39 */
40char *find_icon(list_t *themes, list_t *basedirs, char *name, int size,
41 char *theme, int *min_size, int *max_size);
42char *find_icon_in_dir(char *name, char *dir, int *min_size, int *max_size);
15 43
16#endif /* _SWAYBAR_ICON_H */ 44#endif
diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h
new file mode 100644
index 00000000..4238bdb8
--- /dev/null
+++ b/include/swaybar/tray/item.h
@@ -0,0 +1,49 @@
1#ifndef _SWAYBAR_TRAY_ITEM_H
2#define _SWAYBAR_TRAY_ITEM_H
3
4#include <cairo.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include "swaybar/tray/tray.h"
8#include "list.h"
9
10struct swaybar_output;
11
12struct swaybar_pixmap {
13 int size;
14 unsigned char pixels[];
15};
16
17struct swaybar_sni {
18 // icon properties
19 struct swaybar_tray *tray;
20 cairo_surface_t *icon;
21 int min_size;
22 int max_size;
23
24 // dbus properties
25 char *watcher_id;
26 char *service;
27 char *path;
28 char *interface;
29
30 char *status;
31 char *icon_name;
32 list_t *icon_pixmap; // struct swaybar_pixmap *
33 char *attention_icon_name;
34 list_t *attention_icon_pixmap; // struct swaybar_pixmap *
35 bool item_is_menu;
36 char *menu;
37 char *icon_theme_path; // non-standard KDE property
38
39 sd_bus_slot *new_icon_slot;
40 sd_bus_slot *new_attention_icon_slot;
41 sd_bus_slot *new_status_slot;
42};
43
44struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray);
45void destroy_sni(struct swaybar_sni *sni);
46uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
47 struct swaybar_sni *sni);
48
49#endif
diff --git a/include/swaybar/tray/sni.h b/include/swaybar/tray/sni.h
deleted file mode 100644
index c2544e2a..00000000
--- a/include/swaybar/tray/sni.h
+++ /dev/null
@@ -1,82 +0,0 @@
1#ifndef _SWAYBAR_SNI_H
2#define _SWAYBAR_SNI_H
3
4#include <stdbool.h>
5#include <client/cairo.h>
6
7struct StatusNotifierItem {
8 /* Name registered to sni watcher */
9 char *name;
10 /* Unique bus name, needed for determining signal origins */
11 char *unique_name;
12 bool kde_special_snowflake;
13
14 cairo_surface_t *image;
15 bool dirty;
16};
17
18/* Each output holds an sni_icon_ref of each item to render */
19struct sni_icon_ref {
20 cairo_surface_t *icon;
21 struct StatusNotifierItem *ref;
22};
23
24struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
25 int height);
26
27void sni_icon_ref_free(struct sni_icon_ref *sni_ref);
28
29/**
30 * Will return a new item and get its icon. (see warning below)
31 * May return `NULL` if `name` is not valid.
32 */
33struct StatusNotifierItem *sni_create(const char *name);
34
35/**
36 * `item` must be a struct StatusNotifierItem *
37 * `str` must be a NUL terminated char *
38 *
39 * Returns 0 if `item` has a name of `str`
40 */
41int sni_str_cmp(const void *item, const void *str);
42
43/**
44 * Returns 0 if `item` has a unique name of `str` or if
45 * `item->unique_name == NULL`
46 */
47int sni_uniq_cmp(const void *item, const void *str);
48
49/**
50 * Gets an icon for the given item if found.
51 *
52 * XXX
53 * This function keeps a reference to the item until it gets responses, make
54 * sure that the reference and item are valid during this time.
55 */
56void get_icon(struct StatusNotifierItem *item);
57
58/**
59 * Calls the "activate" method on the given StatusNotifierItem
60 *
61 * x and y should be where the item was clicked
62 */
63void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y);
64
65/**
66 * Asks the item to draw a context menu at the given x and y coords
67 */
68void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y);
69
70/**
71 * Calls the "secondary activate" method on the given StatusNotifierItem
72 *
73 * x and y should be where the item was clicked
74 */
75void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y);
76
77/**
78 * Deconstructs `item`
79 */
80void sni_free(struct StatusNotifierItem *item);
81
82#endif /* _SWAYBAR_SNI_H */
diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h
new file mode 100644
index 00000000..8958b69a
--- /dev/null
+++ b/include/swaybar/tray/tray.h
@@ -0,0 +1,40 @@
1#ifndef _SWAYBAR_TRAY_TRAY_H
2#define _SWAYBAR_TRAY_TRAY_H
3
4#include "config.h"
5#ifdef HAVE_SYSTEMD
6#include <systemd/sd-bus.h>
7#elif HAVE_ELOGIND
8#include <elogind/sd-bus.h>
9#endif
10#include <cairo.h>
11#include <stdint.h>
12#include "swaybar/tray/host.h"
13#include "list.h"
14
15struct swaybar;
16struct swaybar_output;
17struct swaybar_watcher;
18
19struct swaybar_tray {
20 struct swaybar *bar;
21
22 int fd;
23 sd_bus *bus;
24
25 struct swaybar_host host_xdg;
26 struct swaybar_host host_kde;
27 list_t *items; // struct swaybar_sni *
28 struct swaybar_watcher *watcher_xdg;
29 struct swaybar_watcher *watcher_kde;
30
31 list_t *basedirs; // char *
32 list_t *themes; // struct swaybar_theme *
33};
34
35struct swaybar_tray *create_tray(struct swaybar *bar);
36void destroy_tray(struct swaybar_tray *tray);
37void tray_in(int fd, short mask, void *data);
38uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x);
39
40#endif
diff --git a/include/swaybar/tray/watcher.h b/include/swaybar/tray/watcher.h
new file mode 100644
index 00000000..8f276da8
--- /dev/null
+++ b/include/swaybar/tray/watcher.h
@@ -0,0 +1,18 @@
1#ifndef _SWAYBAR_TRAY_WATCHER_H
2#define _SWAYBAR_TRAY_WATCHER_H
3
4#include "swaybar/tray/tray.h"
5#include "list.h"
6
7struct swaybar_watcher {
8 char *interface;
9 sd_bus *bus;
10 list_t *hosts;
11 list_t *items;
12 int version;
13};
14
15struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus);
16void destroy_watcher(struct swaybar_watcher *watcher);
17
18#endif
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index 18af7ab4..516a56f4 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -22,6 +22,7 @@ enum auth_state {
22struct swaylock_colorset { 22struct swaylock_colorset {
23 uint32_t input; 23 uint32_t input;
24 uint32_t cleared; 24 uint32_t cleared;
25 uint32_t caps_lock;
25 uint32_t verifying; 26 uint32_t verifying;
26 uint32_t wrong; 27 uint32_t wrong;
27}; 28};
@@ -30,6 +31,8 @@ struct swaylock_colors {
30 uint32_t background; 31 uint32_t background;
31 uint32_t bs_highlight; 32 uint32_t bs_highlight;
32 uint32_t key_highlight; 33 uint32_t key_highlight;
34 uint32_t caps_lock_bs_highlight;
35 uint32_t caps_lock_key_highlight;
33 uint32_t separator; 36 uint32_t separator;
34 struct swaylock_colorset inside; 37 struct swaylock_colorset inside;
35 struct swaylock_colorset line; 38 struct swaylock_colorset line;
@@ -45,6 +48,8 @@ struct swaylock_args {
45 uint32_t thickness; 48 uint32_t thickness;
46 bool ignore_empty; 49 bool ignore_empty;
47 bool show_indicator; 50 bool show_indicator;
51 bool show_caps_lock_text;
52 bool show_caps_lock_indicator;
48 bool daemonize; 53 bool daemonize;
49}; 54};
50 55
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h
index a32d1503..0fd1eb50 100644
--- a/include/swaynag/swaynag.h
+++ b/include/swaynag/swaynag.h
@@ -44,6 +44,7 @@ struct swaynag_button {
44 int y; 44 int y;
45 int width; 45 int width;
46 int height; 46 int height;
47 bool terminal;
47}; 48};
48 49
49struct swaynag_details { 50struct swaynag_details {
diff --git a/meson.build b/meson.build
index bb60bc89..bffbe312 100644
--- a/meson.build
+++ b/meson.build
@@ -32,8 +32,6 @@ if is_freebsd
32 add_project_arguments('-D_C11_SOURCE', language: 'c') 32 add_project_arguments('-D_C11_SOURCE', language: 'c')
33endif 33endif
34 34
35swayidle_deps = []
36
37jsonc = dependency('json-c', version: '>=0.13') 35jsonc = dependency('json-c', version: '>=0.13')
38pcre = dependency('libpcre') 36pcre = dependency('libpcre')
39wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) 37wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
@@ -48,6 +46,7 @@ pango = dependency('pango')
48pangocairo = dependency('pangocairo') 46pangocairo = dependency('pangocairo')
49gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) 47gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
50pixman = dependency('pixman-1') 48pixman = dependency('pixman-1')
49libevdev = dependency('libevdev')
51libinput = dependency('libinput', version: '>=1.6.0') 50libinput = dependency('libinput', version: '>=1.6.0')
52libpam = cc.find_library('pam', required: false) 51libpam = cc.find_library('pam', required: false)
53crypt = cc.find_library('crypt', required: false) 52crypt = cc.find_library('crypt', required: false)
@@ -67,6 +66,7 @@ endif
67conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found()) 66conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found())
68conf_data.set10('HAVE_SYSTEMD', systemd.found()) 67conf_data.set10('HAVE_SYSTEMD', systemd.found())
69conf_data.set10('HAVE_ELOGIND', elogind.found()) 68conf_data.set10('HAVE_ELOGIND', elogind.found())
69conf_data.set10('HAVE_TRAY', get_option('enable-tray') and (systemd.found() or elogind.found()))
70 70
71if not systemd.found() and not elogind.found() 71if not systemd.found() and not elogind.found()
72 warning('The sway binary must be setuid when compiled without (e)logind') 72 warning('The sway binary must be setuid when compiled without (e)logind')
@@ -86,7 +86,6 @@ if scdoc.found()
86 'sway/sway-output.5.scd', 86 'sway/sway-output.5.scd',
87 'swaylock/swaylock.1.scd', 87 'swaylock/swaylock.1.scd',
88 'swaymsg/swaymsg.1.scd', 88 'swaymsg/swaymsg.1.scd',
89 'swayidle/swayidle.1.scd',
90 'swaynag/swaynag.1.scd', 89 'swaynag/swaynag.1.scd',
91 'swaynag/swaynag.5.scd', 90 'swaynag/swaynag.5.scd',
92 ] 91 ]
@@ -108,13 +107,7 @@ if scdoc.found()
108 endforeach 107 endforeach
109endif 108endif
110 109
111# If prefix is '/usr', sysconfdir will be explicitly set to '/etc' by Meson to 110add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c')
112# enforce FHS compliance, so we should look for configs there as well.
113if prefix == '/usr'
114 add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c')
115else
116 add_project_arguments('-DSYSCONFDIR="/@0@/@1@"'.format(prefix, sysconfdir), language : 'c')
117endif
118 111
119version = get_option('sway-version') 112version = get_option('sway-version')
120if version != '' 113if version != ''
@@ -152,21 +145,13 @@ subdir('swaymsg')
152subdir('client') 145subdir('client')
153subdir('swaybg') 146subdir('swaybg')
154subdir('swaybar') 147subdir('swaybar')
155subdir('swayidle')
156subdir('swaynag') 148subdir('swaynag')
157subdir('swaylock') 149subdir('swaylock')
158 150
159config = configuration_data() 151config = configuration_data()
160config.set('datadir', join_paths(prefix, datadir)) 152config.set('datadir', join_paths(prefix, datadir))
161config.set('prefix', prefix) 153config.set('prefix', prefix)
162 154config.set('sysconfdir', join_paths(prefix, sysconfdir))
163# If prefix is '/usr', sysconfdir will be explicitly set to '/etc' by Meson to
164# enforce FHS compliance, so we should look for configs there as well.
165if prefix == '/usr'
166 config.set('sysconfdir', sysconfdir)
167else
168 config.set('sysconfdir', join_paths(prefix, sysconfdir))
169endif
170 155
171configure_file( 156configure_file(
172 configuration: config, 157 configuration: config,
@@ -226,7 +211,7 @@ endif
226if (get_option('bash-completions')) 211if (get_option('bash-completions'))
227 bash_files = files( 212 bash_files = files(
228 'completions/bash/sway', 213 'completions/bash/sway',
229 'completions/bash/swayidle', 214 'completions/bash/swaybar',
230 'completions/bash/swaylock', 215 'completions/bash/swaylock',
231 'completions/bash/swaymsg', 216 'completions/bash/swaymsg',
232 ) 217 )
@@ -238,7 +223,6 @@ endif
238if (get_option('fish-completions')) 223if (get_option('fish-completions'))
239 fish_files = files( 224 fish_files = files(
240 'completions/fish/sway.fish', 225 'completions/fish/sway.fish',
241 'completions/fish/swayidle.fish',
242 'completions/fish/swaylock.fish', 226 'completions/fish/swaylock.fish',
243 'completions/fish/swaymsg.fish', 227 'completions/fish/swaymsg.fish',
244 'completions/fish/swaynag.fish', 228 'completions/fish/swaynag.fish',
diff --git a/meson_options.txt b/meson_options.txt
index 2db852fc..4640618e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,3 +6,4 @@ option('zsh-completions', type: 'boolean', value: true, description: 'Install zs
6option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') 6option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')
7option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') 7option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.')
8option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications') 8option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications')
9option('enable-tray', type: 'boolean', value: false, description: 'Enable support for swaybar tray')
diff --git a/sway/commands.c b/sway/commands.c
index a68c724a..1d190e0b 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -42,22 +42,6 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
42 : NULL; 42 : NULL;
43} 43}
44 44
45void apply_seat_config(struct seat_config *seat_config) {
46 int i;
47 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
48 if (i >= 0) {
49 // merge existing config
50 struct seat_config *sc = config->seat_configs->items[i];
51 merge_seat_config(sc, seat_config);
52 free_seat_config(seat_config);
53 seat_config = sc;
54 } else {
55 list_add(config->seat_configs, seat_config);
56 }
57
58 input_manager_apply_seat_config(seat_config);
59}
60
61/* Keep alphabetized */ 45/* Keep alphabetized */
62static struct cmd_handler handlers[] = { 46static struct cmd_handler handlers[] = {
63 { "assign", cmd_assign }, 47 { "assign", cmd_assign },
@@ -103,6 +87,10 @@ static struct cmd_handler handlers[] = {
103 { "smart_borders", cmd_smart_borders }, 87 { "smart_borders", cmd_smart_borders },
104 { "smart_gaps", cmd_smart_gaps }, 88 { "smart_gaps", cmd_smart_gaps },
105 { "tiling_drag", cmd_tiling_drag }, 89 { "tiling_drag", cmd_tiling_drag },
90 { "tiling_drag_threshold", cmd_tiling_drag_threshold },
91 { "title_align", cmd_title_align },
92 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
93 { "titlebar_padding", cmd_titlebar_padding },
106 { "workspace", cmd_workspace }, 94 { "workspace", cmd_workspace },
107 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, 95 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
108}; 96};
@@ -213,12 +201,9 @@ static void set_config_node(struct sway_node *node) {
213 } 201 }
214} 202}
215 203
216struct cmd_results *execute_command(char *_exec, struct sway_seat *seat, 204list_t *execute_command(char *_exec, struct sway_seat *seat,
217 struct sway_container *con) { 205 struct sway_container *con) {
218 // Even though this function will process multiple commands we will only 206 list_t *res_list = create_list();
219 // return the last error, if any (for now). (Since we have access to an
220 // error string we could e.g. concatenate all errors there.)
221 struct cmd_results *results = NULL;
222 char *exec = strdup(_exec); 207 char *exec = strdup(_exec);
223 char *head = exec; 208 char *head = exec;
224 char *cmdlist; 209 char *cmdlist;
@@ -233,15 +218,6 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
233 } 218 }
234 } 219 }
235 220
236 // This is the container or workspace which this command will run on.
237 // Ignored if the command string contains criteria.
238 struct sway_node *node;
239 if (con) {
240 node = &con->node;
241 } else {
242 node = seat_get_focus_inactive(seat, &root->node);
243 }
244
245 config->handler_context.seat = seat; 221 config->handler_context.seat = seat;
246 222
247 head = exec; 223 head = exec;
@@ -252,8 +228,8 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
252 char *error = NULL; 228 char *error = NULL;
253 struct criteria *criteria = criteria_parse(head, &error); 229 struct criteria *criteria = criteria_parse(head, &error);
254 if (!criteria) { 230 if (!criteria) {
255 results = cmd_results_new(CMD_INVALID, head, 231 list_add(res_list, cmd_results_new(CMD_INVALID, head,
256 "%s", error); 232 "%s", error));
257 free(error); 233 free(error);
258 goto cleanup; 234 goto cleanup;
259 } 235 }
@@ -262,15 +238,15 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
262 criteria_destroy(criteria); 238 criteria_destroy(criteria);
263 config->handler_context.using_criteria = true; 239 config->handler_context.using_criteria = true;
264 // Skip leading whitespace 240 // Skip leading whitespace
265 head += strspn(head, whitespace); 241 for (; isspace(*head); ++head) {}
266 } 242 }
267 // Split command list 243 // Split command list
268 cmdlist = argsep(&head, ";"); 244 cmdlist = argsep(&head, ";");
269 cmdlist += strspn(cmdlist, whitespace); 245 for (; isspace(*cmdlist); ++cmdlist) {}
270 do { 246 do {
271 // Split commands 247 // Split commands
272 cmd = argsep(&cmdlist, ","); 248 cmd = argsep(&cmdlist, ",");
273 cmd += strspn(cmd, whitespace); 249 for (; isspace(*cmd); ++cmd) {}
274 if (strcmp(cmd, "") == 0) { 250 if (strcmp(cmd, "") == 0) {
275 wlr_log(WLR_INFO, "Ignoring empty command."); 251 wlr_log(WLR_INFO, "Ignoring empty command.");
276 continue; 252 continue;
@@ -289,10 +265,8 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
289 } 265 }
290 struct cmd_handler *handler = find_handler(argv[0], NULL, 0); 266 struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
291 if (!handler) { 267 if (!handler) {
292 if (results) { 268 list_add(res_list, cmd_results_new(CMD_INVALID, cmd,
293 free_cmd_results(results); 269 "Unknown/invalid command"));
294 }
295 results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
296 free_argv(argc, argv); 270 free_argv(argc, argv);
297 goto cleanup; 271 goto cleanup;
298 } 272 }
@@ -304,31 +278,26 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
304 } 278 }
305 279
306 if (!config->handler_context.using_criteria) { 280 if (!config->handler_context.using_criteria) {
281 // The container or workspace which this command will run on.
282 struct sway_node *node = con ? &con->node :
283 seat_get_focus_inactive(seat, &root->node);
307 set_config_node(node); 284 set_config_node(node);
308 struct cmd_results *res = handler->handle(argc-1, argv+1); 285 struct cmd_results *res = handler->handle(argc-1, argv+1);
309 if (res->status != CMD_SUCCESS) { 286 list_add(res_list, res);
287 if (res->status == CMD_INVALID) {
310 free_argv(argc, argv); 288 free_argv(argc, argv);
311 if (results) {
312 free_cmd_results(results);
313 }
314 results = res;
315 goto cleanup; 289 goto cleanup;
316 } 290 }
317 free_cmd_results(res);
318 } else { 291 } else {
319 for (int i = 0; i < views->length; ++i) { 292 for (int i = 0; i < views->length; ++i) {
320 struct sway_view *view = views->items[i]; 293 struct sway_view *view = views->items[i];
321 set_config_node(&view->container->node); 294 set_config_node(&view->container->node);
322 struct cmd_results *res = handler->handle(argc-1, argv+1); 295 struct cmd_results *res = handler->handle(argc-1, argv+1);
323 if (res->status != CMD_SUCCESS) { 296 list_add(res_list, res);
297 if (res->status == CMD_INVALID) {
324 free_argv(argc, argv); 298 free_argv(argc, argv);
325 if (results) {
326 free_cmd_results(results);
327 }
328 results = res;
329 goto cleanup; 299 goto cleanup;
330 } 300 }
331 free_cmd_results(res);
332 } 301 }
333 } 302 }
334 free_argv(argc, argv); 303 free_argv(argc, argv);
@@ -337,10 +306,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
337cleanup: 306cleanup:
338 free(exec); 307 free(exec);
339 list_free(views); 308 list_free(views);
340 if (!results) { 309 return res_list;
341 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
342 }
343 return results;
344} 310}
345 311
346// this is like execute_command above, except: 312// this is like execute_command above, except:
@@ -418,6 +384,7 @@ struct cmd_results *config_command(char *exec) {
418 // Strip quotes and unescape the string 384 // Strip quotes and unescape the string
419 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 385 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
420 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always 386 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always
387 && handler->handle != cmd_mode
421 && handler->handle != cmd_bindsym 388 && handler->handle != cmd_bindsym
422 && handler->handle != cmd_bindcode 389 && handler->handle != cmd_bindcode
423 && handler->handle != cmd_set 390 && handler->handle != cmd_set
@@ -572,20 +539,25 @@ void free_cmd_results(struct cmd_results *results) {
572 free(results); 539 free(results);
573} 540}
574 541
575char *cmd_results_to_json(struct cmd_results *results) { 542char *cmd_results_to_json(list_t *res_list) {
576 json_object *result_array = json_object_new_array(); 543 json_object *result_array = json_object_new_array();
577 json_object *root = json_object_new_object(); 544 for (int i = 0; i < res_list->length; ++i) {
578 json_object_object_add(root, "success", 545 struct cmd_results *results = res_list->items[i];
579 json_object_new_boolean(results->status == CMD_SUCCESS)); 546 json_object *root = json_object_new_object();
580 if (results->input) { 547 json_object_object_add(root, "success",
581 json_object_object_add( 548 json_object_new_boolean(results->status == CMD_SUCCESS));
582 root, "input", json_object_new_string(results->input)); 549 if (results->error) {
583 } 550 json_object_object_add(root, "parse_error",
584 if (results->error) { 551 json_object_new_boolean(results->status == CMD_INVALID));
585 json_object_object_add( 552 json_object_object_add(
586 root, "error", json_object_new_string(results->error)); 553 root, "error", json_object_new_string(results->error));
554 }
555 if (results->input) {
556 json_object_object_add(
557 root, "input", json_object_new_string(results->input));
558 }
559 json_object_array_add(result_array, root);
587 } 560 }
588 json_object_array_add(result_array, root);
589 const char *json = json_object_to_json_string(result_array); 561 const char *json = json_object_to_json_string(result_array);
590 char *res = strdup(json); 562 char *res = strdup(json);
591 json_object_put(result_array); 563 json_object_put(result_array);
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index 04582e88..716d70cf 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <string.h> 3#include <string.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index c808aef2..2a82d508 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -8,12 +8,12 @@
8 8
9// Must be in alphabetical order for bsearch 9// Must be in alphabetical order for bsearch
10static struct cmd_handler bar_handlers[] = { 10static struct cmd_handler bar_handlers[] = {
11 { "activate_button", bar_cmd_activate_button }, 11 { "bindcode", bar_cmd_bindcode },
12 { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, 12 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
13 { "bindsym", bar_cmd_bindsym }, 13 { "bindsym", bar_cmd_bindsym },
14 { "colors", bar_cmd_colors }, 14 { "colors", bar_cmd_colors },
15 { "context_button", bar_cmd_context_button },
16 { "font", bar_cmd_font }, 15 { "font", bar_cmd_font },
16 { "gaps", bar_cmd_gaps },
17 { "height", bar_cmd_height }, 17 { "height", bar_cmd_height },
18 { "hidden_state", bar_cmd_hidden_state }, 18 { "hidden_state", bar_cmd_hidden_state },
19 { "icon_theme", bar_cmd_icon_theme }, 19 { "icon_theme", bar_cmd_icon_theme },
@@ -22,10 +22,13 @@ static struct cmd_handler bar_handlers[] = {
22 { "output", bar_cmd_output }, 22 { "output", bar_cmd_output },
23 { "pango_markup", bar_cmd_pango_markup }, 23 { "pango_markup", bar_cmd_pango_markup },
24 { "position", bar_cmd_position }, 24 { "position", bar_cmd_position },
25 { "secondary_button", bar_cmd_secondary_button },
26 { "separator_symbol", bar_cmd_separator_symbol }, 25 { "separator_symbol", bar_cmd_separator_symbol },
27 { "status_command", bar_cmd_status_command }, 26 { "status_command", bar_cmd_status_command },
27 { "status_edge_padding", bar_cmd_status_edge_padding },
28 { "status_padding", bar_cmd_status_padding },
29 { "strip_workspace_name", bar_cmd_strip_workspace_name },
28 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, 30 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
31 { "tray_bindsym", bar_cmd_tray_bindsym },
29 { "tray_output", bar_cmd_tray_output }, 32 { "tray_output", bar_cmd_tray_output },
30 { "tray_padding", bar_cmd_tray_padding }, 33 { "tray_padding", bar_cmd_tray_padding },
31 { "workspace_buttons", bar_cmd_workspace_buttons }, 34 { "workspace_buttons", bar_cmd_workspace_buttons },
diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c
deleted file mode 100644
index 7310e7ec..00000000
--- a/sway/commands/bar/activate_button.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c
new file mode 100644
index 00000000..a4c65ec4
--- /dev/null
+++ b/sway/commands/bar/bind.c
@@ -0,0 +1,106 @@
1#include <libevdev/libevdev.h>
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "sway/input/cursor.h"
8#include "list.h"
9#include "log.h"
10#include "stringop.h"
11
12static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) {
13 const char *command = code ? "bar bindcode" : "bar bindsym";
14 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, 2))) {
16 return error;
17 }
18 if (!config->current_bar) {
19 return cmd_results_new(CMD_FAILURE, command, "No bar defined.");
20 }
21
22 struct bar_binding *binding = calloc(1, sizeof(struct bar_binding));
23 if (!binding) {
24 return cmd_results_new(CMD_FAILURE, command,
25 "Unable to allocate bar binding");
26 }
27
28 binding->release = false;
29 if (strcmp("--release", argv[0]) == 0) {
30 binding->release = true;
31 argv++;
32 argc--;
33 }
34
35 char *message = NULL;
36 if (code) {
37 binding->button = get_mouse_bindcode(argv[0], &message);
38 } else {
39 binding->button = get_mouse_bindsym(argv[0], &message);
40 }
41 if (message) {
42 free_bar_binding(binding);
43 error = cmd_results_new(CMD_INVALID, command, message);
44 free(message);
45 return error;
46 } else if (!binding->button) {
47 free_bar_binding(binding);
48 return cmd_results_new(CMD_INVALID, command,
49 "Unknown button %s", argv[0]);
50 }
51
52 const char *name = libevdev_event_code_get_name(EV_KEY, binding->button);
53 if (!name) {
54 switch (binding->button) {
55 case SWAY_SCROLL_UP:
56 name = "SWAY_SCROLL_UP";
57 break;
58 case SWAY_SCROLL_DOWN:
59 name = "SWAY_SCROLL_DOWN";
60 break;
61 case SWAY_SCROLL_LEFT:
62 name = "SWAY_SCROLL_LEFT";
63 break;
64 case SWAY_SCROLL_RIGHT:
65 name = "SWAY_SCROLL_RIGHT";
66 break;
67 default:
68 // Unreachable
69 break;
70 }
71 }
72
73 binding->command = join_args(argv + 1, argc - 1);
74
75 list_t *bindings = config->current_bar->bindings;
76 bool overwritten = false;
77 for (int i = 0; i < bindings->length; i++) {
78 struct bar_binding *other = bindings->items[i];
79 if (other->button == binding->button &&
80 other->release == binding->release) {
81 overwritten = true;
82 bindings->items[i] = binding;
83 free_bar_binding(other);
84 wlr_log(WLR_DEBUG, "[bar %s] Updated binding for %u (%s)%s",
85 config->current_bar->id, binding->button, name,
86 binding->release ? " - release" : "");
87 break;
88 }
89 }
90 if (!overwritten) {
91 list_add(bindings, binding);
92 wlr_log(WLR_DEBUG, "[bar %s] Added binding for %u (%s)%s",
93 config->current_bar->id, binding->button, name,
94 binding->release ? " - release" : "");
95 }
96
97 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
98}
99
100struct cmd_results *bar_cmd_bindcode(int argc, char **argv) {
101 return bar_cmd_bind(argc, argv, true);
102}
103
104struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
105 return bar_cmd_bind(argc, argv, false);
106}
diff --git a/sway/commands/bar/bindsym.c b/sway/commands/bar/bindsym.c
deleted file mode 100644
index 965c8903..00000000
--- a/sway/commands/bar/bindsym.c
+++ /dev/null
@@ -1,69 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "list.h"
8#include "log.h"
9#include "stringop.h"
10
11struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, "bar bindsym", EXPECTED_AT_LEAST, 2))) {
14 return error;
15 }
16 if (!config->current_bar) {
17 return cmd_results_new(CMD_FAILURE, "bar bindsym", "No bar defined.");
18 }
19
20 struct bar_binding *binding = calloc(1, sizeof(struct bar_binding));
21 if (!binding) {
22 return cmd_results_new(CMD_FAILURE, "bar bindsym",
23 "Unable to allocate bar binding");
24 }
25
26 binding->release = false;
27 if (strcmp("--release", argv[0]) == 0) {
28 binding->release = true;
29 argv++;
30 argc--;
31 }
32
33 binding->button = 0;
34 if (strncasecmp(argv[0], "button", strlen("button")) == 0 &&
35 strlen(argv[0]) == strlen("button0")) {
36 binding->button = argv[0][strlen("button")] - '0';
37 }
38 if (binding->button < 1 || binding->button > 9) {
39 free_bar_binding(binding);
40 return cmd_results_new(CMD_FAILURE, "bar bindsym",
41 "Only button<1-9> is supported");
42 }
43
44 binding->command = join_args(argv + 1, argc - 1);
45
46 list_t *bindings = config->current_bar->bindings;
47 bool overwritten = false;
48 for (int i = 0; i < bindings->length; i++) {
49 struct bar_binding *other = bindings->items[i];
50 if (other->button == binding->button &&
51 other->release == binding->release) {
52 overwritten = true;
53 bindings->items[i] = binding;
54 free_bar_binding(other);
55 wlr_log(WLR_DEBUG, "[bar %s] Updated binding for button%u%s",
56 config->current_bar->id, binding->button,
57 binding->release ? " (release)" : "");
58 break;
59 }
60 }
61 if (!overwritten) {
62 list_add(bindings, binding);
63 wlr_log(WLR_DEBUG, "[bar %s] Added binding for button%u%s",
64 config->current_bar->id, binding->button,
65 binding->release ? " (release)" : "");
66 }
67
68 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
69}
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 8c862ca9..ebf1e3e1 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -118,8 +118,8 @@ struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
118} 118}
119 119
120struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { 120struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
121 return parse_single_color(&(config->current_bar->colors.focused_separator), 121 return parse_single_color(&(config->current_bar->colors.focused_statusline),
122 "focused_separator", argc, argv); 122 "focused_statusline", argc, argv);
123} 123}
124 124
125struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { 125struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {
diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c
deleted file mode 100644
index 3b76885a..00000000
--- a/sway/commands/bar/context_button.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/gaps.c b/sway/commands/bar/gaps.c
new file mode 100644
index 00000000..f78f3742
--- /dev/null
+++ b/sway/commands/bar/gaps.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/ipc-server.h"
6#include "log.h"
7
8struct cmd_results *bar_cmd_gaps(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if ((error = checkarg(argc, "gaps", EXPECTED_AT_MOST, 4))) {
14 return error;
15 }
16 if (!config->current_bar) {
17 return cmd_results_new(CMD_FAILURE, "bar gaps", "No bar defined.");
18 }
19
20 int top = 0, right = 0, bottom = 0, left = 0;
21
22 for (int i = 0; i < argc; i++) {
23 char *end;
24 int amount = strtol(argv[i], &end, 10);
25 if (strlen(end) && strcasecmp(end, "px") != 0) {
26 return cmd_results_new(CMD_INVALID, "bar gaps",
27 "Expected 'bar [<bar-id>] gaps <all> | <horizonal> "
28 "<vertical> | <top> <right> <bottom> <left>'");
29 }
30
31 if (i == 0) {
32 top = amount;
33 }
34 if (i == 0 || i == 1) {
35 right = amount;
36 }
37 if (i == 0 || i == 2) {
38 bottom = amount;
39 }
40 if (i == 0 || i == 1 || i == 3) {
41 left = amount;
42 }
43 }
44
45 config->current_bar->gaps.top = top;
46 config->current_bar->gaps.right = right;
47 config->current_bar->gaps.bottom = bottom;
48 config->current_bar->gaps.left = left;
49
50 wlr_log(WLR_DEBUG, "Setting bar gaps to %d %d %d %d on bar: %s",
51 config->current_bar->gaps.top, config->current_bar->gaps.right,
52 config->current_bar->gaps.bottom, config->current_bar->gaps.left,
53 config->current_bar->id);
54
55 if (!config->reading) {
56 ipc_event_barconfig_update(config->current_bar);
57 }
58
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
60}
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 5be6c2dc..79eaf01c 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 44cd3076..9d3b6040 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,8 +1,28 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h"
6#include "log.h"
4 7
5struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { 8struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
6 // TODO TRAY 9#if HAVE_TRAY
7 return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY"); 10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "icon_theme", EXPECTED_EQUAL_TO, 1))) {
12 return error;
13 }
14
15 if (!config->current_bar) {
16 return cmd_results_new(CMD_FAILURE, "tray_padding", "No bar defined.");
17 }
18
19 wlr_log(WLR_DEBUG, "[Bar %s] Setting icon theme to %s",
20 config->current_bar->id, argv[0]);
21 free(config->current_bar->icon_theme);
22 config->current_bar->icon_theme = strdup(argv[0]);
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24#else
25 return cmd_results_new(CMD_INVALID, "icon_theme",
26 "Sway has been compiled without tray support");
27#endif
8} 28}
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index 7690a852..35509459 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 2cba785e..dcaf6da9 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c
index 09025fff..b5a16f45 100644
--- a/sway/commands/bar/modifier.c
+++ b/sway/commands/bar/modifier.c
@@ -20,15 +20,14 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
20 uint32_t tmp_mod; 20 uint32_t tmp_mod;
21 if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { 21 if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) {
22 mod |= tmp_mod; 22 mod |= tmp_mod;
23 continue;
24 } else { 23 } else {
25 error = cmd_results_new(CMD_INVALID, "modifier", 24 error = cmd_results_new(CMD_INVALID, "modifier",
26 "Unknown modifier '%s'", split->items[i]); 25 "Unknown modifier '%s'", split->items[i]);
27 free_flat_list(split); 26 list_free_items_and_destroy(split);
28 return error; 27 return error;
29 } 28 }
30 } 29 }
31 free_flat_list(split); 30 list_free_items_and_destroy(split);
32 config->current_bar->modifier = mod; 31 config->current_bar->modifier = mod;
33 wlr_log(WLR_DEBUG, 32 wlr_log(WLR_DEBUG,
34 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); 33 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index 72754e05..930d779d 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 2#include <stdbool.h>
3#include <string.h> 3#include <string.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c
deleted file mode 100644
index 449124cb..00000000
--- a/sway/commands/bar/secondary_button.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 392ab730..060b8f52 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
diff --git a/sway/commands/bar/status_edge_padding.c b/sway/commands/bar/status_edge_padding.c
new file mode 100644
index 00000000..f3b10631
--- /dev/null
+++ b/sway/commands/bar/status_edge_padding.c
@@ -0,0 +1,21 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_status_edge_padding(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_edge_padding", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 char *end;
12 int padding = strtol(argv[0], &end, 10);
13 if (strlen(end) || padding < 0) {
14 return cmd_results_new(CMD_INVALID, "status_edge_padding",
15 "Padding must be a positive integer");
16 }
17 config->current_bar->status_edge_padding = padding;
18 wlr_log(WLR_DEBUG, "Status edge padding on bar %s: %d",
19 config->current_bar->id, config->current_bar->status_edge_padding);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/bar/status_padding.c b/sway/commands/bar/status_padding.c
new file mode 100644
index 00000000..13b8eb6b
--- /dev/null
+++ b/sway/commands/bar/status_padding.c
@@ -0,0 +1,21 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_status_padding(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_padding", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 char *end;
12 int padding = strtol(argv[0], &end, 10);
13 if (strlen(end) || padding < 0) {
14 return cmd_results_new(CMD_INVALID, "status_padding",
15 "Padding must be a positive integer");
16 }
17 config->current_bar->status_padding = padding;
18 wlr_log(WLR_DEBUG, "Status padding on bar %s: %d",
19 config->current_bar->id, config->current_bar->status_padding);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/bar/strip_workspace_name.c b/sway/commands/bar/strip_workspace_name.c
new file mode 100644
index 00000000..79692f6e
--- /dev/null
+++ b/sway/commands/bar/strip_workspace_name.c
@@ -0,0 +1,32 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5#include "util.h"
6
7struct cmd_results *bar_cmd_strip_workspace_name(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc,
10 "strip_workspace_name", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE,
15 "strip_workspace_name", "No bar defined.");
16 }
17
18 config->current_bar->strip_workspace_name =
19 parse_boolean(argv[0], config->current_bar->strip_workspace_name);
20
21 if (config->current_bar->strip_workspace_name) {
22 config->current_bar->strip_workspace_numbers = false;
23
24 wlr_log(WLR_DEBUG, "Stripping workspace name on bar: %s",
25 config->current_bar->id);
26 } else {
27 wlr_log(WLR_DEBUG, "Enabling workspace name on bar: %s",
28 config->current_bar->id);
29 }
30
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32}
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c
index 4e47d047..b33d01e5 100644
--- a/sway/commands/bar/strip_workspace_numbers.c
+++ b/sway/commands/bar/strip_workspace_numbers.c
@@ -2,6 +2,7 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
5#include "util.h"
5 6
6struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { 7struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
@@ -13,17 +14,19 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
13 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
14 "strip_workspace_numbers", "No bar defined."); 15 "strip_workspace_numbers", "No bar defined.");
15 } 16 }
16 if (strcasecmp("yes", argv[0]) == 0) { 17
17 config->current_bar->strip_workspace_numbers = true; 18 config->current_bar->strip_workspace_numbers =
19 parse_boolean(argv[0], config->current_bar->strip_workspace_numbers);
20
21 if (config->current_bar->strip_workspace_numbers) {
22 config->current_bar->strip_workspace_name = false;
23
18 wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s", 24 wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s",
19 config->current_bar->id); 25 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) { 26 } else {
21 config->current_bar->strip_workspace_numbers = false;
22 wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s", 27 wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s",
23 config->current_bar->id); 28 config->current_bar->id);
24 } else {
25 return cmd_results_new(CMD_INVALID,
26 "strip_workspace_numbers", "Invalid value %s", argv[0]);
27 } 29 }
30
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 32}
diff --git a/sway/commands/bar/tray_bindsym.c b/sway/commands/bar/tray_bindsym.c
new file mode 100644
index 00000000..ad413446
--- /dev/null
+++ b/sway/commands/bar/tray_bindsym.c
@@ -0,0 +1,55 @@
1#include <strings.h>
2#include "config.h"
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "log.h"
6
7struct cmd_results *bar_cmd_tray_bindsym(int argc, char **argv) {
8#if HAVE_TRAY
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "tray_bindsym", EXPECTED_EQUAL_TO, 2))) {
11 return error;
12 }
13
14 if (!config->current_bar) {
15 return cmd_results_new(CMD_FAILURE, "tray_bindsym", "No bar defined.");
16 }
17
18 int button = 0;
19 if (strncasecmp(argv[0], "button", strlen("button")) == 0 &&
20 strlen(argv[0]) == strlen("button0")) {
21 button = argv[0][strlen("button")] - '0';
22 }
23 if (button < 1 || button > 9) {
24 return cmd_results_new(CMD_FAILURE, "tray_bindsym",
25 "[Bar %s] Only buttons 1 to 9 are supported",
26 config->current_bar->id);
27 }
28
29 static const char *commands[] = {
30 "ContextMenu",
31 "Activate",
32 "SecondaryActivate",
33 "ScrollDown",
34 "ScrollLeft",
35 "ScrollRight",
36 "ScrollUp",
37 "nop"
38 };
39
40 for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) {
41 if (strcasecmp(argv[1], commands[i]) == 0) {
42 wlr_log(WLR_DEBUG, "[Bar %s] Binding button %d to %s",
43 config->current_bar->id, button, commands[i]);
44 config->current_bar->tray_bindings[button] = commands[i];
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46 }
47 }
48
49 return cmd_results_new(CMD_INVALID, "tray_bindsym",
50 "[Bar %s] Invalid command %s", config->current_bar->id, argv[1]);
51#else
52 return cmd_results_new(CMD_INVALID, "tray_bindsym",
53 "Sway has been compiled without tray support");
54#endif
55}
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index 6ab16731..a1169c20 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,8 +1,42 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h"
6#include "list.h"
7#include "log.h"
4 8
5struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { 9struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
6 // TODO TRAY 10#if HAVE_TRAY
7 return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY"); 11 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "tray_output", EXPECTED_EQUAL_TO, 1))) {
13 return error;
14 }
15
16 if (!config->current_bar) {
17 return cmd_results_new(CMD_FAILURE, "tray_output", "No bar defined.");
18 }
19
20 list_t *outputs = config->current_bar->tray_outputs;
21 if (!outputs) {
22 config->current_bar->tray_outputs = outputs = create_list();
23 }
24
25 if (strcmp(argv[0], "none") == 0) {
26 wlr_log(WLR_DEBUG, "Hiding tray on bar: %s", config->current_bar->id);
27 for (int i = 0; i < outputs->length; ++i) {
28 free(outputs->items[i]);
29 }
30 outputs->length = 0;
31 } else {
32 wlr_log(WLR_DEBUG, "Showing tray on output '%s' for bar: %s", argv[0],
33 config->current_bar->id);
34 }
35 list_add(outputs, strdup(argv[0]));
36
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38#else
39 return cmd_results_new(CMD_INVALID, "tray_output",
40 "Sway has been compiled without tray support");
41#endif
8} 42}
diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c
index 91c56f19..eb795b00 100644
--- a/sway/commands/bar/tray_padding.c
+++ b/sway/commands/bar/tray_padding.c
@@ -1,9 +1,42 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <strings.h> 2#include <strings.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h"
4#include "log.h" 6#include "log.h"
5 7
6struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { 8struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
7 // TODO TRAY 9#if HAVE_TRAY
8 return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY"); 10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "tray_padding", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 if ((error = checkarg(argc, "tray_padding", EXPECTED_AT_MOST, 2))) {
15 return error;
16 }
17
18 if (!config->current_bar) {
19 return cmd_results_new(CMD_FAILURE, "tray_padding", "No bar defined.");
20 }
21 struct bar_config *bar = config->current_bar;
22
23 char *end;
24 int padding = strtol(argv[0], &end, 10);
25 if (padding < 0 || (*end != '\0' && strcasecmp(end, "px") != 0)) {
26 return cmd_results_new(CMD_INVALID, "tray_padding",
27 "[Bar %s] Invalid tray padding value: %s", bar->id, argv[0]);
28 }
29
30 if (argc == 2 && strcasecmp(argv[1], "px") != 0) {
31 return cmd_results_new(CMD_INVALID, "tray_padding",
32 "Expected 'tray_padding <px> [px]'");
33 }
34
35 wlr_log(WLR_DEBUG, "[Bar %s] Setting tray padding to %d", bar->id, padding);
36 config->current_bar->tray_padding = padding;
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38#else
39 return cmd_results_new(CMD_INVALID, "tray_padding",
40 "Sway has been compiled without tray support");
41#endif
9} 42}
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index a9de227f..be47d412 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,15 +1,13 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#ifdef __linux__ 2#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7#include <xkbcommon/xkbcommon.h> 4#include <xkbcommon/xkbcommon.h>
8#include <xkbcommon/xkbcommon-names.h> 5#include <xkbcommon/xkbcommon-names.h>
9#include <string.h> 6#include <string.h>
10#include <strings.h> 7#include <strings.h>
11#include "sway/commands.h" 8#include "sway/commands.h"
12#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/input/cursor.h"
13#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
14#include "list.h" 12#include "list.h"
15#include "log.h" 13#include "log.h"
@@ -23,9 +21,7 @@ void free_sway_binding(struct sway_binding *binding) {
23 return; 21 return;
24 } 22 }
25 23
26 if (binding->keys) { 24 list_free_items_and_destroy(binding->keys);
27 free_flat_list(binding->keys);
28 }
29 free(binding->input); 25 free(binding->input);
30 free(binding->command); 26 free(binding->command);
31 free(binding); 27 free(binding);
@@ -80,7 +76,6 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
80 return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); 76 return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0);
81} 77}
82 78
83
84/** 79/**
85 * From a keycode, bindcode, or bindsym name and the most likely binding type, 80 * From a keycode, bindcode, or bindsym name and the most likely binding type,
86 * identify the appropriate numeric value corresponding to the key. Return NULL 81 * identify the appropriate numeric value corresponding to the key. Return NULL
@@ -90,52 +85,91 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
90 */ 85 */
91static struct cmd_results *identify_key(const char* name, bool first_key, 86static struct cmd_results *identify_key(const char* name, bool first_key,
92 uint32_t* key_val, enum binding_input_type* type) { 87 uint32_t* key_val, enum binding_input_type* type) {
93 if (*type == BINDING_KEYCODE) { 88 if (*type == BINDING_MOUSECODE) {
94 // check for keycode 89 // check for mouse bindcodes
90 char *message = NULL;
91 uint32_t button = get_mouse_bindcode(name, &message);
92 if (!button) {
93 if (message) {
94 struct cmd_results *error =
95 cmd_results_new(CMD_INVALID, "bindcode", message);
96 free(message);
97 return error;
98 } else {
99 return cmd_results_new(CMD_INVALID, "bindcode",
100 "Unknown button code %s", name);
101 }
102 }
103 *key_val = button;
104 } else if (*type == BINDING_MOUSESYM) {
105 // check for mouse bindsyms (x11 buttons or event names)
106 char *message = NULL;
107 uint32_t button = get_mouse_bindsym(name, &message);
108 if (!button) {
109 if (message) {
110 struct cmd_results *error =
111 cmd_results_new(CMD_INVALID, "bindsym", message);
112 free(message);
113 return error;
114 } else if (!button) {
115 return cmd_results_new(CMD_INVALID, "bindsym",
116 "Unknown button %s", name);
117 }
118 }
119 *key_val = button;
120 } else if (*type == BINDING_KEYCODE) {
121 // check for keycode. If it is the first key, allow mouse bindcodes
122 if (first_key) {
123 char *message = NULL;
124 uint32_t button = get_mouse_bindcode(name, &message);
125 free(message);
126 if (button) {
127 *type = BINDING_MOUSECODE;
128 *key_val = button;
129 return NULL;
130 }
131 }
132
95 xkb_keycode_t keycode = strtol(name, NULL, 10); 133 xkb_keycode_t keycode = strtol(name, NULL, 10);
96 if (!xkb_keycode_is_legal_ext(keycode)) { 134 if (!xkb_keycode_is_legal_ext(keycode)) {
97 return cmd_results_new(CMD_INVALID, "bindcode", 135 if (first_key) {
98 "Invalid keycode '%s'", name); 136 return cmd_results_new(CMD_INVALID, "bindcode",
137 "Invalid keycode or button code '%s'", name);
138 } else {
139 return cmd_results_new(CMD_INVALID, "bindcode",
140 "Invalid keycode '%s'", name);
141 }
99 } 142 }
100 *key_val = keycode; 143 *key_val = keycode;
101 } else { 144 } else {
102 // check for keysym 145 // check for keysym. If it is the first key, allow mouse bindsyms
103 xkb_keysym_t keysym = xkb_keysym_from_name(name, 146 if (first_key) {
104 XKB_KEYSYM_CASE_INSENSITIVE); 147 char *message = NULL;
105 148 uint32_t button = get_mouse_bindsym(name, &message);
106 // Check for mouse binding 149 if (message) {
107 uint32_t button = 0; 150 struct cmd_results *error =
108 if (strncasecmp(name, "button", strlen("button")) == 0 && 151 cmd_results_new(CMD_INVALID, "bindsym", message);
109 strlen(name) == strlen("button0")) { 152 free(message);
110 button = name[strlen("button")] - '1' + BTN_LEFT; 153 return error;
154 } else if (button) {
155 *type = BINDING_MOUSESYM;
156 *key_val = button;
157 return NULL;
158 }
111 } 159 }
112 160
113 if (*type == BINDING_KEYSYM) { 161 xkb_keysym_t keysym = xkb_keysym_from_name(name,
114 if (button) { 162 XKB_KEYSYM_CASE_INSENSITIVE);
115 if (first_key) { 163 if (!keysym) {
116 *type = BINDING_MOUSE; 164 if (first_key) {
117 *key_val = button;
118 } else {
119 return cmd_results_new(CMD_INVALID, "bindsym",
120 "Mixed button '%s' into key sequence", name);
121 }
122 } else if (keysym) {
123 *key_val = keysym;
124 } else {
125 return cmd_results_new(CMD_INVALID, "bindsym",
126 "Unknown key '%s'", name);
127 }
128 } else {
129 if (button) {
130 *key_val = button;
131 } else if (keysym) {
132 return cmd_results_new(CMD_INVALID, "bindsym", 165 return cmd_results_new(CMD_INVALID, "bindsym",
133 "Mixed keysym '%s' into button sequence", name); 166 "Unknown key or button '%s'", name);
134 } else { 167 } else {
135 return cmd_results_new(CMD_INVALID, "bindsym", 168 return cmd_results_new(CMD_INVALID, "bindsym",
136 "Unknown button '%s'", name); 169 "Unknown key '%s'", name);
137 } 170 }
138 } 171 }
172 *key_val = keysym;
139 } 173 }
140 return NULL; 174 return NULL;
141} 175}
@@ -161,6 +195,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
161 binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; 195 binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM;
162 196
163 bool exclude_titlebar = false; 197 bool exclude_titlebar = false;
198 bool warn = true;
164 199
165 // Handle --release and --locked 200 // Handle --release and --locked
166 while (argc > 0) { 201 while (argc > 0) {
@@ -178,6 +213,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
178 strlen("--input-device=")) == 0) { 213 strlen("--input-device=")) == 0) {
179 free(binding->input); 214 free(binding->input);
180 binding->input = strdup(argv[0] + strlen("--input-device=")); 215 binding->input = strdup(argv[0] + strlen("--input-device="));
216 } else if (strcmp("--no-warn", argv[0]) == 0) {
217 warn = false;
181 } else { 218 } else {
182 break; 219 break;
183 } 220 }
@@ -186,7 +223,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
186 } 223 }
187 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) 224 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)
188 || exclude_titlebar) { 225 || exclude_titlebar) {
189 binding->type = BINDING_MOUSE; 226 binding->type = binding->type == BINDING_KEYCODE ?
227 BINDING_MOUSECODE : BINDING_MOUSESYM;
190 } 228 }
191 229
192 if (argc < 2) { 230 if (argc < 2) {
@@ -220,21 +258,22 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
220 uint32_t *key = calloc(1, sizeof(uint32_t)); 258 uint32_t *key = calloc(1, sizeof(uint32_t));
221 if (!key) { 259 if (!key) {
222 free_sway_binding(binding); 260 free_sway_binding(binding);
223 free_flat_list(split); 261 list_free_items_and_destroy(split);
224 return cmd_results_new(CMD_FAILURE, bindtype, 262 return cmd_results_new(CMD_FAILURE, bindtype,
225 "Unable to allocate binding key"); 263 "Unable to allocate binding key");
226 } 264 }
227 *key = key_val; 265 *key = key_val;
228 list_add(binding->keys, key); 266 list_add(binding->keys, key);
229 } 267 }
230 free_flat_list(split); 268 list_free_items_and_destroy(split);
231 binding->order = binding_order++; 269 binding->order = binding_order++;
232 270
233 // refine region of interest for mouse binding once we are certain 271 // refine region of interest for mouse binding once we are certain
234 // that this is one 272 // that this is one
235 if (exclude_titlebar) { 273 if (exclude_titlebar) {
236 binding->flags &= ~BINDING_TITLEBAR; 274 binding->flags &= ~BINDING_TITLEBAR;
237 } else if (binding->type == BINDING_MOUSE) { 275 } else if (binding->type == BINDING_MOUSECODE
276 || binding->type == BINDING_MOUSESYM) {
238 binding->flags |= BINDING_TITLEBAR; 277 binding->flags |= BINDING_TITLEBAR;
239 } 278 }
240 279
@@ -255,8 +294,15 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
255 for (int i = 0; i < mode_bindings->length; ++i) { 294 for (int i = 0; i < mode_bindings->length; ++i) {
256 struct sway_binding *config_binding = mode_bindings->items[i]; 295 struct sway_binding *config_binding = mode_bindings->items[i];
257 if (binding_key_compare(binding, config_binding)) { 296 if (binding_key_compare(binding, config_binding)) {
258 wlr_log(WLR_DEBUG, "overwriting old binding with command '%s'", 297 wlr_log(WLR_INFO, "Overwriting binding '%s' for device '%s' "
259 config_binding->command); 298 "from `%s` to `%s`", argv[0], binding->input,
299 binding->command, config_binding->command);
300 if (warn) {
301 config_add_swaynag_warning("Overwriting binding"
302 "'%s' for device '%s' to `%s` from `%s`",
303 argv[0], binding->input, binding->command,
304 config_binding->command);
305 }
260 free_sway_binding(config_binding); 306 free_sway_binding(config_binding);
261 mode_bindings->items[i] = binding; 307 mode_bindings->items[i] = binding;
262 overwritten = true; 308 overwritten = true;
@@ -270,7 +316,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
270 wlr_log(WLR_DEBUG, "%s - Bound %s to command `%s` for device '%s'", 316 wlr_log(WLR_DEBUG, "%s - Bound %s to command `%s` for device '%s'",
271 bindtype, argv[0], binding->command, binding->input); 317 bindtype, argv[0], binding->command, binding->input);
272 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 318 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
273
274} 319}
275 320
276struct cmd_results *cmd_bindsym(int argc, char **argv) { 321struct cmd_results *cmd_bindsym(int argc, char **argv) {
@@ -281,21 +326,25 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
281 return cmd_bindsym_or_bindcode(argc, argv, true); 326 return cmd_bindsym_or_bindcode(argc, argv, true);
282} 327}
283 328
284
285/** 329/**
286 * Execute the command associated to a binding 330 * Execute the command associated to a binding
287 */ 331 */
288void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { 332void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {
289 wlr_log(WLR_DEBUG, "running command for binding: %s", binding->command); 333 wlr_log(WLR_DEBUG, "running command for binding: %s", binding->command);
290 334
291 config->handler_context.seat = seat; 335 list_t *res_list = execute_command(binding->command, seat, NULL);
292 struct cmd_results *results = execute_command(binding->command, NULL, NULL); 336 bool success = true;
293 if (results->status == CMD_SUCCESS) { 337 for (int i = 0; i < res_list->length; ++i) {
338 struct cmd_results *results = res_list->items[i];
339 if (results->status != CMD_SUCCESS) {
340 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
341 binding->command, results->error);
342 success = false;
343 }
344 free_cmd_results(results);
345 }
346 list_free(res_list);
347 if (success) {
294 ipc_event_binding(binding); 348 ipc_event_binding(binding);
295 } else {
296 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
297 binding->command, results->error);
298 } 349 }
299
300 free_cmd_results(results);
301} 350}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 7a15709b..9ec28d81 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <stdint.h> 3#include <stdint.h>
4#include <string.h> 4#include <string.h>
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index f6338c55..97ffe91c 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -193,7 +193,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
193 "Expected 'focus output <direction|name>'"); 193 "Expected 'focus output <direction|name>'");
194 } 194 }
195 char *identifier = join_args(argv, argc); 195 char *identifier = join_args(argv, argc);
196 struct sway_output *output = output_by_name(identifier); 196 struct sway_output *output = output_by_name_or_id(identifier);
197 197
198 if (!output) { 198 if (!output) {
199 enum wlr_direction direction; 199 enum wlr_direction direction;
@@ -269,6 +269,9 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
269 } 269 }
270 270
271 if (argc == 0 && container) { 271 if (argc == 0 && container) {
272 if (container->scratchpad && !container->workspace) {
273 root_scratchpad_show(container);
274 }
272 seat_set_focus_container(seat, container); 275 seat_set_focus_container(seat, container);
273 seat_consider_warp_to_focus(seat); 276 seat_consider_warp_to_focus(seat);
274 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 277 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index ac4d6563..7c0f7d7f 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/criteria.h" 3#include "sway/criteria.h"
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index ff7cbba7..b78187d9 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -13,14 +13,14 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
13 return error; 13 return error;
14 } 14 }
15 if (!root->outputs->length) { 15 if (!root->outputs->length) {
16 return cmd_results_new(CMD_INVALID, "fullscreen", 16 return cmd_results_new(CMD_FAILURE, "fullscreen",
17 "Can't run this command while there's no outputs connected."); 17 "Can't run this command while there's no outputs connected.");
18 } 18 }
19 struct sway_node *node = config->handler_context.node; 19 struct sway_node *node = config->handler_context.node;
20 struct sway_container *container = config->handler_context.container; 20 struct sway_container *container = config->handler_context.container;
21 struct sway_workspace *workspace = config->handler_context.workspace; 21 struct sway_workspace *workspace = config->handler_context.workspace;
22 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) { 22 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
23 return cmd_results_new(CMD_INVALID, "fullscreen", 23 return cmd_results_new(CMD_FAILURE, "fullscreen",
24 "Can't fullscreen an empty workspace"); 24 "Can't fullscreen an empty workspace");
25 } 25 }
26 if (node->type == N_WORKSPACE) { 26 if (node->type == N_WORKSPACE) {
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index e7ed69c6..69f46269 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,10 +1,69 @@
1#include <limits.h>
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
4#include <wlr/backend/libinput.h>
3#include "sway/config.h" 5#include "sway/config.h"
4#include "sway/commands.h" 6#include "sway/commands.h"
5#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
6#include "log.h" 8#include "log.h"
7 9
10static void toggle_send_events_for_device(struct input_config *ic,
11 struct sway_input_device *input_device) {
12 struct wlr_input_device *wlr_device = input_device->wlr_device;
13 if (!wlr_input_device_is_libinput(wlr_device)) {
14 return;
15 }
16 struct libinput_device *libinput_dev
17 = wlr_libinput_get_device_handle(wlr_device);
18
19 enum libinput_config_send_events_mode mode =
20 libinput_device_config_send_events_get_mode(libinput_dev);
21 uint32_t possible =
22 libinput_device_config_send_events_get_modes(libinput_dev);
23
24 switch (mode) {
25 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
26 mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
27 if (possible & mode) {
28 break;
29 }
30 // fall through
31 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
32 mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
33 if (possible & mode) {
34 break;
35 }
36 // fall through
37 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
38 default:
39 mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
40 break;
41 }
42
43 ic->send_events = mode;
44}
45
46static void toggle_send_events(struct input_config *ic) {
47 struct sway_input_device *input_device = NULL;
48 wl_list_for_each(input_device, &server.input->devices, link) {
49 if (strcmp(input_device->identifier, ic->identifier) == 0) {
50 toggle_send_events_for_device(ic, input_device);
51 }
52 }
53}
54
55static void toggle_wildcard_send_events() {
56 struct sway_input_device *input_device = NULL;
57 wl_list_for_each(input_device, &server.input->devices, link) {
58 struct input_config *ic = new_input_config(input_device->identifier);
59 if (!ic) {
60 break;
61 }
62 toggle_send_events_for_device(ic, input_device);
63 store_input_config(ic);
64 }
65}
66
8struct cmd_results *input_cmd_events(int argc, char **argv) { 67struct cmd_results *input_cmd_events(int argc, char **argv) {
9 struct cmd_results *error = NULL; 68 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { 69 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
@@ -23,9 +82,24 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
23 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { 82 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
24 ic->send_events = 83 ic->send_events =
25 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 84 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
26 } else { 85 } else if (config->reading) {
27 return cmd_results_new(CMD_INVALID, "events", 86 return cmd_results_new(CMD_INVALID, "events",
28 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); 87 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
88 } else if (strcasecmp(argv[0], "toggle") == 0) {
89 if (strcmp(ic->identifier, "*") == 0) {
90 // Update the device input configs and then reset the wildcard
91 // config send events mode so that is does not override the device
92 // ones. The device ones will be applied when attempting to apply
93 // the wildcard config
94 toggle_wildcard_send_events();
95 ic->send_events = INT_MIN;
96 } else {
97 toggle_send_events(ic);
98 }
99 } else {
100 return cmd_results_new(CMD_INVALID, "events",
101 "Expected 'events <enabled|disabled|disabled_on_external_mouse|"
102 "toggle>'");
29 } 103 }
30 104
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 105 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 1958f23c..d82a1fe1 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -1,9 +1,7 @@
1#include <string.h> 1#include <libevdev/libevdev.h>
2#include <strings.h>
3#include <errno.h>
4#include "sway/config.h" 2#include "sway/config.h"
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/input/input-manager.h" 4#include "sway/input/cursor.h"
7 5
8struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { 6struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
9 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
@@ -16,22 +14,26 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
16 "No input device defined."); 14 "No input device defined.");
17 } 15 }
18 16
19 errno = 0; 17 if (strcmp(*argv, "disable") == 0) {
20 char *endptr; 18 ic->scroll_button = 0;
21 int scroll_button = strtol(*argv, &endptr, 10); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 if (endptr == *argv && scroll_button == 0) {
23 return cmd_results_new(CMD_INVALID, "scroll_button",
24 "Scroll button identifier must be an integer.");
25 } 20 }
26 if (errno == ERANGE) { 21
22 char *message = NULL;
23 uint32_t button = get_mouse_button(*argv, &message);
24 if (message) {
25 error = cmd_results_new(CMD_INVALID, "scroll_button", message);
26 free(message);
27 return error;
28 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
29 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
27 return cmd_results_new(CMD_INVALID, "scroll_button", 30 return cmd_results_new(CMD_INVALID, "scroll_button",
28 "Scroll button identifier out of range."); 31 "X11 axis buttons are not supported for scroll_button");
29 } 32 } else if (!button) {
30 if (scroll_button < 0) {
31 return cmd_results_new(CMD_INVALID, "scroll_button", 33 return cmd_results_new(CMD_INVALID, "scroll_button",
32 "Scroll button identifier cannot be negative."); 34 "Unknown button %s", *argv);
33 } 35 }
34 ic->scroll_button = scroll_button; 36 ic->scroll_button = button;
35 37
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37} 39}
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 5fccd4a3..43166401 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index c4d04638..066f632b 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index 794ab6e9..09dc4a5c 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 257c3288..d3e576e6 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index 3832dc8e..2d7581d1 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index 637ca45e..189e3c1a 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 2#include <stdbool.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 7d8c1f1a..72e177e8 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 2#include <ctype.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
@@ -64,7 +64,7 @@ static struct sway_output *output_in_direction(const char *direction_string,
64 } 64 }
65 } 65 }
66 66
67 return output_by_name(direction_string); 67 return output_by_name_or_id(direction_string);
68} 68}
69 69
70static bool is_parallel(enum sway_container_layout layout, 70static bool is_parallel(enum sway_container_layout layout,
@@ -154,6 +154,8 @@ static void container_move_to_container_from_direction(
154static void container_move_to_workspace_from_direction( 154static void container_move_to_workspace_from_direction(
155 struct sway_container *container, struct sway_workspace *workspace, 155 struct sway_container *container, struct sway_workspace *workspace,
156 enum wlr_direction move_dir) { 156 enum wlr_direction move_dir) {
157 container->width = container->height = 0;
158
157 if (is_parallel(workspace->layout, move_dir)) { 159 if (is_parallel(workspace->layout, move_dir)) {
158 wlr_log(WLR_DEBUG, "Reparenting container (parallel)"); 160 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
159 int index = 161 int index =
@@ -216,6 +218,7 @@ static void container_move_to_container(struct sway_container *container,
216 return; 218 return;
217 } 219 }
218 if (container_is_floating(container)) { 220 if (container_is_floating(container)) {
221 container_move_to_workspace(container, destination->workspace);
219 return; 222 return;
220 } 223 }
221 struct sway_workspace *old_workspace = container->workspace; 224 struct sway_workspace *old_workspace = container->workspace;
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
index 61a8de7e..cb81a445 100644
--- a/sway/commands/no_focus.c
+++ b/sway/commands/no_focus.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/criteria.h" 3#include "sway/criteria.h"
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 30fb47c4..2cd1b76a 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -116,11 +116,8 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
116 if (!can_access) { 116 if (!can_access) {
117 wlr_log(WLR_ERROR, "Unable to access background file '%s': %s", 117 wlr_log(WLR_ERROR, "Unable to access background file '%s': %s",
118 src, strerror(errno)); 118 src, strerror(errno));
119 if (config->reading && !config->validating) { 119 config_add_swaynag_warning("Unable to access background file '%s'",
120 swaynag_log(config->swaynag_command, 120 src);
121 &config->swaynag_config_errors,
122 "Unable to access background file '%s'", src);
123 }
124 free(src); 121 free(src);
125 } else { 122 } else {
126 // Escape double quotes in the final path for swaybg 123 // Escape double quotes in the final path for swaybg
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
index c1555323..ca6f73a4 100644
--- a/sway/commands/output/transform.c
+++ b/sway/commands/output/transform.c
@@ -45,7 +45,7 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) {
45 return cmd_results_new(CMD_INVALID, "output", 45 return cmd_results_new(CMD_INVALID, "output",
46 "Cannot apply relative transform to all outputs."); 46 "Cannot apply relative transform to all outputs.");
47 } 47 }
48 struct sway_output *s_output = output_by_name(output->name); 48 struct sway_output *s_output = output_by_name_or_id(output->name);
49 if (s_output == NULL) { 49 if (s_output == NULL) {
50 return cmd_results_new(CMD_INVALID, "output", 50 return cmd_results_new(CMD_INVALID, "output",
51 "Cannot apply relative transform to unknown output %s", output->name); 51 "Cannot apply relative transform to unknown output %s", output->name);
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 62105cdc..3ccbbf34 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/config.h" 4#include "sway/config.h"
@@ -24,8 +24,7 @@ static void do_reload(void *data) {
24 24
25 if (!load_main_config(config->current_config_path, true, false)) { 25 if (!load_main_config(config->current_config_path, true, false)) {
26 wlr_log(WLR_ERROR, "Error(s) reloading config"); 26 wlr_log(WLR_ERROR, "Error(s) reloading config");
27 list_foreach(bar_ids, free); 27 list_free_items_and_destroy(bar_ids);
28 list_free(bar_ids);
29 return; 28 return;
30 } 29 }
31 30
@@ -42,9 +41,7 @@ static void do_reload(void *data) {
42 } 41 }
43 } 42 }
44 } 43 }
45 44 list_free_items_and_destroy(bar_ids);
46 list_foreach(bar_ids, free);
47 list_free(bar_ids);
48 45
49 config_update_font_height(true); 46 config_update_font_height(true);
50 root_for_each_container(rebuild_textures_iterator, NULL); 47 root_for_each_container(rebuild_textures_iterator, NULL);
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 0cee9293..491dbab0 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h> 1#include <ctype.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -82,8 +81,12 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
82 struct sway_workspace *tmp_workspace = workspace_by_name(new_name); 81 struct sway_workspace *tmp_workspace = workspace_by_name(new_name);
83 if (tmp_workspace) { 82 if (tmp_workspace) {
84 free(new_name); 83 free(new_name);
85 return cmd_results_new(CMD_INVALID, "rename", 84 if (tmp_workspace == workspace) {
86 "Workspace already exists"); 85 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
86 } else {
87 return cmd_results_new(CMD_INVALID, "rename",
88 "Workspace already exists");
89 }
87 } 90 }
88 91
89 wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 92 wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index a90d578e..cf5dea02 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -512,34 +512,42 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
512 calculate_constraints(&min_width, &max_width, &min_height, &max_height); 512 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
513 513
514 if (width->amount) { 514 if (width->amount) {
515 if (width->unit == RESIZE_UNIT_PPT || 515 switch (width->unit) {
516 width->unit == RESIZE_UNIT_DEFAULT) { 516 case RESIZE_UNIT_PPT:
517 // Convert to px 517 // Convert to px
518 width->amount = con->workspace->width * width->amount / 100; 518 width->amount = con->workspace->width * width->amount / 100;
519 width->unit = RESIZE_UNIT_PX; 519 width->unit = RESIZE_UNIT_PX;
520 } 520 // Falls through
521 if (width->unit == RESIZE_UNIT_PX) { 521 case RESIZE_UNIT_PX:
522 case RESIZE_UNIT_DEFAULT:
522 width->amount = fmax(min_width, fmin(width->amount, max_width)); 523 width->amount = fmax(min_width, fmin(width->amount, max_width));
523 grow_width = width->amount - con->width; 524 grow_width = width->amount - con->width;
524
525 con->x -= grow_width / 2; 525 con->x -= grow_width / 2;
526 con->width = width->amount; 526 con->width = width->amount;
527 break;
528 case RESIZE_UNIT_INVALID:
529 sway_assert(false, "invalid width unit");
530 break;
527 } 531 }
528 } 532 }
529 533
530 if (height->amount) { 534 if (height->amount) {
531 if (height->unit == RESIZE_UNIT_PPT || 535 switch (height->unit) {
532 height->unit == RESIZE_UNIT_DEFAULT) { 536 case RESIZE_UNIT_PPT:
533 // Convert to px 537 // Convert to px
534 height->amount = con->workspace->height * height->amount / 100; 538 height->amount = con->workspace->height * height->amount / 100;
535 height->unit = RESIZE_UNIT_PX; 539 height->unit = RESIZE_UNIT_PX;
536 } 540 // Falls through
537 if (height->unit == RESIZE_UNIT_PX) { 541 case RESIZE_UNIT_PX:
542 case RESIZE_UNIT_DEFAULT:
538 height->amount = fmax(min_height, fmin(height->amount, max_height)); 543 height->amount = fmax(min_height, fmin(height->amount, max_height));
539 grow_height = height->amount - con->height; 544 grow_height = height->amount - con->height;
540
541 con->y -= grow_height / 2; 545 con->y -= grow_height / 2;
542 con->height = height->amount; 546 con->height = height->amount;
547 break;
548 case RESIZE_UNIT_INVALID:
549 sway_assert(false, "invalid height unit");
550 break;
543 } 551 }
544 } 552 }
545 553
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 5abb19b0..b8db862b 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -10,6 +10,7 @@ static struct cmd_handler seat_handlers[] = {
10 { "attach", seat_cmd_attach }, 10 { "attach", seat_cmd_attach },
11 { "cursor", seat_cmd_cursor }, 11 { "cursor", seat_cmd_cursor },
12 { "fallback", seat_cmd_fallback }, 12 { "fallback", seat_cmd_fallback },
13 { "hide_cursor", seat_cmd_hide_cursor },
13}; 14};
14 15
15struct cmd_results *cmd_seat(int argc, char **argv) { 16struct cmd_results *cmd_seat(int argc, char **argv) {
@@ -26,9 +27,18 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
26 27
27 struct cmd_results *res = config_subcommand(argv + 1, argc - 1, 28 struct cmd_results *res = config_subcommand(argv + 1, argc - 1,
28 seat_handlers, sizeof(seat_handlers)); 29 seat_handlers, sizeof(seat_handlers));
30 if (res && res->status != CMD_SUCCESS) {
31 free_seat_config(config->handler_context.seat_config);
32 config->handler_context.seat_config = NULL;
33 return res;
34 }
29 35
30 free_seat_config(config->handler_context.seat_config); 36 struct seat_config *sc =
31 config->handler_context.seat_config = NULL; 37 store_seat_config(config->handler_context.seat_config);
38 if (!config->reading) {
39 input_manager_apply_seat_config(sc);
40 }
32 41
33 return res; 42 config->handler_context.seat_config = NULL;
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
34} 44}
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 6b4bcf1f..0fb17f1d 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -1,10 +1,7 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include <strings.h>
4#include "sway/input/input-manager.h"
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/config.h" 4#include "sway/config.h"
7#include "log.h"
8#include "stringop.h" 5#include "stringop.h"
9 6
10struct cmd_results *seat_cmd_attach(int argc, char **argv) { 7struct cmd_results *seat_cmd_attach(int argc, char **argv) {
@@ -12,19 +9,17 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) {
12 if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) {
13 return error; 10 return error;
14 } 11 }
15 struct seat_config *current_seat_config = 12 if (!config->handler_context.seat_config) {
16 config->handler_context.seat_config;
17 if (!current_seat_config) {
18 return cmd_results_new(CMD_FAILURE, "attach", "No seat defined"); 13 return cmd_results_new(CMD_FAILURE, "attach", "No seat defined");
19 } 14 }
20 15
21 struct seat_config *new_config = new_seat_config(current_seat_config->name); 16 struct seat_attachment_config *attachment = seat_attachment_config_new();
22 struct seat_attachment_config *new_attachment = seat_attachment_config_new(); 17 if (!attachment) {
23 new_attachment->identifier = strdup(argv[0]); 18 return cmd_results_new(CMD_FAILURE, "attach",
24 list_add(new_config->attachments, new_attachment); 19 "Failed to allocate seat attachment config");
25
26 if (!config->validating) {
27 apply_seat_config(new_config);
28 } 20 }
21 attachment->identifier = strdup(argv[0]);
22 list_add(config->handler_context.seat_config->attachments, attachment);
23
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 25}
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 1d41a94e..8d9e426a 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,12 +1,9 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#ifdef __linux__
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7 3
8#include <strings.h> 4#include <strings.h>
9#include <wlr/types/wlr_cursor.h> 5#include <wlr/types/wlr_cursor.h>
6#include <wlr/types/wlr_pointer.h>
10#include "sway/commands.h" 7#include "sway/commands.h"
11#include "sway/input/cursor.h" 8#include "sway/input/cursor.h"
12 9
@@ -15,20 +12,10 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
15 12
16static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or " 13static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or "
17 "'cursor <set> <x> <y>' or " 14 "'cursor <set> <x> <y>' or "
18 "'curor <press|release> <left|right|1|2|3...>'"; 15 "'curor <press|release> <button[1-9]|event-name-or-code>'";
19
20struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
21 struct cmd_results *error = NULL;
22 if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
23 return error;
24 }
25 struct sway_seat *seat = config->handler_context.seat;
26 if (!seat) {
27 return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
28 }
29
30 struct sway_cursor *cursor = seat->cursor;
31 16
17static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) {
32 if (strcasecmp(argv[0], "move") == 0) { 19 if (strcasecmp(argv[0], "move") == 0) {
33 if (argc < 3) { 20 if (argc < 3) {
34 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 21 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
@@ -50,6 +37,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
50 if (argc < 2) { 37 if (argc < 2) {
51 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 38 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
52 } 39 }
40 struct cmd_results *error = NULL;
53 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 41 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
54 return error; 42 return error;
55 } 43 }
@@ -58,6 +46,40 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
58 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 46 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
59} 47}
60 48
49struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
50 struct cmd_results *error = NULL;
51 if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
52 return error;
53 }
54 struct seat_config *sc = config->handler_context.seat_config;
55 if (!sc) {
56 return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
57 }
58
59 if (config->reading || !config->active) {
60 return cmd_results_new(CMD_DEFER, NULL, NULL);
61 }
62
63 if (strcmp(sc->name, "*") != 0) {
64 struct sway_seat *seat = input_manager_get_seat(sc->name);
65 if (!seat) {
66 return cmd_results_new(CMD_FAILURE, "cursor",
67 "Failed to get seat");
68 }
69 error = handle_command(seat->cursor, argc, argv);
70 } else {
71 struct sway_seat *seat = NULL;
72 wl_list_for_each(seat, &server.input->seats, link) {
73 error = handle_command(seat->cursor, argc, argv);
74 if ((error && error->status != CMD_SUCCESS)) {
75 break;
76 }
77 }
78 }
79
80 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
81}
82
61static struct cmd_results *press_or_release(struct sway_cursor *cursor, 83static struct cmd_results *press_or_release(struct sway_cursor *cursor,
62 char *action, char *button_str) { 84 char *action, char *button_str) {
63 enum wlr_button_state state; 85 enum wlr_button_state state;
@@ -70,15 +92,35 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
70 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 92 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
71 } 93 }
72 94
73 if (strcasecmp(button_str, "left") == 0) { 95 char *message = NULL;
74 button = BTN_LEFT; 96 button = get_mouse_button(button_str, &message);
75 } else if (strcasecmp(button_str, "right") == 0) { 97 if (message) {
76 button = BTN_RIGHT; 98 struct cmd_results *error =
77 } else { 99 cmd_results_new(CMD_INVALID, "cursor", message);
78 button = strtol(button_str, NULL, 10); 100 free(message);
79 if (button == 0) { 101 return error;
80 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 102 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
81 } 103 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
104 // Dispatch axis event
105 enum wlr_axis_orientation orientation =
106 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
107 ? WLR_AXIS_ORIENTATION_VERTICAL
108 : WLR_AXIS_ORIENTATION_HORIZONTAL;
109 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
110 ? -1 : 1;
111 struct wlr_event_pointer_axis event = {
112 .device = NULL,
113 .time_msec = 0,
114 .source = WLR_AXIS_SOURCE_WHEEL,
115 .orientation = orientation,
116 .delta = delta * 15,
117 .delta_discrete = delta
118 };
119 dispatch_cursor_axis(cursor, &event);
120 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
121 } else if (!button) {
122 return cmd_results_new(CMD_INVALID, "curor",
123 "Unknown button %s", button_str);
82 } 124 }
83 dispatch_cursor_button(cursor, NULL, 0, button, state); 125 dispatch_cursor_button(cursor, NULL, 0, button, state);
84 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 126 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c
index a0ddf3ef..8f1ab12c 100644
--- a/sway/commands/seat/fallback.c
+++ b/sway/commands/seat/fallback.c
@@ -1,27 +1,18 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h" 1#include "sway/config.h"
4#include "sway/commands.h" 2#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6#include "util.h" 3#include "util.h"
7 4
8struct cmd_results *seat_cmd_fallback(int argc, char **argv) { 5struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
9 struct cmd_results *error = NULL; 6 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) { 7 if ((error = checkarg(argc, "fallback", EXPECTED_EQUAL_TO, 1))) {
11 return error; 8 return error;
12 } 9 }
13 struct seat_config *current_seat_config = 10 if (!config->handler_context.seat_config) {
14 config->handler_context.seat_config;
15 if (!current_seat_config) {
16 return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined"); 11 return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined");
17 } 12 }
18 struct seat_config *new_config =
19 new_seat_config(current_seat_config->name);
20
21 new_config->fallback = parse_boolean(argv[0], false);
22 13
23 if (!config->validating) { 14 config->handler_context.seat_config->fallback =
24 apply_seat_config(new_config); 15 parse_boolean(argv[0], false);
25 } 16
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 18}
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
new file mode 100644
index 00000000..343573b5
--- /dev/null
+++ b/sway/commands/seat/hide_cursor.c
@@ -0,0 +1,29 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "sway/input/seat.h"
6#include "stringop.h"
7
8struct cmd_results *seat_cmd_hide_cursor(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "hide_cursor", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if (!config->handler_context.seat_config) {
14 return cmd_results_new(CMD_FAILURE, "hide_cursor", "No seat defined");
15 }
16
17 char *end;
18 int timeout = strtol(argv[0], &end, 10);
19 if (*end) {
20 return cmd_results_new(CMD_INVALID, "hide_cursor",
21 "Expected an integer timeout");
22 }
23 if (timeout < 100 && timeout != 0) {
24 timeout = 100;
25 }
26 config->handler_context.seat_config->hide_cursor_timeout = timeout;
27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29}
diff --git a/sway/commands/set.c b/sway/commands/set.c
index be51230b..d912e4fd 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
diff --git a/sway/commands/split.c b/sway/commands/split.c
index ed106a86..84385fa9 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -18,6 +18,10 @@ static struct cmd_results *do_split(int layout) {
18 workspace_split(ws, layout); 18 workspace_split(ws, layout);
19 } 19 }
20 20
21 if (con && con->parent && con->parent->parent) {
22 container_flatten(con->parent->parent);
23 }
24
21 arrange_workspace(ws); 25 arrange_workspace(ws);
22 26
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 08860264..670d6bca 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -108,7 +108,7 @@ static void container_swap(struct sway_container *con1,
108 container_set_fullscreen(con2, false); 108 container_set_fullscreen(con2, false);
109 } 109 }
110 110
111 struct sway_seat *seat = input_manager_get_default_seat(); 111 struct sway_seat *seat = config->handler_context.seat;
112 struct sway_container *focus = seat_get_focused_container(seat); 112 struct sway_container *focus = seat_get_focused_container(seat);
113 struct sway_workspace *vis1 = 113 struct sway_workspace *vis1 =
114 output_get_active_workspace(con1->workspace->output); 114 output_get_active_workspace(con1->workspace->output);
diff --git a/sway/commands/tiling_drag_threshold.c b/sway/commands/tiling_drag_threshold.c
new file mode 100644
index 00000000..6b0531c3
--- /dev/null
+++ b/sway/commands/tiling_drag_threshold.c
@@ -0,0 +1,22 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "log.h"
5
6struct cmd_results *cmd_tiling_drag_threshold(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "tiling_drag_threshold", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 char *inv;
13 int value = strtol(argv[0], &inv, 10);
14 if (*inv != '\0' || value < 0) {
15 return cmd_results_new(CMD_INVALID, "tiling_drag_threshold",
16 "Invalid threshold specified");
17 }
18
19 config->tiling_drag_threshold = value;
20
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22}
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c
new file mode 100644
index 00000000..82578186
--- /dev/null
+++ b/sway/commands/title_align.c
@@ -0,0 +1,30 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3#include "sway/output.h"
4#include "sway/tree/container.h"
5#include "sway/tree/root.h"
6
7struct cmd_results *cmd_title_align(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (strcmp(argv[0], "left") == 0) {
14 config->title_align = ALIGN_LEFT;
15 } else if (strcmp(argv[0], "center") == 0) {
16 config->title_align = ALIGN_CENTER;
17 } else if (strcmp(argv[0], "right") == 0) {
18 config->title_align = ALIGN_RIGHT;
19 } else {
20 return cmd_results_new(CMD_INVALID, "title_align",
21 "Expected 'title_align left|center|right'");
22 }
23
24 for (int i = 0; i < root->outputs->length; ++i) {
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_whole(output);
27 }
28
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c
new file mode 100644
index 00000000..c1e9bb52
--- /dev/null
+++ b/sway/commands/titlebar_border_thickness.c
@@ -0,0 +1,30 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "sway/tree/arrange.h"
6#include "log.h"
7
8struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "titlebar_border_thickness", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13
14 char *inv;
15 int value = strtol(argv[0], &inv, 10);
16 if (*inv != '\0' || value < 0 || value > config->titlebar_v_padding) {
17 return cmd_results_new(CMD_FAILURE, "titlebar_border_thickness",
18 "Invalid size specified");
19 }
20
21 config->titlebar_border_thickness = value;
22
23 for (int i = 0; i < root->outputs->length; ++i) {
24 struct sway_output *output = root->outputs->items[i];
25 arrange_workspace(output_get_active_workspace(output));
26 output_damage_whole(output);
27 }
28
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c
new file mode 100644
index 00000000..a642e945
--- /dev/null
+++ b/sway/commands/titlebar_padding.c
@@ -0,0 +1,42 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "sway/tree/arrange.h"
6#include "log.h"
7
8struct cmd_results *cmd_titlebar_padding(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "titlebar_padding", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13
14 char *inv;
15 int h_value = strtol(argv[0], &inv, 10);
16 if (*inv != '\0' || h_value < 0 || h_value < config->titlebar_border_thickness) {
17 return cmd_results_new(CMD_FAILURE, "titlebar_padding",
18 "Invalid size specified");
19 }
20
21 int v_value;
22 if (argc == 1) {
23 v_value = h_value;
24 } else {
25 v_value = strtol(argv[1], &inv, 10);
26 if (*inv != '\0' || v_value < 0 || v_value < config->titlebar_border_thickness) {
27 return cmd_results_new(CMD_FAILURE, "titlebar_padding",
28 "Invalid size specified");
29 }
30 }
31
32 config->titlebar_v_padding = v_value;
33 config->titlebar_h_padding = h_value;
34
35 for (int i = 0; i < root->outputs->length; ++i) {
36 struct sway_output *output = root->outputs->items[i];
37 arrange_workspace(output_get_active_workspace(output));
38 output_damage_whole(output);
39 }
40
41 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
42}
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 92118ecf..211b344d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 2#include <ctype.h>
3#include <limits.h> 3#include <limits.h>
4#include <string.h> 4#include <string.h>
@@ -33,12 +33,12 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
33 33
34void free_workspace_config(struct workspace_config *wsc) { 34void free_workspace_config(struct workspace_config *wsc) {
35 free(wsc->workspace); 35 free(wsc->workspace);
36 free_flat_list(wsc->outputs); 36 list_free_items_and_destroy(wsc->outputs);
37 free(wsc); 37 free(wsc);
38} 38}
39 39
40static void prevent_invalid_outer_gaps(struct workspace_config *wsc) { 40static void prevent_invalid_outer_gaps(struct workspace_config *wsc) {
41 if (wsc->gaps_outer.top != INT_MIN && 41 if (wsc->gaps_outer.top != INT_MIN &&
42 wsc->gaps_outer.top < -wsc->gaps_inner) { 42 wsc->gaps_outer.top < -wsc->gaps_inner) {
43 wsc->gaps_outer.top = -wsc->gaps_inner; 43 wsc->gaps_outer.top = -wsc->gaps_inner;
44 } 44 }
diff --git a/sway/config.c b/sway/config.c
index 6f65d0c2..18fee404 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -1,5 +1,4 @@
1#define _POSIX_C_SOURCE 200809L 1#define _XOPEN_SOURCE 700 // for realpath
2#define _XOPEN_SOURCE 700
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
5#include <stdlib.h> 4#include <stdlib.h>
@@ -14,11 +13,7 @@
14#include <limits.h> 13#include <limits.h>
15#include <dirent.h> 14#include <dirent.h>
16#include <strings.h> 15#include <strings.h>
17#ifdef __linux__
18#include <linux/input-event-codes.h> 16#include <linux/input-event-codes.h>
19#elif __FreeBSD__
20#include <dev/evdev/input-event-codes.h>
21#endif
22#include <wlr/types/wlr_output.h> 17#include <wlr/types/wlr_output.h>
23#include "sway/input/input-manager.h" 18#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 19#include "sway/input/seat.h"
@@ -31,7 +26,6 @@
31#include "sway/tree/workspace.h" 26#include "sway/tree/workspace.h"
32#include "cairo.h" 27#include "cairo.h"
33#include "pango.h" 28#include "pango.h"
34#include "readline.h"
35#include "stringop.h" 29#include "stringop.h"
36#include "list.h" 30#include "list.h"
37#include "log.h" 31#include "log.h"
@@ -39,26 +33,24 @@
39struct sway_config *config = NULL; 33struct sway_config *config = NULL;
40 34
41static void free_mode(struct sway_mode *mode) { 35static void free_mode(struct sway_mode *mode) {
42 int i;
43
44 if (!mode) { 36 if (!mode) {
45 return; 37 return;
46 } 38 }
47 free(mode->name); 39 free(mode->name);
48 if (mode->keysym_bindings) { 40 if (mode->keysym_bindings) {
49 for (i = 0; i < mode->keysym_bindings->length; i++) { 41 for (int i = 0; i < mode->keysym_bindings->length; i++) {
50 free_sway_binding(mode->keysym_bindings->items[i]); 42 free_sway_binding(mode->keysym_bindings->items[i]);
51 } 43 }
52 list_free(mode->keysym_bindings); 44 list_free(mode->keysym_bindings);
53 } 45 }
54 if (mode->keycode_bindings) { 46 if (mode->keycode_bindings) {
55 for (i = 0; i < mode->keycode_bindings->length; i++) { 47 for (int i = 0; i < mode->keycode_bindings->length; i++) {
56 free_sway_binding(mode->keycode_bindings->items[i]); 48 free_sway_binding(mode->keycode_bindings->items[i]);
57 } 49 }
58 list_free(mode->keycode_bindings); 50 list_free(mode->keycode_bindings);
59 } 51 }
60 if (mode->mouse_bindings) { 52 if (mode->mouse_bindings) {
61 for (i = 0; i < mode->mouse_bindings->length; i++) { 53 for (int i = 0; i < mode->mouse_bindings->length; i++) {
62 free_sway_binding(mode->mouse_bindings->items[i]); 54 free_sway_binding(mode->mouse_bindings->items[i]);
63 } 55 }
64 list_free(mode->mouse_bindings); 56 list_free(mode->mouse_bindings);
@@ -214,6 +206,10 @@ static void config_defaults(struct sway_config *config) {
214 config->popup_during_fullscreen = POPUP_SMART; 206 config->popup_during_fullscreen = POPUP_SMART;
215 config->xwayland = true; 207 config->xwayland = true;
216 208
209 config->titlebar_border_thickness = 1;
210 config->titlebar_h_padding = 5;
211 config->titlebar_v_padding = 4;
212
217 // floating view 213 // floating view
218 config->floating_maximum_width = 0; 214 config->floating_maximum_width = 0;
219 config->floating_maximum_height = 0; 215 config->floating_maximum_height = 0;
@@ -231,7 +227,9 @@ static void config_defaults(struct sway_config *config) {
231 config->auto_back_and_forth = false; 227 config->auto_back_and_forth = false;
232 config->reading = false; 228 config->reading = false;
233 config->show_marks = true; 229 config->show_marks = true;
230 config->title_align = ALIGN_LEFT;
234 config->tiling_drag = true; 231 config->tiling_drag = true;
232 config->tiling_drag_threshold = 9;
235 233
236 config->smart_gaps = false; 234 config->smart_gaps = false;
237 config->gaps_inner = 0; 235 config->gaps_inner = 0;
@@ -313,27 +311,16 @@ static char *get_config_path(void) {
313 SYSCONFDIR "/i3/config", 311 SYSCONFDIR "/i3/config",
314 }; 312 };
315 313
316 if (!getenv("XDG_CONFIG_HOME")) { 314 char *config_home = getenv("XDG_CONFIG_HOME");
317 char *home = getenv("HOME"); 315 if (!config_home || !*config_home) {
318 char *config_home = malloc(strlen(home) + strlen("/.config") + 1); 316 config_paths[1] = "$HOME/.config/sway/config";
319 if (!config_home) { 317 config_paths[3] = "$HOME/.config/i3/config";
320 wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config");
321 } else {
322 strcpy(config_home, home);
323 strcat(config_home, "/.config");
324 setenv("XDG_CONFIG_HOME", config_home, 1);
325 wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
326 free(config_home);
327 }
328 } 318 }
329 319
330 wordexp_t p; 320 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {
331 char *path; 321 wordexp_t p;
332 322 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) {
333 int i; 323 char *path = strdup(p.we_wordv[0]);
334 for (i = 0; i < (int)(sizeof(config_paths) / sizeof(char *)); ++i) {
335 if (wordexp(config_paths[i], &p, 0) == 0) {
336 path = strdup(p.we_wordv[0]);
337 wordfree(&p); 324 wordfree(&p);
338 if (file_exists(path)) { 325 if (file_exists(path)) {
339 return path; 326 return path;
@@ -342,7 +329,7 @@ static char *get_config_path(void) {
342 } 329 }
343 } 330 }
344 331
345 return NULL; // Not reached 332 return NULL;
346} 333}
347 334
348static bool load_config(const char *path, struct sway_config *config, 335static bool load_config(const char *path, struct sway_config *config,
@@ -402,7 +389,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
402 &old_config->swaynag_config_errors, 389 &old_config->swaynag_config_errors,
403 sizeof(struct swaynag_instance)); 390 sizeof(struct swaynag_instance));
404 391
405 create_default_output_configs(); 392 input_manager_reset_all_inputs();
406 } 393 }
407 394
408 config->current_config_path = path; 395 config->current_config_path = path;
@@ -454,7 +441,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
454 } 441 }
455 } 442 }
456 443
457 free_flat_list(secconfigs); 444 list_free_items_and_destroy(secconfigs);
458 } 445 }
459 */ 446 */
460 447
@@ -475,6 +462,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
475 if (config->swaynag_config_errors.pid > 0) { 462 if (config->swaynag_config_errors.pid > 0) {
476 swaynag_show(&config->swaynag_config_errors); 463 swaynag_show(&config->swaynag_config_errors);
477 } 464 }
465
466 input_manager_verify_fallback_seat();
467 for (int i = 0; i < config->seat_configs->length; i++) {
468 input_manager_apply_seat_config(config->seat_configs->items[i]);
469 }
478 } 470 }
479 471
480 if (old_config) { 472 if (old_config) {
@@ -581,29 +573,56 @@ bool load_include_configs(const char *path, struct sway_config *config,
581 return true; 573 return true;
582} 574}
583 575
584static int detect_brace_on_following_line(FILE *file, char *line, 576// get line, with backslash continuation
585 int line_number) { 577static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file,
586 int lines = 0; 578 int *nlines) {
587 if (line[strlen(line) - 1] != '{' && line[strlen(line) - 1] != '}') { 579 char *next_line = NULL;
588 char *peeked = NULL; 580 size_t next_line_size = 0;
589 long position = 0; 581 ssize_t nread = getline(lineptr, line_size, file);
590 do { 582 *nlines = nread == -1 ? 0 : 1;
591 free(peeked); 583 while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) {
592 peeked = peek_line(file, lines, &position); 584 ssize_t next_nread = getline(&next_line, &next_line_size, file);
593 if (peeked) { 585 if (next_nread == -1) {
594 peeked = strip_whitespace(peeked); 586 break;
587 }
588 (*nlines)++;
589
590 nread += next_nread - 2;
591 if ((ssize_t) *line_size < nread + 1) {
592 *line_size = nread + 1;
593 *lineptr = realloc(*lineptr, *line_size);
594 if (!*lineptr) {
595 nread = -1;
596 break;
595 } 597 }
596 lines++; 598 }
597 } while (peeked && strlen(peeked) == 0); 599 strcpy(&(*lineptr)[nread - next_nread], next_line);
600 }
601 free(next_line);
602 return nread;
603}
598 604
599 if (peeked && strlen(peeked) == 1 && peeked[0] == '{') { 605static int detect_brace(FILE *file) {
600 fseek(file, position, SEEK_SET); 606 int ret = 0;
601 } else { 607 int lines = 0;
602 lines = 0; 608 long pos = ftell(file);
609 char *line = NULL;
610 size_t line_size = 0;
611 while ((getline(&line, &line_size, file)) != -1) {
612 lines++;
613 strip_whitespace(line);
614 if (*line) {
615 if (strcmp(line, "{") == 0) {
616 ret = lines;
617 }
618 break;
603 } 619 }
604 free(peeked);
605 } 620 }
606 return lines; 621 free(line);
622 if (ret == 0) {
623 fseek(file, pos, SEEK_SET);
624 }
625 return ret;
607} 626}
608 627
609static char *expand_line(const char *block, const char *line, bool add_brace) { 628static char *expand_line(const char *block, const char *line, bool add_brace) {
@@ -645,58 +664,51 @@ bool read_config(FILE *file, struct sway_config *config,
645 664
646 bool success = true; 665 bool success = true;
647 int line_number = 0; 666 int line_number = 0;
648 char *line; 667 char *line = NULL;
668 size_t line_size = 0;
669 ssize_t nread;
649 list_t *stack = create_list(); 670 list_t *stack = create_list();
650 size_t read = 0; 671 size_t read = 0;
651 while (!feof(file)) { 672 int nlines = 0;
652 char *block = stack->length ? stack->items[0] : NULL; 673 while ((nread = getline_with_cont(&line, &line_size, file, &nlines)) != -1) {
653 line = read_line(file);
654 if (!line) {
655 continue;
656 }
657 line_number++;
658 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
659
660 if (reading_main_config) { 674 if (reading_main_config) {
661 size_t length = strlen(line); 675 if (read + nread > config_size) {
662
663 if (read + length > config_size) {
664 wlr_log(WLR_ERROR, "Config file changed during reading"); 676 wlr_log(WLR_ERROR, "Config file changed during reading");
665 list_foreach(stack, free); 677 success = false;
666 list_free(stack); 678 break;
667 free(line);
668 return false;
669 } 679 }
670 680
671 strcpy(this_config + read, line); 681 strcpy(&this_config[read], line);
672 if (line_number != 1) { 682 read += nread;
673 this_config[read - 1] = '\n';
674 }
675 read += length + 1;
676 } 683 }
677 684
678 line = strip_whitespace(line); 685 if (line[nread - 1] == '\n') {
679 if (line[0] == '#') { 686 line[nread - 1] = '\0';
680 free(line);
681 continue;
682 } 687 }
683 if (strlen(line) == 0) { 688
684 free(line); 689 line_number += nlines;
690 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
691
692 strip_whitespace(line);
693 if (!*line || line[0] == '#') {
685 continue; 694 continue;
686 } 695 }
687 int brace_detected = detect_brace_on_following_line(file, line, 696 int brace_detected = 0;
688 line_number); 697 if (line[strlen(line) - 1] != '{' && line[strlen(line) - 1] != '}') {
689 if (brace_detected > 0) { 698 brace_detected = detect_brace(file);
690 line_number += brace_detected; 699 if (brace_detected > 0) {
691 wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number); 700 line_number += brace_detected;
701 wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number);
702 }
692 } 703 }
704 char *block = stack->length ? stack->items[0] : NULL;
693 char *expanded = expand_line(block, line, brace_detected > 0); 705 char *expanded = expand_line(block, line, brace_detected > 0);
694 if (!expanded) { 706 if (!expanded) {
695 list_foreach(stack, free); 707 success = false;
696 list_free(stack); 708 break;
697 free(line);
698 return false;
699 } 709 }
710 config->current_config_line_number = line_number;
711 config->current_config_line = line;
700 struct cmd_results *res; 712 struct cmd_results *res;
701 if (block && strcmp(block, "<commands>") == 0) { 713 if (block && strcmp(block, "<commands>") == 0) {
702 // Special case 714 // Special case
@@ -753,15 +765,40 @@ bool read_config(FILE *file, struct sway_config *config,
753 default:; 765 default:;
754 } 766 }
755 free(expanded); 767 free(expanded);
756 free(line);
757 free_cmd_results(res); 768 free_cmd_results(res);
758 } 769 }
759 list_foreach(stack, free); 770 free(line);
760 list_free(stack); 771 list_free_items_and_destroy(stack);
772 config->current_config_line_number = 0;
773 config->current_config_line = NULL;
761 774
762 return success; 775 return success;
763} 776}
764 777
778void config_add_swaynag_warning(char *fmt, ...) {
779 if (config->reading && !config->validating) {
780 va_list args;
781 va_start(args, fmt);
782 size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
783 va_end(args);
784
785 char *temp = malloc(length + 1);
786 if (!temp) {
787 wlr_log(WLR_ERROR, "Failed to allocate buffer for warning.");
788 return;
789 }
790
791 va_start(args, fmt);
792 vsnprintf(temp, length, fmt, args);
793 va_end(args);
794
795 swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
796 "Warning on line %i (%s) '%s': %s",
797 config->current_config_line_number, config->current_config_path,
798 config->current_config_line, temp);
799 }
800}
801
765char *do_var_replacement(char *str) { 802char *do_var_replacement(char *str) {
766 int i; 803 int i;
767 char *find = str; 804 char *find = str;
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 7bca5f49..701bf051 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,5 +1,4 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#define _XOPEN_SOURCE 700
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
5#include <stdlib.h> 4#include <stdlib.h>
@@ -13,6 +12,7 @@
13#include <signal.h> 12#include <signal.h>
14#include "sway/config.h" 13#include "sway/config.h"
15#include "sway/output.h" 14#include "sway/output.h"
15#include "config.h"
16#include "stringop.h" 16#include "stringop.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
@@ -50,13 +50,10 @@ void free_bar_config(struct bar_config *bar) {
50 free(bar->font); 50 free(bar->font);
51 free(bar->separator_symbol); 51 free(bar->separator_symbol);
52 for (int i = 0; i < bar->bindings->length; i++) { 52 for (int i = 0; i < bar->bindings->length; i++) {
53 struct bar_binding *binding = bar->bindings->items[i]; 53 free_bar_binding(bar->bindings->items[i]);
54 free_bar_binding(binding);
55 } 54 }
56 list_free(bar->bindings); 55 list_free(bar->bindings);
57 if (bar->outputs) { 56 list_free_items_and_destroy(bar->outputs);
58 free_flat_list(bar->outputs);
59 }
60 if (bar->pid != 0) { 57 if (bar->pid != 0) {
61 terminate_swaybar(bar->pid); 58 terminate_swaybar(bar->pid);
62 } 59 }
@@ -81,6 +78,10 @@ void free_bar_config(struct bar_config *bar) {
81 free(bar->colors.binding_mode_border); 78 free(bar->colors.binding_mode_border);
82 free(bar->colors.binding_mode_bg); 79 free(bar->colors.binding_mode_bg);
83 free(bar->colors.binding_mode_text); 80 free(bar->colors.binding_mode_text);
81#if HAVE_TRAY
82 list_free_items_and_destroy(bar->tray_outputs);
83 free(bar->icon_theme);
84#endif
84 free(bar); 85 free(bar);
85} 86}
86 87
@@ -95,15 +96,18 @@ struct bar_config *default_bar_config(void) {
95 bar->pango_markup = false; 96 bar->pango_markup = false;
96 bar->swaybar_command = NULL; 97 bar->swaybar_command = NULL;
97 bar->font = NULL; 98 bar->font = NULL;
98 bar->height = -1; 99 bar->height = 0;
99 bar->workspace_buttons = true; 100 bar->workspace_buttons = true;
100 bar->wrap_scroll = false; 101 bar->wrap_scroll = false;
101 bar->separator_symbol = NULL; 102 bar->separator_symbol = NULL;
102 bar->strip_workspace_numbers = false; 103 bar->strip_workspace_numbers = false;
104 bar->strip_workspace_name = false;
103 bar->binding_mode_indicator = true; 105 bar->binding_mode_indicator = true;
104 bar->verbose = false; 106 bar->verbose = false;
105 bar->pid = 0; 107 bar->pid = 0;
106 bar->modifier = get_modifier_mask_by_name("Mod4"); 108 bar->modifier = get_modifier_mask_by_name("Mod4");
109 bar->status_padding = 1;
110 bar->status_edge_padding = 3;
107 if (!(bar->mode = strdup("dock"))) { 111 if (!(bar->mode = strdup("dock"))) {
108 goto cleanup; 112 goto cleanup;
109 } 113 }
@@ -168,6 +172,10 @@ struct bar_config *default_bar_config(void) {
168 bar->colors.binding_mode_bg = NULL; 172 bar->colors.binding_mode_bg = NULL;
169 bar->colors.binding_mode_text = NULL; 173 bar->colors.binding_mode_text = NULL;
170 174
175#if HAVE_TRAY
176 bar->tray_padding = 2;
177#endif
178
171 list_add(config->bars, bar); 179 list_add(config->bars, bar);
172 return bar; 180 return bar;
173cleanup: 181cleanup:
diff --git a/sway/config/input.c b/sway/config/input.c
index d5d2d90b..d649d34d 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <limits.h> 3#include <limits.h>
4#include <float.h> 4#include <float.h>
diff --git a/sway/config/output.c b/sway/config/output.c
index 07543e3c..f24e7d66 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
@@ -179,10 +179,6 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
179 179
180 if (oc && oc->enabled == 0) { 180 if (oc && oc->enabled == 0) {
181 if (output->enabled) { 181 if (output->enabled) {
182 if (output->bg_pid != 0) {
183 terminate_swaybg(output->bg_pid);
184 output->bg_pid = 0;
185 }
186 output_disable(output); 182 output_disable(output);
187 wlr_output_layout_remove(root->output_layout, wlr_output); 183 wlr_output_layout_remove(root->output_layout, wlr_output);
188 } 184 }
@@ -276,18 +272,86 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
276 } 272 }
277} 273}
278 274
279static struct output_config *get_output_config(char *name, char *identifier) { 275static void default_output_config(struct output_config *oc,
276 struct wlr_output *wlr_output) {
277 oc->enabled = 1;
278 if (!wl_list_empty(&wlr_output->modes)) {
279 struct wlr_output_mode *mode =
280 wl_container_of(wlr_output->modes.prev, mode, link);
281 oc->width = mode->width;
282 oc->height = mode->height;
283 oc->refresh_rate = mode->refresh;
284 }
285 oc->x = oc->y = -1;
286 oc->scale = 1;
287 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
288}
289
290static struct output_config *get_output_config(char *identifier,
291 struct sway_output *sway_output) {
292 const char *name = sway_output->wlr_output->name;
293 struct output_config *oc_name = NULL;
280 int i = list_seq_find(config->output_configs, output_name_cmp, name); 294 int i = list_seq_find(config->output_configs, output_name_cmp, name);
281 if (i >= 0) { 295 if (i >= 0) {
282 return config->output_configs->items[i]; 296 oc_name = config->output_configs->items[i];
283 } 297 }
284 298
299 struct output_config *oc_id = NULL;
285 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 300 i = list_seq_find(config->output_configs, output_name_cmp, identifier);
286 if (i >= 0) { 301 if (i >= 0) {
287 return config->output_configs->items[i]; 302 oc_id = config->output_configs->items[i];
303 }
304
305 struct output_config *result = result = new_output_config("temp");
306 if (config->reloading) {
307 default_output_config(result, sway_output->wlr_output);
308 }
309 if (oc_name && oc_id) {
310 // Generate a config named `<identifier> on <name>` which contains a
311 // merged copy of the identifier on name. This will make sure that both
312 // identifier and name configs are respected, with identifier getting
313 // priority
314 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1;
315 char *temp = malloc(length);
316 snprintf(temp, length, "%s on %s", identifier, name);
317
318 free(result->name);
319 result->name = temp;
320 merge_output_config(result, oc_name);
321 merge_output_config(result, oc_id);
322
323 wlr_log(WLR_DEBUG, "Generated output config \"%s\" (enabled: %d)"
324 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
325 " (dpms %d)", result->name, result->enabled, result->width,
326 result->height, result->refresh_rate, result->x, result->y,
327 result->scale, result->transform, result->background,
328 result->background_option, result->dpms_state);
329 } else if (oc_name) {
330 // No identifier config, just return a copy of the name config
331 free(result->name);
332 result->name = strdup(name);
333 merge_output_config(result, oc_name);
334 } else if (oc_id) {
335 // No name config, just return a copy of the identifier config
336 free(result->name);
337 result->name = strdup(identifier);
338 merge_output_config(result, oc_id);
339 } else if (config->reloading) {
340 // Neither config exists, but we need to reset the output so create a
341 // default config for the output and if a wildcard config exists, merge
342 // that on top
343 free(result->name);
344 result->name = strdup("*");
345 i = list_seq_find(config->output_configs, output_name_cmp, "*");
346 if (i >= 0) {
347 merge_output_config(result, config->output_configs->items[i]);
348 }
349 } else {
350 free_output_config(result);
351 result = NULL;
288 } 352 }
289 353
290 return NULL; 354 return result;
291} 355}
292 356
293void apply_output_config_to_outputs(struct output_config *oc) { 357void apply_output_config_to_outputs(struct output_config *oc) {
@@ -301,14 +365,17 @@ void apply_output_config_to_outputs(struct output_config *oc) {
301 char *name = sway_output->wlr_output->name; 365 char *name = sway_output->wlr_output->name;
302 output_get_identifier(id, sizeof(id), sway_output); 366 output_get_identifier(id, sizeof(id), sway_output);
303 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { 367 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
304 struct output_config *current = oc; 368 struct output_config *current = new_output_config(oc->name);
369 merge_output_config(current, oc);
305 if (wildcard) { 370 if (wildcard) {
306 struct output_config *tmp = get_output_config(name, id); 371 struct output_config *tmp = get_output_config(id, sway_output);
307 if (tmp) { 372 if (tmp) {
373 free_output_config(current);
308 current = tmp; 374 current = tmp;
309 } 375 }
310 } 376 }
311 apply_output_config(current, sway_output); 377 apply_output_config(current, sway_output);
378 free_output_config(current);
312 379
313 if (!wildcard) { 380 if (!wildcard) {
314 // Stop looking if the output config isn't applicable to all 381 // Stop looking if the output config isn't applicable to all
@@ -329,28 +396,3 @@ void free_output_config(struct output_config *oc) {
329 free(oc->background_fallback); 396 free(oc->background_fallback);
330 free(oc); 397 free(oc);
331} 398}
332
333static void default_output_config(struct output_config *oc,
334 struct wlr_output *wlr_output) {
335 oc->enabled = 1;
336 if (!wl_list_empty(&wlr_output->modes)) {
337 struct wlr_output_mode *mode =
338 wl_container_of(wlr_output->modes.prev, mode, link);
339 oc->width = mode->width;
340 oc->height = mode->height;
341 oc->refresh_rate = mode->refresh;
342 }
343 oc->x = oc->y = -1;
344 oc->scale = 1;
345 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
346}
347
348void create_default_output_configs(void) {
349 struct sway_output *sway_output;
350 wl_list_for_each(sway_output, &root->all_outputs, link) {
351 char *name = sway_output->wlr_output->name;
352 struct output_config *oc = new_output_config(name);
353 default_output_config(oc, sway_output->wlr_output);
354 list_add(config->output_configs, oc);
355 }
356}
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 46456caf..d7316c68 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include "sway/config.h" 4#include "sway/config.h"
@@ -11,7 +11,6 @@ struct seat_config *new_seat_config(const char* name) {
11 return NULL; 11 return NULL;
12 } 12 }
13 13
14 wlr_log(WLR_DEBUG, "new_seat_config(%s)", name);
15 seat->name = strdup(name); 14 seat->name = strdup(name);
16 if (!sway_assert(seat->name, "could not allocate name for seat")) { 15 if (!sway_assert(seat->name, "could not allocate name for seat")) {
17 free(seat); 16 free(seat);
@@ -26,10 +25,57 @@ struct seat_config *new_seat_config(const char* name) {
26 free(seat); 25 free(seat);
27 return NULL; 26 return NULL;
28 } 27 }
28 seat->hide_cursor_timeout = -1;
29 29
30 return seat; 30 return seat;
31} 31}
32 32
33static void merge_wildcard_on_all(struct seat_config *wildcard) {
34 for (int i = 0; i < config->seat_configs->length; i++) {
35 struct seat_config *sc = config->seat_configs->items[i];
36 if (strcmp(wildcard->name, sc->name) != 0) {
37 wlr_log(WLR_DEBUG, "Merging seat * config on %s", sc->name);
38 merge_seat_config(sc, wildcard);
39 }
40 }
41}
42
43struct seat_config *store_seat_config(struct seat_config *sc) {
44 bool wildcard = strcmp(sc->name, "*") == 0;
45 if (wildcard) {
46 merge_wildcard_on_all(sc);
47 }
48
49 int i = list_seq_find(config->seat_configs, seat_name_cmp, sc->name);
50 if (i >= 0) {
51 wlr_log(WLR_DEBUG, "Merging on top of existing seat config");
52 struct seat_config *current = config->seat_configs->items[i];
53 merge_seat_config(current, sc);
54 free_seat_config(sc);
55 sc = current;
56 } else if (!wildcard) {
57 wlr_log(WLR_DEBUG, "Adding non-wildcard seat config");
58 i = list_seq_find(config->seat_configs, seat_name_cmp, "*");
59 if (i >= 0) {
60 wlr_log(WLR_DEBUG, "Merging on top of seat * config");
61 struct seat_config *current = new_seat_config(sc->name);
62 merge_seat_config(current, config->seat_configs->items[i]);
63 merge_seat_config(current, sc);
64 free_seat_config(sc);
65 sc = current;
66 }
67 list_add(config->seat_configs, sc);
68 } else {
69 // New wildcard config. Just add it
70 wlr_log(WLR_DEBUG, "Adding seat * config");
71 list_add(config->seat_configs, sc);
72 }
73
74 wlr_log(WLR_DEBUG, "Config stored for seat %s", sc->name);
75
76 return sc;
77}
78
33struct seat_attachment_config *seat_attachment_config_new(void) { 79struct seat_attachment_config *seat_attachment_config_new(void) {
34 struct seat_attachment_config *attachment = 80 struct seat_attachment_config *attachment =
35 calloc(1, sizeof(struct seat_attachment_config)); 81 calloc(1, sizeof(struct seat_attachment_config));
@@ -65,11 +111,6 @@ static void merge_seat_attachment_config(struct seat_attachment_config *dest,
65} 111}
66 112
67void merge_seat_config(struct seat_config *dest, struct seat_config *source) { 113void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
68 if (source->name) {
69 free(dest->name);
70 dest->name = strdup(source->name);
71 }
72
73 if (source->fallback != -1) { 114 if (source->fallback != -1) {
74 dest->fallback = source->fallback; 115 dest->fallback = source->fallback;
75 } 116 }
@@ -97,6 +138,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
97 } 138 }
98 } 139 }
99 } 140 }
141
142 if (source->hide_cursor_timeout != -1) {
143 dest->hide_cursor_timeout = source->hide_cursor_timeout;
144 }
100} 145}
101 146
102struct seat_config *copy_seat_config(struct seat_config *seat) { 147struct seat_config *copy_seat_config(struct seat_config *seat) {
@@ -117,11 +162,8 @@ void free_seat_config(struct seat_config *seat) {
117 162
118 free(seat->name); 163 free(seat->name);
119 for (int i = 0; i < seat->attachments->length; ++i) { 164 for (int i = 0; i < seat->attachments->length; ++i) {
120 struct seat_attachment_config *attachment = 165 seat_attachment_config_free(seat->attachments->items[i]);
121 seat->attachments->items[i];
122 seat_attachment_config_free(attachment);
123 } 166 }
124
125 list_free(seat->attachments); 167 list_free(seat->attachments);
126 free(seat); 168 free(seat);
127} 169}
diff --git a/sway/criteria.c b/sway/criteria.c
index 3393852c..54583b04 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <stdio.h> 3#include <stdio.h>
4#include <stdbool.h> 4#include <stdbool.h>
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index d649100f..04c9b4f6 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -29,23 +29,13 @@
29#include "sway/tree/view.h" 29#include "sway/tree/view.h"
30#include "sway/tree/workspace.h" 30#include "sway/tree/workspace.h"
31 31
32struct sway_output *output_by_name(const char *name) { 32struct sway_output *output_by_name_or_id(const char *name_or_id) {
33 for (int i = 0; i < root->outputs->length; ++i) { 33 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 34 struct sway_output *output = root->outputs->items[i];
35 if (strcasecmp(output->wlr_output->name, name) == 0) { 35 char identifier[128];
36 return output; 36 output_get_identifier(identifier, sizeof(identifier), output);
37 } 37 if (strcasecmp(identifier, name_or_id) == 0
38 } 38 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
39 return NULL;
40}
41
42struct sway_output *output_by_identifier(const char *identifier) {
43 for (int i = 0; i < root->outputs->length; ++i) {
44 struct sway_output *output = root->outputs->items[i];
45 char output_identifier[128];
46 snprintf(output_identifier, sizeof(output_identifier), "%s %s %s", output->wlr_output->make,
47 output->wlr_output->model, output->wlr_output->serial);
48 if (strcasecmp(output_identifier, identifier) == 0) {
49 return output; 39 return output;
50 } 40 }
51 } 41 }
@@ -110,7 +100,7 @@ static bool get_surface_box(struct surface_iterator_data *data,
110 } 100 }
111 101
112 struct wlr_box rotated_box; 102 struct wlr_box rotated_box;
113 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); 103 wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
114 104
115 struct wlr_box output_box = { 105 struct wlr_box output_box = {
116 .width = output->width, 106 .width = output->width,
@@ -118,7 +108,7 @@ static bool get_surface_box(struct surface_iterator_data *data,
118 }; 108 };
119 109
120 struct wlr_box intersection; 110 struct wlr_box intersection;
121 return wlr_box_intersection(&output_box, &rotated_box, &intersection); 111 return wlr_box_intersection(&intersection, &output_box, &rotated_box);
122} 112}
123 113
124static void output_for_each_surface_iterator(struct wlr_surface *surface, 114static void output_for_each_surface_iterator(struct wlr_surface *surface,
@@ -310,7 +300,7 @@ static int scale_length(int length, int offset, float scale) {
310 return round((offset + length) * scale) - round(offset * scale); 300 return round((offset + length) * scale) - round(offset * scale);
311} 301}
312 302
313static void scale_box(struct wlr_box *box, float scale) { 303void scale_box(struct wlr_box *box, float scale) {
314 box->width = scale_length(box->width, box->x, scale); 304 box->width = scale_length(box->width, box->x, scale);
315 box->height = scale_length(box->height, box->y, scale); 305 box->height = scale_length(box->height, box->y, scale);
316 box->x = round(box->x * scale); 306 box->x = round(box->x * scale);
@@ -433,7 +423,7 @@ static void damage_surface_iterator(struct sway_output *output,
433 } 423 }
434 424
435 if (whole) { 425 if (whole) {
436 wlr_box_rotated_bounds(&box, rotation, &box); 426 wlr_box_rotated_bounds(&box, &box, rotation);
437 wlr_output_damage_add_box(output->damage, &box); 427 wlr_output_damage_add_box(output->damage, &box);
438 } 428 }
439 429
@@ -568,6 +558,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
568 558
569 if (!oc || oc->enabled) { 559 if (!oc || oc->enabled) {
570 output_enable(output, oc); 560 output_enable(output, oc);
561 } else {
562 wlr_output_enable(output->wlr_output, false);
571 } 563 }
572 564
573 transaction_commit_dirty(); 565 transaction_commit_dirty();
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 8d4a701b..a38c6a07 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -49,13 +49,6 @@ static int scale_length(int length, int offset, float scale) {
49 return round((offset + length) * scale) - round(offset * scale); 49 return round((offset + length) * scale) - round(offset * scale);
50} 50}
51 51
52static void scale_box(struct wlr_box *box, float scale) {
53 box->width = scale_length(box->width, box->x, scale);
54 box->height = scale_length(box->height, box->y, scale);
55 box->x = round(box->x * scale);
56 box->y = round(box->y * scale);
57}
58
59static void scissor_output(struct wlr_output *wlr_output, 52static void scissor_output(struct wlr_output *wlr_output,
60 pixman_box32_t *rect) { 53 pixman_box32_t *rect) {
61 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); 54 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
@@ -73,7 +66,7 @@ static void scissor_output(struct wlr_output *wlr_output,
73 66
74 enum wl_output_transform transform = 67 enum wl_output_transform transform =
75 wlr_output_transform_invert(wlr_output->transform); 68 wlr_output_transform_invert(wlr_output->transform);
76 wlr_box_transform(&box, transform, ow, oh, &box); 69 wlr_box_transform(&box, &box, transform, ow, oh);
77 70
78 wlr_renderer_scissor(renderer, &box); 71 wlr_renderer_scissor(renderer, &box);
79} 72}
@@ -164,7 +157,7 @@ static void render_drag_icons(struct sway_output *output,
164 157
165// _box.x and .y are expected to be layout-local 158// _box.x and .y are expected to be layout-local
166// _box.width and .height are expected to be output-buffer-local 159// _box.width and .height are expected to be output-buffer-local
167static void render_rect(struct wlr_output *wlr_output, 160void render_rect(struct wlr_output *wlr_output,
168 pixman_region32_t *output_damage, const struct wlr_box *_box, 161 pixman_region32_t *output_damage, const struct wlr_box *_box,
169 float color[static 4]) { 162 float color[static 4]) {
170 struct wlr_renderer *renderer = 163 struct wlr_renderer *renderer =
@@ -197,7 +190,7 @@ damage_finish:
197 pixman_region32_fini(&damage); 190 pixman_region32_fini(&damage);
198} 191}
199 192
200static void premultiply_alpha(float color[4], float opacity) { 193void premultiply_alpha(float color[4], float opacity) {
201 color[3] *= opacity; 194 color[3] *= opacity;
202 color[0] *= color[3]; 195 color[0] *= color[3];
203 color[1] *= color[3]; 196 color[1] *= color[3];
@@ -261,7 +254,7 @@ static void render_saved_view(struct sway_view *view,
261 }; 254 };
262 255
263 struct wlr_box intersection; 256 struct wlr_box intersection;
264 bool intersects = wlr_box_intersection(&output_box, &box, &intersection); 257 bool intersects = wlr_box_intersection(&intersection, &output_box, &box);
265 if (!intersects) { 258 if (!intersects) {
266 return; 259 return;
267 } 260 }
@@ -368,6 +361,10 @@ static void render_titlebar(struct sway_output *output,
368 children->items[children->length - 1] == con; 361 children->items[children->length - 1] == con;
369 double output_x = output->wlr_output->lx; 362 double output_x = output->wlr_output->lx;
370 double output_y = output->wlr_output->ly; 363 double output_y = output->wlr_output->ly;
364 int titlebar_border_thickness = config->titlebar_border_thickness;
365 int titlebar_h_padding = config->titlebar_h_padding;
366 int titlebar_v_padding = config->titlebar_v_padding;
367 enum alignment title_align = config->title_align;
371 368
372 // Single pixel bar above title 369 // Single pixel bar above title
373 memcpy(&color, colors->border, sizeof(float) * 4); 370 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -375,7 +372,7 @@ static void render_titlebar(struct sway_output *output,
375 box.x = x; 372 box.x = x;
376 box.y = y; 373 box.y = y;
377 box.width = width; 374 box.width = width;
378 box.height = TITLEBAR_BORDER_THICKNESS; 375 box.height = titlebar_border_thickness;
379 scale_box(&box, output_scale); 376 scale_box(&box, output_scale);
380 render_rect(output->wlr_output, output_damage, &box, color); 377 render_rect(output->wlr_output, output_damage, &box, color);
381 378
@@ -391,45 +388,51 @@ static void render_titlebar(struct sway_output *output,
391 } 388 }
392 } 389 }
393 box.x = x + left_offset; 390 box.x = x + left_offset;
394 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 391 box.y = y + container_titlebar_height() - titlebar_border_thickness;
395 box.width = width - left_offset - right_offset; 392 box.width = width - left_offset - right_offset;
396 box.height = TITLEBAR_BORDER_THICKNESS; 393 box.height = titlebar_border_thickness;
397 scale_box(&box, output_scale); 394 scale_box(&box, output_scale);
398 render_rect(output->wlr_output, output_damage, &box, color); 395 render_rect(output->wlr_output, output_damage, &box, color);
399 396
400 if (layout == L_TABBED) { 397 if (layout == L_TABBED) {
401 // Single pixel left edge 398 // Single pixel left edge
402 box.x = x; 399 box.x = x;
403 box.y = y + TITLEBAR_BORDER_THICKNESS; 400 box.y = y + titlebar_border_thickness;
404 box.width = TITLEBAR_BORDER_THICKNESS; 401 box.width = titlebar_border_thickness;
405 box.height = 402 box.height =
406 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; 403 container_titlebar_height() - titlebar_border_thickness * 2;
407 scale_box(&box, output_scale); 404 scale_box(&box, output_scale);
408 render_rect(output->wlr_output, output_damage, &box, color); 405 render_rect(output->wlr_output, output_damage, &box, color);
409 406
410 // Single pixel right edge 407 // Single pixel right edge
411 box.x = x + width - TITLEBAR_BORDER_THICKNESS; 408 box.x = x + width - titlebar_border_thickness;
412 box.y = y + TITLEBAR_BORDER_THICKNESS; 409 box.y = y + titlebar_border_thickness;
413 box.width = TITLEBAR_BORDER_THICKNESS; 410 box.width = titlebar_border_thickness;
414 box.height = 411 box.height =
415 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; 412 container_titlebar_height() - titlebar_border_thickness * 2;
416 scale_box(&box, output_scale); 413 scale_box(&box, output_scale);
417 render_rect(output->wlr_output, output_damage, &box, color); 414 render_rect(output->wlr_output, output_damage, &box, color);
418 } 415 }
419 416
420 size_t inner_width = width - TITLEBAR_H_PADDING * 2; 417 int inner_x = x - output_x + titlebar_h_padding;
421 int bg_y = y + TITLEBAR_BORDER_THICKNESS; 418 int bg_y = y + titlebar_border_thickness;
419 size_t inner_width = width - titlebar_h_padding * 2;
420
421 // output-buffer local
422 int ob_inner_x = round(inner_x * output_scale);
423 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
422 int ob_bg_height = scale_length( 424 int ob_bg_height = scale_length(
423 (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + 425 (titlebar_v_padding - titlebar_border_thickness) * 2 +
424 config->font_height, bg_y, output_scale); 426 config->font_height, bg_y, output_scale);
425 427
426 // Marks 428 // Marks
427 int marks_ob_width = 0; // output-buffer-local 429 int ob_marks_x = 0; // output-buffer-local
430 int ob_marks_width = 0; // output-buffer-local
428 if (config->show_marks && marks_texture) { 431 if (config->show_marks && marks_texture) {
429 struct wlr_box texture_box; 432 struct wlr_box texture_box;
430 wlr_texture_get_size(marks_texture, 433 wlr_texture_get_size(marks_texture,
431 &texture_box.width, &texture_box.height); 434 &texture_box.width, &texture_box.height);
432 marks_ob_width = texture_box.width; 435 ob_marks_width = texture_box.width;
433 436
434 // The marks texture might be shorter than the config->font_height, in 437 // The marks texture might be shorter than the config->font_height, in
435 // which case we need to pad it as evenly as possible above and below. 438 // which case we need to pad it as evenly as possible above and below.
@@ -437,9 +440,15 @@ static void render_titlebar(struct sway_output *output,
437 int ob_padding_above = floor(ob_padding_total / 2.0); 440 int ob_padding_above = floor(ob_padding_total / 2.0);
438 int ob_padding_below = ceil(ob_padding_total / 2.0); 441 int ob_padding_below = ceil(ob_padding_total / 2.0);
439 442
440 // Render texture 443 // Render texture. If the title is on the right, the marks will be on
441 texture_box.x = round((x - output_x + width - TITLEBAR_H_PADDING) 444 // the left. Otherwise, they will be on the right.
442 * output_scale) - texture_box.width; 445 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
446 texture_box.x = ob_inner_x;
447 } else {
448 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
449 }
450 ob_marks_x = texture_box.x;
451
443 texture_box.y = round((bg_y - output_y) * output_scale) + 452 texture_box.y = round((bg_y - output_y) * output_scale) +
444 ob_padding_above; 453 ob_padding_above;
445 454
@@ -448,8 +457,8 @@ static void render_titlebar(struct sway_output *output,
448 WL_OUTPUT_TRANSFORM_NORMAL, 457 WL_OUTPUT_TRANSFORM_NORMAL,
449 0.0, output->wlr_output->transform_matrix); 458 0.0, output->wlr_output->transform_matrix);
450 459
451 if (inner_width * output_scale < texture_box.width) { 460 if (ob_inner_width < texture_box.width) {
452 texture_box.width = inner_width * output_scale; 461 texture_box.width = ob_inner_width;
453 } 462 }
454 render_texture(output->wlr_output, output_damage, marks_texture, 463 render_texture(output->wlr_output, output_damage, marks_texture,
455 &texture_box, matrix, con->alpha); 464 &texture_box, matrix, con->alpha);
@@ -458,7 +467,7 @@ static void render_titlebar(struct sway_output *output,
458 memcpy(&color, colors->background, sizeof(float) * 4); 467 memcpy(&color, colors->background, sizeof(float) * 4);
459 premultiply_alpha(color, con->alpha); 468 premultiply_alpha(color, con->alpha);
460 box.x = texture_box.x + round(output_x * output_scale); 469 box.x = texture_box.x + round(output_x * output_scale);
461 box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale); 470 box.y = round((y + titlebar_border_thickness) * output_scale);
462 box.width = texture_box.width; 471 box.width = texture_box.width;
463 box.height = ob_padding_above; 472 box.height = ob_padding_above;
464 render_rect(output->wlr_output, output_damage, &box, color); 473 render_rect(output->wlr_output, output_damage, &box, color);
@@ -470,24 +479,43 @@ static void render_titlebar(struct sway_output *output,
470 } 479 }
471 480
472 // Title text 481 // Title text
473 size_t title_ob_width = 0; // output-buffer-local 482 int ob_title_x = 0; // output-buffer-local
483 int ob_title_width = 0; // output-buffer-local
474 if (title_texture) { 484 if (title_texture) {
475 struct wlr_box texture_box; 485 struct wlr_box texture_box;
476 wlr_texture_get_size(title_texture, 486 wlr_texture_get_size(title_texture,
477 &texture_box.width, &texture_box.height); 487 &texture_box.width, &texture_box.height);
478 title_ob_width = texture_box.width; 488 ob_title_width = texture_box.width;
479 489
480 // The title texture might be shorter than the config->font_height, 490 // The title texture might be shorter than the config->font_height,
481 // in which case we need to pad it above and below. 491 // in which case we need to pad it above and below.
482 int ob_padding_above = round((config->font_baseline - 492 int ob_padding_above = round((config->font_baseline -
483 con->title_baseline + TITLEBAR_V_PADDING - 493 con->title_baseline + titlebar_v_padding -
484 TITLEBAR_BORDER_THICKNESS) * output_scale); 494 titlebar_border_thickness) * output_scale);
485 int ob_padding_below = ob_bg_height - ob_padding_above - 495 int ob_padding_below = ob_bg_height - ob_padding_above -
486 texture_box.height; 496 texture_box.height;
487 497
488 // Render texture 498 // Render texture
489 texture_box.x = 499 if (texture_box.width > ob_inner_width - ob_marks_width) {
490 round((x - output_x + TITLEBAR_H_PADDING) * output_scale); 500 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
501 ? ob_marks_x + ob_marks_width : ob_inner_x;
502 } else if (title_align == ALIGN_LEFT) {
503 texture_box.x = ob_inner_x;
504 } else if (title_align == ALIGN_CENTER) {
505 // If there are marks visible, center between the edge and marks.
506 // Otherwise, center in the inner area.
507 if (ob_marks_width) {
508 texture_box.x = (ob_inner_x + ob_marks_x) / 2
509 - texture_box.width / 2;
510 } else {
511 texture_box.x = ob_inner_x + ob_inner_width / 2
512 - texture_box.width / 2;
513 }
514 } else {
515 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
516 }
517 ob_title_x = texture_box.x;
518
491 texture_box.y = 519 texture_box.y =
492 round((bg_y - output_y) * output_scale) + ob_padding_above; 520 round((bg_y - output_y) * output_scale) + ob_padding_above;
493 521
@@ -496,11 +524,10 @@ static void render_titlebar(struct sway_output *output,
496 WL_OUTPUT_TRANSFORM_NORMAL, 524 WL_OUTPUT_TRANSFORM_NORMAL,
497 0.0, output->wlr_output->transform_matrix); 525 0.0, output->wlr_output->transform_matrix);
498 526
499 int inner_x = x - output_x + TITLEBAR_H_PADDING; 527 if (ob_inner_width - ob_marks_width < texture_box.width) {
500 int ob_inner_width = scale_length(inner_width, inner_x, output_scale); 528 texture_box.width = ob_inner_width - ob_marks_width;
501 if (ob_inner_width - marks_ob_width < texture_box.width) {
502 texture_box.width = ob_inner_width - marks_ob_width;
503 } 529 }
530
504 render_texture(output->wlr_output, output_damage, title_texture, 531 render_texture(output->wlr_output, output_damage, title_texture,
505 &texture_box, matrix, con->alpha); 532 &texture_box, matrix, con->alpha);
506 533
@@ -508,7 +535,7 @@ static void render_titlebar(struct sway_output *output,
508 memcpy(&color, colors->background, sizeof(float) * 4); 535 memcpy(&color, colors->background, sizeof(float) * 4);
509 premultiply_alpha(color, con->alpha); 536 premultiply_alpha(color, con->alpha);
510 box.x = texture_box.x + round(output_x * output_scale); 537 box.x = texture_box.x + round(output_x * output_scale);
511 box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale); 538 box.y = round((y + titlebar_border_thickness) * output_scale);
512 box.width = texture_box.width; 539 box.width = texture_box.width;
513 box.height = ob_padding_above; 540 box.height = ob_padding_above;
514 render_rect(output->wlr_output, output_damage, &box, color); 541 render_rect(output->wlr_output, output_damage, &box, color);
@@ -519,50 +546,83 @@ static void render_titlebar(struct sway_output *output,
519 render_rect(output->wlr_output, output_damage, &box, color); 546 render_rect(output->wlr_output, output_damage, &box, color);
520 } 547 }
521 548
549 // Determine the left + right extends of the textures (output-buffer local)
550 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
551 if (ob_title_width == 0 && ob_marks_width == 0) {
552 ob_left_x = ob_inner_x;
553 ob_left_width = 0;
554 ob_right_x = ob_inner_x;
555 ob_right_width = 0;
556 } else if (ob_title_x < ob_marks_x) {
557 ob_left_x = ob_title_x;
558 ob_left_width = ob_title_width;
559 ob_right_x = ob_marks_x;
560 ob_right_width = ob_marks_width;
561 } else {
562 ob_left_x = ob_marks_x;
563 ob_left_width = ob_marks_width;
564 ob_right_x = ob_title_x;
565 ob_right_width = ob_title_width;
566 }
567 if (ob_left_x < ob_inner_x) {
568 ob_left_x = ob_inner_x;
569 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
570 ob_right_x = ob_left_x;
571 ob_right_width = ob_left_width;
572 }
573
522 // Filler between title and marks 574 // Filler between title and marks
523 box.width = 575 box.width = ob_right_x - ob_left_x - ob_left_width;
524 round(inner_width * output_scale) - title_ob_width - marks_ob_width;
525 if (box.width > 0) { 576 if (box.width > 0) {
526 box.x = round((x + TITLEBAR_H_PADDING) * output_scale) + title_ob_width; 577 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
527 box.y = round(bg_y * output_scale); 578 box.y = round(bg_y * output_scale);
528 box.height = ob_bg_height; 579 box.height = ob_bg_height;
529 render_rect(output->wlr_output, output_damage, &box, color); 580 render_rect(output->wlr_output, output_damage, &box, color);
530 } 581 }
531 582
532 // Padding left of title 583 // Padding on left side
533 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; 584 left_offset = (layout == L_TABBED) * titlebar_border_thickness;
534 box.x = x + left_offset; 585 box.x = x + left_offset;
535 box.y = y + TITLEBAR_BORDER_THICKNESS; 586 box.y = y + titlebar_border_thickness;
536 box.width = TITLEBAR_H_PADDING - left_offset; 587 box.width = titlebar_h_padding - left_offset;
537 box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + 588 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
538 config->font_height; 589 config->font_height;
539 scale_box(&box, output_scale); 590 scale_box(&box, output_scale);
591 int left_x = ob_left_x + round(output_x * output_scale);
592 if (box.x + box.width < left_x) {
593 box.width += left_x - box.x - box.width;
594 }
540 render_rect(output->wlr_output, output_damage, &box, color); 595 render_rect(output->wlr_output, output_damage, &box, color);
541 596
542 // Padding right of marks 597 // Padding on right side
543 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; 598 right_offset = (layout == L_TABBED) * titlebar_border_thickness;
544 box.x = x + width - TITLEBAR_H_PADDING; 599 box.x = x + width - titlebar_h_padding;
545 box.y = y + TITLEBAR_BORDER_THICKNESS; 600 box.y = y + titlebar_border_thickness;
546 box.width = TITLEBAR_H_PADDING - right_offset; 601 box.width = titlebar_h_padding - right_offset;
547 box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + 602 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
548 config->font_height; 603 config->font_height;
549 scale_box(&box, output_scale); 604 scale_box(&box, output_scale);
605 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
606 if (right_rx < box.x) {
607 box.width += box.x - right_rx;
608 box.x = right_rx;
609 }
550 render_rect(output->wlr_output, output_damage, &box, color); 610 render_rect(output->wlr_output, output_damage, &box, color);
551 611
552 if (connects_sides) { 612 if (connects_sides) {
553 // Left pixel in line with bottom bar 613 // Left pixel in line with bottom bar
554 box.x = x; 614 box.x = x;
555 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 615 box.y = y + container_titlebar_height() - titlebar_border_thickness;
556 box.width = state->border_thickness * state->border_left; 616 box.width = state->border_thickness * state->border_left;
557 box.height = TITLEBAR_BORDER_THICKNESS; 617 box.height = titlebar_border_thickness;
558 scale_box(&box, output_scale); 618 scale_box(&box, output_scale);
559 render_rect(output->wlr_output, output_damage, &box, color); 619 render_rect(output->wlr_output, output_damage, &box, color);
560 620
561 // Right pixel in line with bottom bar 621 // Right pixel in line with bottom bar
562 box.x = x + width - state->border_thickness * state->border_right; 622 box.x = x + width - state->border_thickness * state->border_right;
563 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 623 box.y = y + container_titlebar_height() - titlebar_border_thickness;
564 box.width = state->border_thickness * state->border_right; 624 box.width = state->border_thickness * state->border_right;
565 box.height = TITLEBAR_BORDER_THICKNESS; 625 box.height = titlebar_border_thickness;
566 scale_box(&box, output_scale); 626 scale_box(&box, output_scale);
567 render_rect(output->wlr_output, output_damage, &box, color); 627 render_rect(output->wlr_output, output_damage, &box, color);
568 } 628 }
@@ -882,21 +942,11 @@ static void render_floating(struct sway_output *soutput,
882 } 942 }
883} 943}
884 944
885static void render_dropzones(struct sway_output *output, 945static void render_seatops(struct sway_output *output,
886 pixman_region32_t *damage) { 946 pixman_region32_t *damage) {
887 struct sway_seat *seat; 947 struct sway_seat *seat;
888 wl_list_for_each(seat, &server.input->seats, link) { 948 wl_list_for_each(seat, &server.input->seats, link) {
889 if (seat->operation == OP_MOVE_TILING && seat->op_target_node 949 seatop_render(seat, output, damage);
890 && node_get_output(seat->op_target_node) == output) {
891 float color[4];
892 memcpy(&color, config->border_colors.focused.indicator,
893 sizeof(float) * 4);
894 premultiply_alpha(color, 0.5);
895 struct wlr_box box;
896 memcpy(&box, &seat->op_drop_box, sizeof(struct wlr_box));
897 scale_box(&box, output->wlr_output->scale);
898 render_rect(output->wlr_output, damage, &box, color);
899 }
900 } 950 }
901} 951}
902 952
@@ -950,7 +1000,7 @@ void output_render(struct sway_output *output, struct timespec *when,
950 if (fullscreen_con->view) { 1000 if (fullscreen_con->view) {
951 if (fullscreen_con->view->saved_buffer) { 1001 if (fullscreen_con->view->saved_buffer) {
952 render_saved_view(fullscreen_con->view, output, damage, 1.0f); 1002 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
953 } else { 1003 } else if (fullscreen_con->view->surface) {
954 render_view_toplevels(fullscreen_con->view, 1004 render_view_toplevels(fullscreen_con->view,
955 output, damage, 1.0f); 1005 output, damage, 1.0f);
956 } 1006 }
@@ -993,7 +1043,7 @@ void output_render(struct sway_output *output, struct timespec *when,
993 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 1043 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
994 } 1044 }
995 1045
996 render_dropzones(output, damage); 1046 render_seatops(output, damage);
997 1047
998 struct sway_seat *seat = input_manager_current_seat(); 1048 struct sway_seat *seat = input_manager_current_seat();
999 struct sway_container *focus = seat_get_focused_container(seat); 1049 struct sway_container *focus = seat_get_focused_container(seat);
@@ -1012,15 +1062,22 @@ renderer_end:
1012 wlr_render_texture(renderer, root->debug_tree, 1062 wlr_render_texture(renderer, root->debug_tree,
1013 wlr_output->transform_matrix, 0, 40, 1); 1063 wlr_output->transform_matrix, 0, 40, 1);
1014 } 1064 }
1015 if (debug.damage == DAMAGE_HIGHLIGHT) {
1016 int width, height;
1017 wlr_output_transformed_resolution(wlr_output, &width, &height);
1018 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1019 }
1020 1065
1021 wlr_renderer_scissor(renderer, NULL); 1066 wlr_renderer_scissor(renderer, NULL);
1022 wlr_output_render_software_cursors(wlr_output, damage); 1067 wlr_output_render_software_cursors(wlr_output, damage);
1023 wlr_renderer_end(renderer); 1068 wlr_renderer_end(renderer);
1069
1070 int width, height;
1071 wlr_output_transformed_resolution(wlr_output, &width, &height);
1072
1073 if (debug.damage == DAMAGE_HIGHLIGHT) {
1074 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1075 }
1076
1077 enum wl_output_transform transform =
1078 wlr_output_transform_invert(wlr_output->transform);
1079 wlr_region_transform(damage, damage, transform, width, height);
1080
1024 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { 1081 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
1025 return; 1082 return;
1026 } 1083 }
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index bf0038b4..1cdd7c6d 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -299,7 +299,7 @@ static void transaction_apply(struct sway_transaction *transaction) {
299 if (root->outputs->length) { 299 if (root->outputs->length) {
300 struct sway_seat *seat; 300 struct sway_seat *seat;
301 wl_list_for_each(seat, &server.input->seats, link) { 301 wl_list_for_each(seat, &server.input->seats, link) {
302 if (seat->operation == OP_NONE) { 302 if (!seat_doing_seatop(seat)) {
303 cursor_rebase(seat->cursor); 303 cursor_rebase(seat->cursor);
304 } 304 }
305 } 305 }
@@ -363,7 +363,7 @@ static void transaction_progress_queue(void) {
363 363
364static int handle_timeout(void *data) { 364static int handle_timeout(void *data) {
365 struct sway_transaction *transaction = data; 365 struct sway_transaction *transaction = data;
366 wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)", 366 wlr_log(WLR_DEBUG, "Transaction %p timed out (%zi waiting)",
367 transaction, transaction->num_waiting); 367 transaction, transaction->num_waiting);
368 transaction->num_waiting = 0; 368 transaction->num_waiting = 0;
369 transaction_progress_queue(); 369 transaction_progress_queue();
@@ -472,7 +472,7 @@ static void set_instruction_ready(
472 struct timespec *start = &transaction->commit_time; 472 struct timespec *start = &transaction->commit_time;
473 float ms = (now.tv_sec - start->tv_sec) * 1000 + 473 float ms = (now.tv_sec - start->tv_sec) * 1000 +
474 (now.tv_nsec - start->tv_nsec) / 1000000.0; 474 (now.tv_nsec - start->tv_nsec) / 1000000.0;
475 wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", 475 wlr_log(WLR_DEBUG, "Transaction %p: %zi/%zi ready in %.1fms (%s)",
476 transaction, 476 transaction,
477 transaction->num_configures - transaction->num_waiting + 1, 477 transaction->num_configures - transaction->num_waiting + 1,
478 transaction->num_configures, ms, 478 transaction->num_configures, ms,
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 801dcee0..f05e156f 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -360,7 +360,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
360 struct wlr_xdg_toplevel_move_event *e = data; 360 struct wlr_xdg_toplevel_move_event *e = data;
361 struct sway_seat *seat = e->seat->seat->data; 361 struct sway_seat *seat = e->seat->seat->data;
362 if (e->serial == seat->last_button_serial) { 362 if (e->serial == seat->last_button_serial) {
363 seat_begin_move_floating(seat, view->container, seat->last_button); 363 seatop_begin_move_floating(seat, view->container, seat->last_button);
364 } 364 }
365} 365}
366 366
@@ -374,7 +374,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
374 struct wlr_xdg_toplevel_resize_event *e = data; 374 struct wlr_xdg_toplevel_resize_event *e = data;
375 struct sway_seat *seat = e->seat->seat->data; 375 struct sway_seat *seat = e->seat->seat->data;
376 if (e->serial == seat->last_button_serial) { 376 if (e->serial == seat->last_button_serial) {
377 seat_begin_resize_floating(seat, view->container, 377 seatop_begin_resize_floating(seat, view->container,
378 seat->last_button, e->edges); 378 seat->last_button, e->edges);
379 } 379 }
380} 380}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 4bc83b8e..9f6741c8 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -357,7 +357,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
357 struct wlr_xdg_toplevel_v6_move_event *e = data; 357 struct wlr_xdg_toplevel_v6_move_event *e = data;
358 struct sway_seat *seat = e->seat->seat->data; 358 struct sway_seat *seat = e->seat->seat->data;
359 if (e->serial == seat->last_button_serial) { 359 if (e->serial == seat->last_button_serial) {
360 seat_begin_move_floating(seat, view->container, seat->last_button); 360 seatop_begin_move_floating(seat, view->container, seat->last_button);
361 } 361 }
362} 362}
363 363
@@ -371,7 +371,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
371 struct wlr_xdg_toplevel_v6_resize_event *e = data; 371 struct wlr_xdg_toplevel_v6_resize_event *e = data;
372 struct sway_seat *seat = e->seat->seat->data; 372 struct sway_seat *seat = e->seat->seat->data;
373 if (e->serial == seat->last_button_serial) { 373 if (e->serial == seat->last_button_serial) {
374 seat_begin_resize_floating(seat, view->container, 374 seatop_begin_resize_floating(seat, view->container,
375 seat->last_button, e->edges); 375 seat->last_button, e->edges);
376 } 376 }
377} 377}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 1838ad32..080f6c41 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -470,7 +470,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
470 return; 470 return;
471 } 471 }
472 struct sway_seat *seat = input_manager_current_seat(); 472 struct sway_seat *seat = input_manager_current_seat();
473 seat_begin_move_floating(seat, view->container, seat->last_button); 473 seatop_begin_move_floating(seat, view->container, seat->last_button);
474} 474}
475 475
476static void handle_request_resize(struct wl_listener *listener, void *data) { 476static void handle_request_resize(struct wl_listener *listener, void *data) {
@@ -486,7 +486,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
486 } 486 }
487 struct wlr_xwayland_resize_event *e = data; 487 struct wlr_xwayland_resize_event *e = data;
488 struct sway_seat *seat = input_manager_current_seat(); 488 struct sway_seat *seat = input_manager_current_seat();
489 seat_begin_resize_floating(seat, view->container, 489 seatop_begin_resize_floating(seat, view->container,
490 seat->last_button, e->edges); 490 seat->last_button, e->edges);
491} 491}
492 492
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index d89f64d8..08222494 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,12 +1,11 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <math.h> 2#include <math.h>
3#ifdef __linux__ 3#include <libevdev/libevdev.h>
4#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
5#elif __FreeBSD__ 5#include <errno.h>
6#include <dev/evdev/input-event-codes.h>
7#endif
8#include <float.h> 6#include <float.h>
9#include <limits.h> 7#include <limits.h>
8#include <strings.h>
10#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
11#include <wlr/types/wlr_xcursor_manager.h> 10#include <wlr/types/wlr_xcursor_manager.h>
12#include <wlr/types/wlr_idle.h> 11#include <wlr/types/wlr_idle.h>
@@ -28,10 +27,6 @@
28#include "sway/tree/workspace.h" 27#include "sway/tree/workspace.h"
29#include "wlr-layer-shell-unstable-v1-protocol.h" 28#include "wlr-layer-shell-unstable-v1-protocol.h"
30 29
31// When doing a tiling drag, this is the thickness of the dropzone
32// when dragging to the edge of a layout container.
33#define DROP_LAYOUT_BORDER 30
34
35static uint32_t get_current_time_msec(void) { 30static uint32_t get_current_time_msec(void) {
36 struct timespec now; 31 struct timespec now;
37 clock_gettime(CLOCK_MONOTONIC, &now); 32 clock_gettime(CLOCK_MONOTONIC, &now);
@@ -60,7 +55,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
60 * Returns the node at the cursor's position. If there is a surface at that 55 * Returns the node at the cursor's position. If there is a surface at that
61 * location, it is stored in **surface (it may not be a view). 56 * location, it is stored in **surface (it may not be a view).
62 */ 57 */
63static struct sway_node *node_at_coords( 58struct sway_node *node_at_coords(
64 struct sway_seat *seat, double lx, double ly, 59 struct sway_seat *seat, double lx, double ly,
65 struct wlr_surface **surface, double *sx, double *sy) { 60 struct wlr_surface **surface, double *sx, double *sy) {
66 // check for unmanaged views first 61 // check for unmanaged views first
@@ -88,6 +83,10 @@ static struct sway_node *node_at_coords(
88 return NULL; 83 return NULL;
89 } 84 }
90 struct sway_output *output = wlr_output->data; 85 struct sway_output *output = wlr_output->data;
86 if (!output) {
87 // output is being destroyed
88 return NULL;
89 }
91 double ox = lx, oy = ly; 90 double ox = lx, oy = ly;
92 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); 91 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
93 92
@@ -223,323 +222,6 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
223 return edge; 222 return edge;
224} 223}
225 224
226static void handle_down_motion(struct sway_seat *seat,
227 struct sway_cursor *cursor, uint32_t time_msec) {
228 struct sway_container *con = seat->op_container;
229 if (seat_is_input_allowed(seat, con->view->surface)) {
230 double moved_x = cursor->cursor->x - seat->op_ref_lx;
231 double moved_y = cursor->cursor->y - seat->op_ref_ly;
232 double sx = seat->op_ref_con_lx + moved_x;
233 double sy = seat->op_ref_con_ly + moved_y;
234 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
235 }
236 seat->op_moved = true;
237}
238
239static void handle_move_floating_motion(struct sway_seat *seat,
240 struct sway_cursor *cursor) {
241 struct sway_container *con = seat->op_container;
242 desktop_damage_whole_container(con);
243 container_floating_translate(con,
244 cursor->cursor->x - cursor->previous.x,
245 cursor->cursor->y - cursor->previous.y);
246 desktop_damage_whole_container(con);
247}
248
249static void resize_box(struct wlr_box *box, enum wlr_edges edge,
250 int thickness) {
251 switch (edge) {
252 case WLR_EDGE_TOP:
253 box->height = thickness;
254 break;
255 case WLR_EDGE_LEFT:
256 box->width = thickness;
257 break;
258 case WLR_EDGE_RIGHT:
259 box->x = box->x + box->width - thickness;
260 box->width = thickness;
261 break;
262 case WLR_EDGE_BOTTOM:
263 box->y = box->y + box->height - thickness;
264 box->height = thickness;
265 break;
266 case WLR_EDGE_NONE:
267 box->x += thickness;
268 box->y += thickness;
269 box->width -= thickness * 2;
270 box->height -= thickness * 2;
271 break;
272 }
273}
274
275static void handle_move_tiling_motion(struct sway_seat *seat,
276 struct sway_cursor *cursor) {
277 struct wlr_surface *surface = NULL;
278 double sx, sy;
279 struct sway_node *node = node_at_coords(seat,
280 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
281 // Damage the old location
282 desktop_damage_box(&seat->op_drop_box);
283
284 if (!node) {
285 // Eg. hovered over a layer surface such as swaybar
286 seat->op_target_node = NULL;
287 seat->op_target_edge = WLR_EDGE_NONE;
288 return;
289 }
290
291 if (node->type == N_WORKSPACE) {
292 // Emtpy workspace
293 seat->op_target_node = node;
294 seat->op_target_edge = WLR_EDGE_NONE;
295 workspace_get_box(node->sway_workspace, &seat->op_drop_box);
296 desktop_damage_box(&seat->op_drop_box);
297 return;
298 }
299
300 // Deny moving within own workspace if this is the only child
301 struct sway_container *con = node->sway_container;
302 if (workspace_num_tiling_views(seat->op_container->workspace) == 1 &&
303 con->workspace == seat->op_container->workspace) {
304 seat->op_target_node = NULL;
305 seat->op_target_edge = WLR_EDGE_NONE;
306 return;
307 }
308
309 // Traverse the ancestors, trying to find a layout container perpendicular
310 // to the edge. Eg. close to the top or bottom of a horiz layout.
311 while (con) {
312 enum wlr_edges edge = WLR_EDGE_NONE;
313 enum sway_container_layout layout = container_parent_layout(con);
314 struct wlr_box parent;
315 con->parent ? container_get_box(con->parent, &parent) :
316 workspace_get_box(con->workspace, &parent);
317 if (layout == L_HORIZ || layout == L_TABBED) {
318 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
319 edge = WLR_EDGE_TOP;
320 } else if (cursor->cursor->y > parent.y + parent.height
321 - DROP_LAYOUT_BORDER) {
322 edge = WLR_EDGE_BOTTOM;
323 }
324 } else if (layout == L_VERT || layout == L_STACKED) {
325 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
326 edge = WLR_EDGE_LEFT;
327 } else if (cursor->cursor->x > parent.x + parent.width
328 - DROP_LAYOUT_BORDER) {
329 edge = WLR_EDGE_RIGHT;
330 }
331 }
332 if (edge) {
333 seat->op_target_node = node_get_parent(&con->node);
334 seat->op_target_edge = edge;
335 node_get_box(seat->op_target_node, &seat->op_drop_box);
336 resize_box(&seat->op_drop_box, edge, DROP_LAYOUT_BORDER);
337 desktop_damage_box(&seat->op_drop_box);
338 return;
339 }
340 con = con->parent;
341 }
342
343 // Use the hovered view - but we must be over the actual surface
344 con = node->sway_container;
345 if (!con->view->surface || node == &seat->op_container->node) {
346 seat->op_target_node = NULL;
347 seat->op_target_edge = WLR_EDGE_NONE;
348 return;
349 }
350
351 // Find the closest edge
352 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
353 size_t closest_dist = INT_MAX;
354 size_t dist;
355 seat->op_target_edge = WLR_EDGE_NONE;
356 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
357 closest_dist = dist;
358 seat->op_target_edge = WLR_EDGE_TOP;
359 }
360 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
361 closest_dist = dist;
362 seat->op_target_edge = WLR_EDGE_LEFT;
363 }
364 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
365 closest_dist = dist;
366 seat->op_target_edge = WLR_EDGE_RIGHT;
367 }
368 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
369 closest_dist = dist;
370 seat->op_target_edge = WLR_EDGE_BOTTOM;
371 }
372
373 if (closest_dist > thickness) {
374 seat->op_target_edge = WLR_EDGE_NONE;
375 }
376
377 seat->op_target_node = node;
378 seat->op_drop_box.x = con->content_x;
379 seat->op_drop_box.y = con->content_y;
380 seat->op_drop_box.width = con->content_width;
381 seat->op_drop_box.height = con->content_height;
382 resize_box(&seat->op_drop_box, seat->op_target_edge, thickness);
383 desktop_damage_box(&seat->op_drop_box);
384}
385
386static void calculate_floating_constraints(struct sway_container *con,
387 int *min_width, int *max_width, int *min_height, int *max_height) {
388 if (config->floating_minimum_width == -1) { // no minimum
389 *min_width = 0;
390 } else if (config->floating_minimum_width == 0) { // automatic
391 *min_width = 75;
392 } else {
393 *min_width = config->floating_minimum_width;
394 }
395
396 if (config->floating_minimum_height == -1) { // no minimum
397 *min_height = 0;
398 } else if (config->floating_minimum_height == 0) { // automatic
399 *min_height = 50;
400 } else {
401 *min_height = config->floating_minimum_height;
402 }
403
404 if (config->floating_maximum_width == -1) { // no maximum
405 *max_width = INT_MAX;
406 } else if (config->floating_maximum_width == 0) { // automatic
407 *max_width = con->workspace->width;
408 } else {
409 *max_width = config->floating_maximum_width;
410 }
411
412 if (config->floating_maximum_height == -1) { // no maximum
413 *max_height = INT_MAX;
414 } else if (config->floating_maximum_height == 0) { // automatic
415 *max_height = con->workspace->height;
416 } else {
417 *max_height = config->floating_maximum_height;
418 }
419}
420
421static void handle_resize_floating_motion(struct sway_seat *seat,
422 struct sway_cursor *cursor) {
423 struct sway_container *con = seat->op_container;
424 enum wlr_edges edge = seat->op_resize_edge;
425
426 // The amount the mouse has moved since the start of the resize operation
427 // Positive is down/right
428 double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
429 double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
430
431 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
432 mouse_move_x = 0;
433 }
434 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
435 mouse_move_y = 0;
436 }
437
438 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
439 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
440
441 if (seat->op_resize_preserve_ratio) {
442 double x_multiplier = grow_width / seat->op_ref_width;
443 double y_multiplier = grow_height / seat->op_ref_height;
444 double max_multiplier = fmax(x_multiplier, y_multiplier);
445 grow_width = seat->op_ref_width * max_multiplier;
446 grow_height = seat->op_ref_height * max_multiplier;
447 }
448
449 // Determine new width/height, and accommodate for floating min/max values
450 double width = seat->op_ref_width + grow_width;
451 double height = seat->op_ref_height + grow_height;
452 int min_width, max_width, min_height, max_height;
453 calculate_floating_constraints(con, &min_width, &max_width,
454 &min_height, &max_height);
455 width = fmax(min_width, fmin(width, max_width));
456 height = fmax(min_height, fmin(height, max_height));
457
458 // Apply the view's min/max size
459 if (con->view) {
460 double view_min_width, view_max_width, view_min_height, view_max_height;
461 view_get_constraints(con->view, &view_min_width, &view_max_width,
462 &view_min_height, &view_max_height);
463 width = fmax(view_min_width, fmin(width, view_max_width));
464 height = fmax(view_min_height, fmin(height, view_max_height));
465 }
466
467 // Recalculate these, in case we hit a min/max limit
468 grow_width = width - seat->op_ref_width;
469 grow_height = height - seat->op_ref_height;
470
471 // Determine grow x/y values - these are relative to the container's x/y at
472 // the start of the resize operation.
473 double grow_x = 0, grow_y = 0;
474 if (edge & WLR_EDGE_LEFT) {
475 grow_x = -grow_width;
476 } else if (edge & WLR_EDGE_RIGHT) {
477 grow_x = 0;
478 } else {
479 grow_x = -grow_width / 2;
480 }
481 if (edge & WLR_EDGE_TOP) {
482 grow_y = -grow_height;
483 } else if (edge & WLR_EDGE_BOTTOM) {
484 grow_y = 0;
485 } else {
486 grow_y = -grow_height / 2;
487 }
488
489 // Determine the amounts we need to bump everything relative to the current
490 // size.
491 int relative_grow_width = width - con->width;
492 int relative_grow_height = height - con->height;
493 int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
494 int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
495
496 // Actually resize stuff
497 con->x += relative_grow_x;
498 con->y += relative_grow_y;
499 con->width += relative_grow_width;
500 con->height += relative_grow_height;
501
502 con->content_x += relative_grow_x;
503 con->content_y += relative_grow_y;
504 con->content_width += relative_grow_width;
505 con->content_height += relative_grow_height;
506
507 arrange_container(con);
508}
509
510static void handle_resize_tiling_motion(struct sway_seat *seat,
511 struct sway_cursor *cursor) {
512 int amount_x = 0;
513 int amount_y = 0;
514 int moved_x = cursor->cursor->x - seat->op_ref_lx;
515 int moved_y = cursor->cursor->y - seat->op_ref_ly;
516 enum wlr_edges edge_x = WLR_EDGE_NONE;
517 enum wlr_edges edge_y = WLR_EDGE_NONE;
518 struct sway_container *con = seat->op_container;
519
520 if (seat->op_resize_edge & WLR_EDGE_TOP) {
521 amount_y = (seat->op_ref_height - moved_y) - con->height;
522 edge_y = WLR_EDGE_TOP;
523 } else if (seat->op_resize_edge & WLR_EDGE_BOTTOM) {
524 amount_y = (seat->op_ref_height + moved_y) - con->height;
525 edge_y = WLR_EDGE_BOTTOM;
526 }
527 if (seat->op_resize_edge & WLR_EDGE_LEFT) {
528 amount_x = (seat->op_ref_width - moved_x) - con->width;
529 edge_x = WLR_EDGE_LEFT;
530 } else if (seat->op_resize_edge & WLR_EDGE_RIGHT) {
531 amount_x = (seat->op_ref_width + moved_x) - con->width;
532 edge_x = WLR_EDGE_RIGHT;
533 }
534
535 if (amount_x != 0) {
536 container_resize_tiled(seat->op_container, edge_x, amount_x);
537 }
538 if (amount_y != 0) {
539 container_resize_tiled(seat->op_container, edge_y, amount_y);
540 }
541}
542
543static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, 225static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
544 struct sway_node *node, struct wlr_surface *surface, 226 struct sway_node *node, struct wlr_surface *surface,
545 double sx, double sy) { 227 double sx, double sy) {
@@ -589,6 +271,50 @@ void cursor_rebase(struct sway_cursor *cursor) {
589 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); 271 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy);
590} 272}
591 273
274static int hide_notify(void *data) {
275 struct sway_cursor *cursor = data;
276 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
277 cursor->hidden = true;
278 return 1;
279}
280
281int cursor_get_timeout(struct sway_cursor *cursor){
282 struct seat_config *sc = seat_get_config(cursor->seat);
283 if (!sc) {
284 sc = seat_get_config_by_name("*");
285 }
286 int timeout = sc ? sc->hide_cursor_timeout : 0;
287 if (timeout < 0) {
288 timeout = 0;
289 }
290 return timeout;
291}
292
293void cursor_handle_activity(struct sway_cursor *cursor) {
294 wl_event_source_timer_update(
295 cursor->hide_source, cursor_get_timeout(cursor));
296
297 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
298 if (cursor->hidden) {
299 cursor_unhide(cursor);
300 }
301}
302
303void cursor_unhide(struct sway_cursor *cursor) {
304 cursor->hidden = false;
305 if (cursor->image_surface) {
306 cursor_set_image_surface(cursor,
307 cursor->image_surface,
308 cursor->hotspot_x,
309 cursor->hotspot_y,
310 cursor->image_client);
311 } else {
312 const char *image = cursor->image;
313 cursor->image = NULL;
314 cursor_set_image(cursor, image, cursor->image_client);
315 }
316}
317
592void cursor_send_pointer_motion(struct sway_cursor *cursor, 318void cursor_send_pointer_motion(struct sway_cursor *cursor,
593 uint32_t time_msec) { 319 uint32_t time_msec) {
594 if (time_msec == 0) { 320 if (time_msec == 0) {
@@ -598,26 +324,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
598 struct sway_seat *seat = cursor->seat; 324 struct sway_seat *seat = cursor->seat;
599 struct wlr_seat *wlr_seat = seat->wlr_seat; 325 struct wlr_seat *wlr_seat = seat->wlr_seat;
600 326
601 if (seat->operation != OP_NONE) { 327 if (seat_doing_seatop(seat)) {
602 switch (seat->operation) { 328 seatop_motion(seat, time_msec);
603 case OP_DOWN:
604 handle_down_motion(seat, cursor, time_msec);
605 break;
606 case OP_MOVE_FLOATING:
607 handle_move_floating_motion(seat, cursor);
608 break;
609 case OP_MOVE_TILING:
610 handle_move_tiling_motion(seat, cursor);
611 break;
612 case OP_RESIZE_FLOATING:
613 handle_resize_floating_motion(seat, cursor);
614 break;
615 case OP_RESIZE_TILING:
616 handle_resize_tiling_motion(seat, cursor);
617 break;
618 case OP_NONE:
619 break;
620 }
621 cursor->previous.x = cursor->cursor->x; 329 cursor->previous.x = cursor->cursor->x;
622 cursor->previous.y = cursor->cursor->y; 330 cursor->previous.y = cursor->cursor->y;
623 return; 331 return;
@@ -679,11 +387,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
679 387
680static void handle_cursor_motion(struct wl_listener *listener, void *data) { 388static void handle_cursor_motion(struct wl_listener *listener, void *data) {
681 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 389 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
682 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
683 struct wlr_event_pointer_motion *event = data; 390 struct wlr_event_pointer_motion *event = data;
684 wlr_cursor_move(cursor->cursor, event->device, 391 wlr_cursor_move(cursor->cursor, event->device,
685 event->delta_x, event->delta_y); 392 event->delta_x, event->delta_y);
686 cursor_send_pointer_motion(cursor, event->time_msec); 393 cursor_send_pointer_motion(cursor, event->time_msec);
394 cursor_handle_activity(cursor);
687 transaction_commit_dirty(); 395 transaction_commit_dirty();
688} 396}
689 397
@@ -691,10 +399,10 @@ static void handle_cursor_motion_absolute(
691 struct wl_listener *listener, void *data) { 399 struct wl_listener *listener, void *data) {
692 struct sway_cursor *cursor = 400 struct sway_cursor *cursor =
693 wl_container_of(listener, cursor, motion_absolute); 401 wl_container_of(listener, cursor, motion_absolute);
694 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
695 struct wlr_event_pointer_motion_absolute *event = data; 402 struct wlr_event_pointer_motion_absolute *event = data;
696 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 403 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
697 cursor_send_pointer_motion(cursor, event->time_msec); 404 cursor_send_pointer_motion(cursor, event->time_msec);
405 cursor_handle_activity(cursor);
698 transaction_commit_dirty(); 406 transaction_commit_dirty();
699} 407}
700 408
@@ -794,11 +502,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
794 struct sway_seat *seat = cursor->seat; 502 struct sway_seat *seat = cursor->seat;
795 503
796 // Handle existing seat operation 504 // Handle existing seat operation
797 if (cursor->seat->operation != OP_NONE) { 505 if (seat_doing_seatop(seat)) {
798 if (button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { 506 if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) {
799 seat_end_mouse_operation(seat); 507 seatop_finish(seat);
800 seat_pointer_notify_button(seat, time_msec, button, state); 508 seat_pointer_notify_button(seat, time_msec, button, state);
801 } 509 }
510 if (state == WLR_BUTTON_PRESSED) {
511 state_add_button(cursor, button);
512 } else {
513 state_erase_button(cursor, button);
514 }
802 return; 515 return;
803 } 516 }
804 517
@@ -864,7 +577,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
864 if (cont && resize_edge && button == BTN_LEFT && 577 if (cont && resize_edge && button == BTN_LEFT &&
865 state == WLR_BUTTON_PRESSED && !is_floating) { 578 state == WLR_BUTTON_PRESSED && !is_floating) {
866 seat_set_focus_container(seat, cont); 579 seat_set_focus_container(seat, cont);
867 seat_begin_resize_tiling(seat, cont, button, edge); 580 seatop_begin_resize_tiling(seat, cont, button, edge);
868 return; 581 return;
869 } 582 }
870 583
@@ -894,7 +607,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
894 } 607 }
895 cursor_set_image(seat->cursor, image, NULL); 608 cursor_set_image(seat->cursor, image, NULL);
896 seat_set_focus_container(seat, cont); 609 seat_set_focus_container(seat, cont);
897 seat_begin_resize_tiling(seat, cont, button, edge); 610 seatop_begin_resize_tiling(seat, cont, button, edge);
898 return; 611 return;
899 } 612 }
900 } 613 }
@@ -909,7 +622,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
909 cont = cont->parent; 622 cont = cont->parent;
910 } 623 }
911 seat_set_focus_container(seat, cont); 624 seat_set_focus_container(seat, cont);
912 seat_begin_move_floating(seat, cont, button); 625 seatop_begin_move_floating(seat, cont, button);
913 return; 626 return;
914 } 627 }
915 } 628 }
@@ -919,7 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
919 state == WLR_BUTTON_PRESSED) { 632 state == WLR_BUTTON_PRESSED) {
920 // Via border 633 // Via border
921 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 634 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
922 seat_begin_resize_floating(seat, cont, button, resize_edge); 635 seatop_begin_resize_floating(seat, cont, button, resize_edge);
923 return; 636 return;
924 } 637 }
925 638
@@ -936,16 +649,30 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
936 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 649 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
937 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 650 edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
938 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 651 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
939 seat_begin_resize_floating(seat, floater, button, edge); 652 seatop_begin_resize_floating(seat, floater, button, edge);
940 return; 653 return;
941 } 654 }
942 } 655 }
943 656
944 // Handle moving a tiling container 657 // Handle moving a tiling container
945 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && 658 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
946 !is_floating_or_child && cont && !cont->is_fullscreen) { 659 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
660 cont && !cont->is_fullscreen) {
661 struct sway_container *focus = seat_get_focused_container(seat);
662 bool focused = focus == cont || container_has_ancestor(focus, cont);
663 if (on_titlebar && !focused) {
664 node = seat_get_focus_inactive(seat, &cont->node);
665 seat_set_focus(seat, node);
666 }
667
947 seat_pointer_notify_button(seat, time_msec, button, state); 668 seat_pointer_notify_button(seat, time_msec, button, state);
948 seat_begin_move_tiling(seat, cont, button); 669
670 // If moving a container by it's title bar, use a threshold for the drag
671 if (!mod_pressed && config->tiling_drag_threshold > 0) {
672 seatop_begin_move_tiling_threshold(seat, cont, button);
673 } else {
674 seatop_begin_move_tiling(seat, cont, button);
675 }
949 return; 676 return;
950 } 677 }
951 678
@@ -953,7 +680,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
953 if (surface && cont && state == WLR_BUTTON_PRESSED) { 680 if (surface && cont && state == WLR_BUTTON_PRESSED) {
954 seat_set_focus_container(seat, cont); 681 seat_set_focus_container(seat, cont);
955 seat_pointer_notify_button(seat, time_msec, button, state); 682 seat_pointer_notify_button(seat, time_msec, button, state);
956 seat_begin_down(seat, cont, button, sx, sy); 683 seatop_begin_down(seat, cont, button, sx, sy);
957 return; 684 return;
958 } 685 }
959 686
@@ -970,18 +697,32 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
970 697
971static void handle_cursor_button(struct wl_listener *listener, void *data) { 698static void handle_cursor_button(struct wl_listener *listener, void *data) {
972 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 699 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
973 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
974 struct wlr_event_pointer_button *event = data; 700 struct wlr_event_pointer_button *event = data;
975 dispatch_cursor_button(cursor, event->device, 701 dispatch_cursor_button(cursor, event->device,
976 event->time_msec, event->button, event->state); 702 event->time_msec, event->button, event->state);
703 cursor_handle_activity(cursor);
977 transaction_commit_dirty(); 704 transaction_commit_dirty();
978} 705}
979 706
980static void dispatch_cursor_axis(struct sway_cursor *cursor, 707static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
708 switch (event->orientation) {
709 case WLR_AXIS_ORIENTATION_VERTICAL:
710 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
711 case WLR_AXIS_ORIENTATION_HORIZONTAL:
712 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
713 default:
714 wlr_log(WLR_DEBUG, "Unknown axis orientation");
715 return 0;
716 }
717}
718
719void dispatch_cursor_axis(struct sway_cursor *cursor,
981 struct wlr_event_pointer_axis *event) { 720 struct wlr_event_pointer_axis *event) {
982 struct sway_seat *seat = cursor->seat; 721 struct sway_seat *seat = cursor->seat;
983 struct sway_input_device *input_device = event->device->data; 722 struct sway_input_device *input_device =
984 struct input_config *ic = input_device_get_config(input_device); 723 event->device ? event->device->data : NULL;
724 struct input_config *ic =
725 input_device ? input_device_get_config(input_device) : NULL;
985 726
986 // Determine what's under the cursor 727 // Determine what's under the cursor
987 struct wlr_surface *surface = NULL; 728 struct wlr_surface *surface = NULL;
@@ -993,11 +734,35 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
993 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; 734 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
994 bool on_border = edge != WLR_EDGE_NONE; 735 bool on_border = edge != WLR_EDGE_NONE;
995 bool on_titlebar = cont && !on_border && !surface; 736 bool on_titlebar = cont && !on_border && !surface;
737 bool on_titlebar_border = cont && on_border &&
738 cursor->cursor->y < cont->content_y;
739 bool on_contents = cont && !on_border && surface;
996 float scroll_factor = 740 float scroll_factor =
997 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; 741 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
998 742
999 // Scrolling on a tabbed or stacked title bar 743 bool handled = false;
1000 if (on_titlebar) { 744
745 // Gather information needed for mouse bindings
746 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
747 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
748 struct wlr_input_device *device =
749 input_device ? input_device->wlr_device : NULL;
750 char *dev_id = device ? input_device_get_identifier(device) : strdup("*");
751 uint32_t button = wl_axis_to_button(event);
752
753 // Handle mouse bindings - x11 mouse buttons 4-7 - press event
754 struct sway_binding *binding = NULL;
755 state_add_button(cursor, button);
756 binding = get_active_mouse_binding(cursor,
757 config->current_mode->mouse_bindings, modifiers, false,
758 on_titlebar, on_border, on_contents, dev_id);
759 if (binding) {
760 seat_execute_command(seat, binding);
761 handled = true;
762 }
763
764 // Scrolling on a tabbed or stacked title bar (handled as press event)
765 if (!handled && (on_titlebar || on_titlebar_border)) {
1001 enum sway_container_layout layout = container_parent_layout(cont); 766 enum sway_container_layout layout = container_parent_layout(cont);
1002 if (layout == L_TABBED || layout == L_STACKED) { 767 if (layout == L_TABBED || layout == L_STACKED) {
1003 struct sway_node *tabcontainer = node_get_parent(node); 768 struct sway_node *tabcontainer = node_get_parent(node);
@@ -1024,20 +789,33 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1024 seat_set_raw_focus(seat, new_focus); 789 seat_set_raw_focus(seat, new_focus);
1025 seat_set_raw_focus(seat, old_focus); 790 seat_set_raw_focus(seat, old_focus);
1026 } 791 }
1027 return; 792 handled = true;
1028 } 793 }
1029 } 794 }
1030 795
1031 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 796 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
1032 event->orientation, scroll_factor * event->delta, 797 binding = get_active_mouse_binding(cursor,
1033 round(scroll_factor * event->delta_discrete), event->source); 798 config->current_mode->mouse_bindings, modifiers, true,
799 on_titlebar, on_border, on_contents, dev_id);
800 state_erase_button(cursor, button);
801 if (binding) {
802 seat_execute_command(seat, binding);
803 handled = true;
804 }
805 free(dev_id);
806
807 if (!handled) {
808 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
809 event->orientation, scroll_factor * event->delta,
810 round(scroll_factor * event->delta_discrete), event->source);
811 }
1034} 812}
1035 813
1036static void handle_cursor_axis(struct wl_listener *listener, void *data) { 814static void handle_cursor_axis(struct wl_listener *listener, void *data) {
1037 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 815 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
1038 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
1039 struct wlr_event_pointer_axis *event = data; 816 struct wlr_event_pointer_axis *event = data;
1040 dispatch_cursor_axis(cursor, event); 817 dispatch_cursor_axis(cursor, event);
818 cursor_handle_activity(cursor);
1041 transaction_commit_dirty(); 819 transaction_commit_dirty();
1042} 820}
1043 821
@@ -1209,7 +987,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1209 void *data) { 987 void *data) {
1210 struct sway_cursor *cursor = 988 struct sway_cursor *cursor =
1211 wl_container_of(listener, cursor, request_set_cursor); 989 wl_container_of(listener, cursor, request_set_cursor);
1212 if (cursor->seat->operation != OP_NONE) { 990 if (seat_doing_seatop(cursor->seat)) {
1213 return; 991 return;
1214 } 992 }
1215 struct wlr_seat_pointer_request_set_cursor_event *event = data; 993 struct wlr_seat_pointer_request_set_cursor_event *event = data;
@@ -1228,10 +1006,8 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1228 return; 1006 return;
1229 } 1007 }
1230 1008
1231 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, 1009 cursor_set_image_surface(cursor, event->surface, event->hotspot_x,
1232 event->hotspot_y); 1010 event->hotspot_y, focused_client);
1233 cursor->image = NULL;
1234 cursor->image_client = focused_client;
1235} 1011}
1236 1012
1237void cursor_set_image(struct sway_cursor *cursor, const char *image, 1013void cursor_set_image(struct sway_cursor *cursor, const char *image,
@@ -1239,14 +1015,43 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1239 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { 1015 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
1240 return; 1016 return;
1241 } 1017 }
1018
1019 const char *current_image = cursor->image;
1020 cursor->image = image;
1021 cursor->image_surface = NULL;
1022 cursor->hotspot_x = cursor->hotspot_y = 0;
1023 cursor->image_client = client;
1024
1025 if (cursor->hidden) {
1026 return;
1027 }
1028
1242 if (!image) { 1029 if (!image) {
1243 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 1030 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1244 } else if (!cursor->image || strcmp(cursor->image, image) != 0) { 1031 } else if (!current_image || strcmp(current_image, image) != 0) {
1245 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 1032 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
1246 cursor->cursor); 1033 cursor->cursor);
1247 } 1034 }
1248 cursor->image = image; 1035}
1036
1037void cursor_set_image_surface(struct sway_cursor *cursor,
1038 struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y,
1039 struct wl_client *client) {
1040 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
1041 return;
1042 }
1043
1044 cursor->image = NULL;
1045 cursor->image_surface = surface;
1046 cursor->hotspot_x = hotspot_x;
1047 cursor->hotspot_y = hotspot_y;
1249 cursor->image_client = client; 1048 cursor->image_client = client;
1049
1050 if (cursor->hidden) {
1051 return;
1052 }
1053
1054 wlr_cursor_set_surface(cursor->cursor, surface, hotspot_x, hotspot_y);
1250} 1055}
1251 1056
1252void sway_cursor_destroy(struct sway_cursor *cursor) { 1057void sway_cursor_destroy(struct sway_cursor *cursor) {
@@ -1254,6 +1059,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1254 return; 1059 return;
1255 } 1060 }
1256 1061
1062 wl_event_source_remove(cursor->hide_source);
1063
1257 wlr_xcursor_manager_destroy(cursor->xcursor_manager); 1064 wlr_xcursor_manager_destroy(cursor->xcursor_manager);
1258 wlr_cursor_destroy(cursor->cursor); 1065 wlr_cursor_destroy(cursor->cursor);
1259 free(cursor); 1066 free(cursor);
@@ -1277,6 +1084,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1277 cursor->seat = seat; 1084 cursor->seat = seat;
1278 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); 1085 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);
1279 1086
1087 cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop,
1088 hide_notify, cursor);
1089
1280 // input events 1090 // input events
1281 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1091 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
1282 cursor->motion.notify = handle_cursor_motion; 1092 cursor->motion.notify = handle_cursor_motion;
@@ -1362,3 +1172,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor,
1362 1172
1363 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1173 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1364} 1174}
1175
1176uint32_t get_mouse_bindsym(const char *name, char **error) {
1177 if (strncasecmp(name, "button", strlen("button")) == 0) {
1178 // Map to x11 mouse buttons
1179 int number = name[strlen("button")] - '0';
1180 if (number < 1 || number > 9 || strlen(name) > strlen("button0")) {
1181 *error = strdup("Only buttons 1-9 are supported. For other mouse "
1182 "buttons, use the name of the event code.");
1183 return 0;
1184 }
1185 static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
1186 SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,
1187 SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};
1188 return buttons[number - 1];
1189 } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) {
1190 // Get event code from name
1191 int code = libevdev_event_code_from_name(EV_KEY, name);
1192 if (code == -1) {
1193 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1;
1194 *error = malloc(len);
1195 if (*error) {
1196 snprintf(*error, len, "Unknown event %s", name);
1197 }
1198 return 0;
1199 }
1200 return code;
1201 }
1202 return 0;
1203}
1204
1205uint32_t get_mouse_bindcode(const char *name, char **error) {
1206 // Validate event code
1207 errno = 0;
1208 char *endptr;
1209 int code = strtol(name, &endptr, 10);
1210 if (endptr == name && code <= 0) {
1211 *error = strdup("Button event code must be a positive integer.");
1212 return 0;
1213 } else if (errno == ERANGE) {
1214 *error = strdup("Button event code out of range.");
1215 return 0;
1216 }
1217 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1218 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1219 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button",
1220 code, event) + 1;
1221 *error = malloc(len);
1222 if (*error) {
1223 snprintf(*error, len, "Event code %d (%s) is not a button",
1224 code, event);
1225 }
1226 return 0;
1227 }
1228 return code;
1229}
1230
1231uint32_t get_mouse_button(const char *name, char **error) {
1232 uint32_t button = get_mouse_bindsym(name, error);
1233 if (!button && !*error) {
1234 button = get_mouse_bindcode(name, error);
1235 }
1236 return button;
1237}
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 68445d68..d90803f6 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 2#include <ctype.h>
3#include <float.h> 3#include <float.h>
4#include <limits.h> 4#include <limits.h>
@@ -49,7 +49,7 @@ char *input_device_get_identifier(struct wlr_input_device *device) {
49 int vendor = device->vendor; 49 int vendor = device->vendor;
50 int product = device->product; 50 int product = device->product;
51 char *name = strdup(device->name); 51 char *name = strdup(device->name);
52 name = strip_whitespace(name); 52 strip_whitespace(name);
53 53
54 char *p = name; 54 char *p = name;
55 for (; *p; ++p) { 55 for (; *p; ++p) {
@@ -82,11 +82,12 @@ static struct sway_input_device *input_sway_device_from_wlr(
82 return NULL; 82 return NULL;
83} 83}
84 84
85static bool input_has_seat_configuration(void) { 85static bool input_has_seat_fallback_configuration(void) {
86 struct sway_seat *seat = NULL; 86 struct sway_seat *seat = NULL;
87 wl_list_for_each(seat, &server.input->seats, link) { 87 wl_list_for_each(seat, &server.input->seats, link) {
88 struct seat_config *seat_config = seat_get_config(seat); 88 struct seat_config *seat_config = seat_get_config(seat);
89 if (seat_config) { 89 if (seat_config && strcmp(seat_config->name, "*") != 0
90 && seat_config->fallback != -1) {
90 return true; 91 return true;
91 } 92 }
92 } 93 }
@@ -94,6 +95,18 @@ static bool input_has_seat_configuration(void) {
94 return false; 95 return false;
95} 96}
96 97
98void input_manager_verify_fallback_seat(void) {
99 struct sway_seat *seat = NULL;
100 if (!input_has_seat_fallback_configuration()) {
101 wlr_log(WLR_DEBUG, "no fallback seat config - creating default");
102 seat = input_manager_get_default_seat();
103 struct seat_config *sc = new_seat_config(seat->wlr_seat->name);
104 sc->fallback = true;
105 sc = store_seat_config(sc);
106 input_manager_apply_seat_config(sc);
107 }
108}
109
97static void input_manager_libinput_config_keyboard( 110static void input_manager_libinput_config_keyboard(
98 struct sway_input_device *input_device) { 111 struct sway_input_device *input_device) {
99 struct wlr_input_device *wlr_device = input_device->wlr_device; 112 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -116,6 +129,24 @@ static void input_manager_libinput_config_keyboard(
116 } 129 }
117} 130}
118 131
132static void input_manager_libinput_reset_keyboard(
133 struct sway_input_device *input_device) {
134 struct wlr_input_device *wlr_device = input_device->wlr_device;
135 struct libinput_device *libinput_device;
136
137 if (!wlr_input_device_is_libinput(wlr_device)) {
138 return;
139 }
140
141 libinput_device = wlr_libinput_get_device_handle(wlr_device);
142
143 uint32_t send_events =
144 libinput_device_config_send_events_get_default_mode(libinput_device);
145 wlr_log(WLR_DEBUG, "libinput_reset_keyboard(%s) send_events_set_mode(%d)",
146 input_device->identifier, send_events);
147 libinput_device_config_send_events_set_mode(libinput_device, send_events);
148}
149
119static void input_manager_libinput_config_touch( 150static void input_manager_libinput_config_touch(
120 struct sway_input_device *input_device) { 151 struct sway_input_device *input_device) {
121 struct wlr_input_device *wlr_device = input_device->wlr_device; 152 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -138,6 +169,24 @@ static void input_manager_libinput_config_touch(
138 } 169 }
139} 170}
140 171
172static void input_manager_libinput_reset_touch(
173 struct sway_input_device *input_device) {
174 struct wlr_input_device *wlr_device = input_device->wlr_device;
175 struct libinput_device *libinput_device;
176
177 if (!wlr_input_device_is_libinput(wlr_device)) {
178 return;
179 }
180
181 libinput_device = wlr_libinput_get_device_handle(wlr_device);
182
183 uint32_t send_events =
184 libinput_device_config_send_events_get_default_mode(libinput_device);
185 wlr_log(WLR_DEBUG, "libinput_reset_touch(%s) send_events_set_mode(%d)",
186 input_device->identifier, send_events);
187 libinput_device_config_send_events_set_mode(libinput_device, send_events);
188}
189
141static void input_manager_libinput_config_pointer( 190static void input_manager_libinput_config_pointer(
142 struct sway_input_device *input_device) { 191 struct sway_input_device *input_device) {
143 struct wlr_input_device *wlr_device = input_device->wlr_device; 192 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -167,14 +216,14 @@ static void input_manager_libinput_config_pointer(
167 if (ic->drag != INT_MIN) { 216 if (ic->drag != INT_MIN) {
168 wlr_log(WLR_DEBUG, 217 wlr_log(WLR_DEBUG,
169 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", 218 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)",
170 ic->identifier, ic->click_method); 219 ic->identifier, ic->drag);
171 libinput_device_config_tap_set_drag_enabled(libinput_device, 220 libinput_device_config_tap_set_drag_enabled(libinput_device,
172 ic->drag); 221 ic->drag);
173 } 222 }
174 if (ic->drag_lock != INT_MIN) { 223 if (ic->drag_lock != INT_MIN) {
175 wlr_log(WLR_DEBUG, 224 wlr_log(WLR_DEBUG,
176 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 225 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
177 ic->identifier, ic->click_method); 226 ic->identifier, ic->drag_lock);
178 libinput_device_config_tap_set_drag_lock_enabled(libinput_device, 227 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
179 ic->drag_lock); 228 ic->drag_lock);
180 } 229 }
@@ -235,12 +284,118 @@ static void input_manager_libinput_config_pointer(
235 } 284 }
236 if (ic->tap_button_map != INT_MIN) { 285 if (ic->tap_button_map != INT_MIN) {
237 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", 286 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)",
238 ic->identifier, ic->tap); 287 ic->identifier, ic->tap_button_map);
239 libinput_device_config_tap_set_button_map(libinput_device, 288 libinput_device_config_tap_set_button_map(libinput_device,
240 ic->tap_button_map); 289 ic->tap_button_map);
241 } 290 }
242} 291}
243 292
293static void input_manager_libinput_reset_pointer(
294 struct sway_input_device *input_device) {
295 struct wlr_input_device *wlr_device = input_device->wlr_device;
296
297 if (!wlr_input_device_is_libinput(wlr_device)) {
298 return;
299 }
300
301 struct libinput_device *libinput_device =
302 wlr_libinput_get_device_handle(wlr_device);
303
304 enum libinput_config_accel_profile accel_profile =
305 libinput_device_config_accel_get_default_profile(libinput_device);
306 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_profile(%d)",
307 input_device->identifier, accel_profile);
308 libinput_device_config_accel_set_profile(libinput_device, accel_profile);
309
310 enum libinput_config_click_method click_method =
311 libinput_device_config_click_get_default_method(libinput_device);
312 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) click_set_method(%d)",
313 input_device->identifier, click_method);
314 libinput_device_config_click_set_method(libinput_device, click_method);
315
316 enum libinput_config_drag_state drag =
317 libinput_device_config_tap_get_default_drag_enabled(libinput_device);
318 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_drag_enabled(%d)",
319 input_device->identifier, drag);
320 libinput_device_config_tap_set_drag_enabled(libinput_device, drag);
321
322 enum libinput_config_drag_lock_state drag_lock =
323 libinput_device_config_tap_get_default_drag_lock_enabled(
324 libinput_device);
325 wlr_log(WLR_DEBUG,
326 "libinput_reset_pointer(%s) tap_set_drag_lock_enabled(%d)",
327 input_device->identifier, drag_lock);
328 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
329 drag_lock);
330
331 enum libinput_config_dwt_state dwt =
332 libinput_device_config_dwt_get_default_enabled(libinput_device);
333 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) dwt_set_enabled(%d)",
334 input_device->identifier, dwt);
335 libinput_device_config_dwt_set_enabled(libinput_device, dwt);
336
337 int left_handed =
338 libinput_device_config_left_handed_get_default(libinput_device);
339 wlr_log(WLR_DEBUG,
340 "libinput_reset_pointer(%s) left_handed_set_enabled(%d)",
341 input_device->identifier, left_handed);
342 libinput_device_config_left_handed_set(libinput_device, left_handed);
343
344 enum libinput_config_middle_emulation_state middle_emulation =
345 libinput_device_config_middle_emulation_get_default_enabled(
346 libinput_device);
347 wlr_log(WLR_DEBUG,
348 "libinput_reset_pointer(%s) middle_emulation_set_enabled(%d)",
349 input_device->identifier, middle_emulation);
350 libinput_device_config_middle_emulation_set_enabled(libinput_device,
351 middle_emulation);
352
353 int natural_scroll =
354 libinput_device_config_scroll_get_default_natural_scroll_enabled(
355 libinput_device);
356 wlr_log(WLR_DEBUG,
357 "libinput_reset_pointer(%s) natural_scroll_set_enabled(%d)",
358 input_device->identifier, natural_scroll);
359 libinput_device_config_scroll_set_natural_scroll_enabled(
360 libinput_device, natural_scroll);
361
362 double pointer_accel =
363 libinput_device_config_accel_get_default_speed(libinput_device);
364 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_speed(%f)",
365 input_device->identifier, pointer_accel);
366 libinput_device_config_accel_set_speed(libinput_device, pointer_accel);
367
368 uint32_t scroll_button =
369 libinput_device_config_scroll_get_default_button(libinput_device);
370 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_button(%d)",
371 input_device->identifier, scroll_button);
372 libinput_device_config_scroll_set_button(libinput_device, scroll_button);
373
374 enum libinput_config_scroll_method scroll_method =
375 libinput_device_config_scroll_get_default_method(libinput_device);
376 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_method(%d)",
377 input_device->identifier, scroll_method);
378 libinput_device_config_scroll_set_method(libinput_device, scroll_method);
379
380 uint32_t send_events =
381 libinput_device_config_send_events_get_default_mode(libinput_device);
382 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) send_events_set_mode(%d)",
383 input_device->identifier, send_events);
384 libinput_device_config_send_events_set_mode(libinput_device, send_events);
385
386 enum libinput_config_tap_state tap =
387 libinput_device_config_tap_get_default_enabled(libinput_device);
388 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_enabled(%d)",
389 input_device->identifier, tap);
390 libinput_device_config_tap_set_enabled(libinput_device, tap);
391
392 enum libinput_config_tap_button_map tap_button_map =
393 libinput_device_config_tap_get_button_map(libinput_device);
394 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_button_map(%d)",
395 input_device->identifier, tap_button_map);
396 libinput_device_config_tap_set_button_map(libinput_device, tap_button_map);
397}
398
244static void handle_device_destroy(struct wl_listener *listener, void *data) { 399static void handle_device_destroy(struct wl_listener *listener, void *data) {
245 struct wlr_input_device *device = data; 400 struct wlr_input_device *device = data;
246 401
@@ -295,15 +450,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
295 wl_signal_add(&device->events.destroy, &input_device->device_destroy); 450 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
296 input_device->device_destroy.notify = handle_device_destroy; 451 input_device->device_destroy.notify = handle_device_destroy;
297 452
298 struct sway_seat *seat = NULL; 453 input_manager_verify_fallback_seat();
299 if (!input_has_seat_configuration()) {
300 wlr_log(WLR_DEBUG, "no seat configuration, using default seat");
301 seat = input_manager_get_default_seat();
302 seat_add_device(seat, input_device);
303 return;
304 }
305 454
306 bool added = false; 455 bool added = false;
456 struct sway_seat *seat = NULL;
307 wl_list_for_each(seat, &input->seats, link) { 457 wl_list_for_each(seat, &input->seats, link) {
308 struct seat_config *seat_config = seat_get_config(seat); 458 struct seat_config *seat_config = seat_get_config(seat);
309 bool has_attachment = seat_config && 459 bool has_attachment = seat_config &&
@@ -458,15 +608,50 @@ void input_manager_apply_input_config(struct input_config *input_config) {
458 } 608 }
459} 609}
460 610
461void input_manager_apply_seat_config(struct seat_config *seat_config) { 611void input_manager_reset_input(struct sway_input_device *input_device) {
462 wlr_log(WLR_DEBUG, "applying new seat config for seat %s", 612 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
463 seat_config->name); 613 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
464 struct sway_seat *seat = input_manager_get_seat(seat_config->name); 614 input_manager_libinput_reset_pointer(input_device);
465 if (!seat) { 615 } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
466 return; 616 input_manager_libinput_reset_keyboard(input_device);
617 } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) {
618 input_manager_libinput_reset_touch(input_device);
619 }
620
621 struct sway_seat *seat = NULL;
622 wl_list_for_each(seat, &server.input->seats, link) {
623 seat_reset_device(seat, input_device);
467 } 624 }
625}
626
627void input_manager_reset_all_inputs() {
628 struct sway_input_device *input_device = NULL;
629 wl_list_for_each(input_device, &server.input->devices, link) {
630 input_manager_reset_input(input_device);
631 }
632}
468 633
469 seat_apply_config(seat, seat_config); 634
635void input_manager_apply_seat_config(struct seat_config *seat_config) {
636 wlr_log(WLR_DEBUG, "applying seat config for seat %s", seat_config->name);
637 if (strcmp(seat_config->name, "*") == 0) {
638 struct sway_seat *seat = NULL;
639 wl_list_for_each(seat, &server.input->seats, link) {
640 // Only apply the wildcard config directly if there is no seat
641 // specific config
642 struct seat_config *sc = seat_get_config(seat);
643 if (!sc) {
644 sc = seat_config;
645 }
646 seat_apply_config(seat, sc);
647 }
648 } else {
649 struct sway_seat *seat = input_manager_get_seat(seat_config->name);
650 if (!seat) {
651 return;
652 }
653 seat_apply_config(seat, seat_config);
654 }
470 655
471 // for every device, try to add it to a seat and if no seat has it 656 // for every device, try to add it to a seat and if no seat has it
472 // attached, add it to the fallback seats. 657 // attached, add it to the fallback seats.
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index c1b53237..46286410 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -295,14 +295,10 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
295 get_active_binding(&keyboard->state_keysyms_raw, 295 get_active_binding(&keyboard->state_keysyms_raw,
296 config->current_mode->keysym_bindings, &binding, 296 config->current_mode->keysym_bindings, &binding,
297 raw_modifiers, false, input_inhibited, device_identifier); 297 raw_modifiers, false, input_inhibited, device_identifier);
298
299 if (binding) {
300 seat_execute_command(seat, binding);
301 handled = true;
302 }
303 } 298 }
304 299
305 // Set up (or clear) keyboard repeat for a pressed binding 300 // Set up (or clear) keyboard repeat for a pressed binding. Since the
301 // binding may remove the keyboard, the timer needs to be updated first
306 if (binding && wlr_device->keyboard->repeat_info.delay > 0) { 302 if (binding && wlr_device->keyboard->repeat_info.delay > 0) {
307 keyboard->repeat_binding = binding; 303 keyboard->repeat_binding = binding;
308 if (wl_event_source_timer_update(keyboard->key_repeat_source, 304 if (wl_event_source_timer_update(keyboard->key_repeat_source,
@@ -316,6 +312,11 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
316 } 312 }
317 } 313 }
318 314
315 if (binding) {
316 seat_execute_command(seat, binding);
317 handled = true;
318 }
319
319 // Compositor bindings 320 // Compositor bindings
320 if (!handled && event->state == WLR_KEY_PRESSED) { 321 if (!handled && event->state == WLR_KEY_PRESSED) {
321 handled = keyboard_execute_compositor_binding( 322 handled = keyboard_execute_compositor_binding(
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 663c5140..a63999b6 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,12 +1,7 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#define _POSIX_C_SOURCE 199309L
3#include <assert.h> 2#include <assert.h>
4#include <errno.h> 3#include <errno.h>
5#ifdef __linux__
6#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
7#elif __FreeBSD__
8#include <dev/evdev/input-event-codes.h>
9#endif
10#include <strings.h> 5#include <strings.h>
11#include <time.h> 6#include <time.h>
12#include <wlr/types/wlr_cursor.h> 7#include <wlr/types/wlr_cursor.h>
@@ -175,6 +170,12 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
175 parent = node_get_parent(parent); 170 parent = node_get_parent(parent);
176 } 171 }
177 172
173 if (next_focus->type == N_WORKSPACE &&
174 !workspace_is_visible(next_focus->sway_workspace)) {
175 // Do not change focus to a non-visible workspace
176 return;
177 }
178
178 if (needs_new_focus) { 179 if (needs_new_focus) {
179 // The structure change might have caused it to move up to the top of 180 // The structure change might have caused it to move up to the top of
180 // the focus stack without sending focus notifications to the view 181 // the focus stack without sending focus notifications to the view
@@ -307,7 +308,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {
307 wl_list_insert(&root->drag_icons, &icon->link); 308 wl_list_insert(&root->drag_icons, &icon->link);
308 309
309 drag_icon_update_position(icon); 310 drag_icon_update_position(icon);
310 seat_end_mouse_operation(seat); 311 seatop_abort(seat);
311} 312}
312 313
313static void collect_focus_iter(struct sway_node *node, void *data) { 314static void collect_focus_iter(struct sway_node *node, void *data) {
@@ -387,6 +388,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
387 caps |= WL_SEAT_CAPABILITY_POINTER; 388 caps |= WL_SEAT_CAPABILITY_POINTER;
388 break; 389 break;
389 case WLR_INPUT_DEVICE_TABLET_PAD: 390 case WLR_INPUT_DEVICE_TABLET_PAD:
391 case WLR_INPUT_DEVICE_SWITCH:
390 break; 392 break;
391 } 393 }
392 } 394 }
@@ -403,6 +405,14 @@ static void seat_update_capabilities(struct sway_seat *seat) {
403 } 405 }
404} 406}
405 407
408static void seat_reset_input_config(struct sway_seat *seat,
409 struct sway_seat_device *sway_device) {
410 wlr_log(WLR_DEBUG, "Resetting output mapping for input device %s",
411 sway_device->input_device->identifier);
412 wlr_cursor_map_input_to_output(seat->cursor->cursor,
413 sway_device->input_device->wlr_device, NULL);
414}
415
406static void seat_apply_input_config(struct sway_seat *seat, 416static void seat_apply_input_config(struct sway_seat *seat,
407 struct sway_seat_device *sway_device) { 417 struct sway_seat_device *sway_device) {
408 const char *mapped_to_output = NULL; 418 const char *mapped_to_output = NULL;
@@ -422,7 +432,13 @@ static void seat_apply_input_config(struct sway_seat *seat,
422 if (mapped_to_output != NULL) { 432 if (mapped_to_output != NULL) {
423 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", 433 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",
424 sway_device->input_device->identifier, mapped_to_output); 434 sway_device->input_device->identifier, mapped_to_output);
425 struct sway_output *output = output_by_name(mapped_to_output); 435 if (strcmp("*", mapped_to_output) == 0) {
436 wlr_cursor_map_input_to_output(seat->cursor->cursor,
437 sway_device->input_device->wlr_device, NULL);
438 wlr_log(WLR_DEBUG, "Reset output mapping");
439 return;
440 }
441 struct sway_output *output = output_by_name_or_id(mapped_to_output);
426 if (output) { 442 if (output) {
427 wlr_cursor_map_input_to_output(seat->cursor->cursor, 443 wlr_cursor_map_input_to_output(seat->cursor->cursor,
428 sway_device->input_device->wlr_device, output->wlr_output); 444 sway_device->input_device->wlr_device, output->wlr_output);
@@ -508,6 +524,38 @@ void seat_configure_device(struct sway_seat *seat,
508 case WLR_INPUT_DEVICE_TABLET_PAD: 524 case WLR_INPUT_DEVICE_TABLET_PAD:
509 wlr_log(WLR_DEBUG, "TODO: configure tablet pad"); 525 wlr_log(WLR_DEBUG, "TODO: configure tablet pad");
510 break; 526 break;
527 case WLR_INPUT_DEVICE_SWITCH:
528 wlr_log(WLR_DEBUG, "TODO: configure switch device");
529 break;
530 }
531}
532
533void seat_reset_device(struct sway_seat *seat,
534 struct sway_input_device *input_device) {
535 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
536 if (!seat_device) {
537 return;
538 }
539
540 switch (input_device->wlr_device->type) {
541 case WLR_INPUT_DEVICE_POINTER:
542 seat_reset_input_config(seat, seat_device);
543 break;
544 case WLR_INPUT_DEVICE_KEYBOARD:
545 sway_keyboard_configure(seat_device->keyboard);
546 break;
547 case WLR_INPUT_DEVICE_TOUCH:
548 seat_reset_input_config(seat, seat_device);
549 break;
550 case WLR_INPUT_DEVICE_TABLET_TOOL:
551 seat_reset_input_config(seat, seat_device);
552 break;
553 case WLR_INPUT_DEVICE_TABLET_PAD:
554 wlr_log(WLR_DEBUG, "TODO: reset tablet pad");
555 break;
556 case WLR_INPUT_DEVICE_SWITCH:
557 wlr_log(WLR_DEBUG, "TODO: reset switch device");
558 break;
511 } 559 }
512} 560}
513 561
@@ -614,18 +662,6 @@ static int handle_urgent_timeout(void *data) {
614 return 0; 662 return 0;
615} 663}
616 664
617static void container_raise_floating(struct sway_container *con) {
618 // Bring container to front by putting it at the end of the floating list.
619 struct sway_container *floater = con;
620 while (floater->parent) {
621 floater = floater->parent;
622 }
623 if (container_is_floating(floater)) {
624 list_move_to_end(floater->workspace->floating, floater);
625 node_set_dirty(&floater->workspace->node);
626 }
627}
628
629static void set_workspace(struct sway_seat *seat, 665static void set_workspace(struct sway_seat *seat,
630 struct sway_workspace *new_ws) { 666 struct sway_workspace *new_ws) {
631 if (seat->workspace == new_ws) { 667 if (seat->workspace == new_ws) {
@@ -986,6 +1022,8 @@ void seat_apply_config(struct sway_seat *seat,
986 wl_list_for_each(seat_device, &seat->devices, link) { 1022 wl_list_for_each(seat_device, &seat->devices, link) {
987 seat_configure_device(seat, seat_device->input_device); 1023 seat_configure_device(seat, seat_device->input_device);
988 } 1024 }
1025
1026 cursor_handle_activity(seat->cursor);
989} 1027}
990 1028
991struct seat_config *seat_get_config(struct sway_seat *seat) { 1029struct seat_config *seat_get_config(struct sway_seat *seat) {
@@ -1000,174 +1038,16 @@ struct seat_config *seat_get_config(struct sway_seat *seat) {
1000 return NULL; 1038 return NULL;
1001} 1039}
1002 1040
1003void seat_begin_down(struct sway_seat *seat, struct sway_container *con, 1041struct seat_config *seat_get_config_by_name(const char *name) {
1004 uint32_t button, double sx, double sy) { 1042 struct seat_config *seat_config = NULL;
1005 seat->operation = OP_DOWN; 1043 for (int i = 0; i < config->seat_configs->length; ++i ) {
1006 seat->op_container = con; 1044 seat_config = config->seat_configs->items[i];
1007 seat->op_button = button; 1045 if (strcmp(name, seat_config->name) == 0) {
1008 seat->op_ref_lx = seat->cursor->cursor->x; 1046 return seat_config;
1009 seat->op_ref_ly = seat->cursor->cursor->y;
1010 seat->op_ref_con_lx = sx;
1011 seat->op_ref_con_ly = sy;
1012 seat->op_moved = false;
1013
1014 container_raise_floating(con);
1015}
1016
1017void seat_begin_move_floating(struct sway_seat *seat,
1018 struct sway_container *con, uint32_t button) {
1019 if (!seat->cursor) {
1020 wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device");
1021 return;
1022 }
1023 seat->operation = OP_MOVE_FLOATING;
1024 seat->op_container = con;
1025 seat->op_button = button;
1026
1027 container_raise_floating(con);
1028
1029 cursor_set_image(seat->cursor, "grab", NULL);
1030}
1031
1032void seat_begin_move_tiling(struct sway_seat *seat,
1033 struct sway_container *con, uint32_t button) {
1034 seat->operation = OP_MOVE_TILING;
1035 seat->op_container = con;
1036 seat->op_button = button;
1037 seat->op_target_node = NULL;
1038 seat->op_target_edge = 0;
1039 cursor_set_image(seat->cursor, "grab", NULL);
1040}
1041
1042void seat_begin_resize_floating(struct sway_seat *seat,
1043 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
1044 if (!seat->cursor) {
1045 wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device");
1046 return;
1047 }
1048 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
1049 seat->operation = OP_RESIZE_FLOATING;
1050 seat->op_container = con;
1051 seat->op_resize_preserve_ratio = keyboard &&
1052 (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
1053 seat->op_resize_edge = edge == WLR_EDGE_NONE ?
1054 WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
1055 seat->op_button = button;
1056 seat->op_ref_lx = seat->cursor->cursor->x;
1057 seat->op_ref_ly = seat->cursor->cursor->y;
1058 seat->op_ref_con_lx = con->x;
1059 seat->op_ref_con_ly = con->y;
1060 seat->op_ref_width = con->width;
1061 seat->op_ref_height = con->height;
1062
1063 container_raise_floating(con);
1064
1065 const char *image = edge == WLR_EDGE_NONE ?
1066 "se-resize" : wlr_xcursor_get_resize_name(edge);
1067 cursor_set_image(seat->cursor, image, NULL);
1068}
1069
1070void seat_begin_resize_tiling(struct sway_seat *seat,
1071 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
1072 seat->operation = OP_RESIZE_TILING;
1073 seat->op_container = con;
1074 seat->op_resize_edge = edge;
1075 seat->op_button = button;
1076 seat->op_ref_lx = seat->cursor->cursor->x;
1077 seat->op_ref_ly = seat->cursor->cursor->y;
1078 seat->op_ref_con_lx = con->x;
1079 seat->op_ref_con_ly = con->y;
1080 seat->op_ref_width = con->width;
1081 seat->op_ref_height = con->height;
1082}
1083
1084static bool is_parallel(enum sway_container_layout layout,
1085 enum wlr_edges edge) {
1086 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
1087 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
1088 return layout_is_horiz == edge_is_horiz;
1089}
1090
1091static void seat_end_move_tiling(struct sway_seat *seat) {
1092 struct sway_container *con = seat->op_container;
1093 struct sway_container *old_parent = con->parent;
1094 struct sway_workspace *old_ws = con->workspace;
1095 struct sway_node *target_node = seat->op_target_node;
1096 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
1097 target_node->sway_workspace : target_node->sway_container->workspace;
1098 enum wlr_edges edge = seat->op_target_edge;
1099 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
1100
1101 container_detach(con);
1102
1103 // Moving container into empty workspace
1104 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
1105 workspace_add_tiling(new_ws, con);
1106 } else if (target_node->type == N_CONTAINER) {
1107 // Moving container before/after another
1108 struct sway_container *target = target_node->sway_container;
1109 enum sway_container_layout layout = container_parent_layout(target);
1110 if (edge && !is_parallel(layout, edge)) {
1111 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
1112 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
1113 container_split(target, new_layout);
1114 }
1115 container_add_sibling(target, con, after);
1116 } else {
1117 // Target is a workspace which requires splitting
1118 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
1119 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
1120 workspace_split(new_ws, new_layout);
1121 workspace_insert_tiling(new_ws, con, after);
1122 }
1123
1124 if (old_parent) {
1125 container_reap_empty(old_parent);
1126 }
1127
1128 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
1129 // I don't think there's any other way to make it consistent without
1130 // changing how we auto-size containers.
1131 list_t *siblings = container_get_siblings(con);
1132 if (siblings->length > 1) {
1133 int index = list_find(siblings, con);
1134 struct sway_container *sibling = index == 0 ?
1135 siblings->items[1] : siblings->items[index - 1];
1136 con->width = sibling->width;
1137 con->height = sibling->height;
1138 }
1139
1140 arrange_workspace(old_ws);
1141 if (new_ws != old_ws) {
1142 arrange_workspace(new_ws);
1143 }
1144}
1145
1146void seat_end_mouse_operation(struct sway_seat *seat) {
1147 enum sway_seat_operation operation = seat->operation;
1148 if (seat->operation == OP_MOVE_FLOATING) {
1149 // We "move" the container to its own location so it discovers its
1150 // output again.
1151 struct sway_container *con = seat->op_container;
1152 container_floating_move_to(con, con->x, con->y);
1153 } else if (seat->operation == OP_MOVE_TILING && seat->op_target_node) {
1154 seat_end_move_tiling(seat);
1155 }
1156 seat->operation = OP_NONE;
1157 seat->op_container = NULL;
1158 if (operation == OP_DOWN) {
1159 // Set the cursor's previous coords to the x/y at the start of the
1160 // operation, so the container change will be detected if using
1161 // focus_follows_mouse and the cursor moved off the original container
1162 // during the operation.
1163 seat->cursor->previous.x = seat->op_ref_lx;
1164 seat->cursor->previous.y = seat->op_ref_ly;
1165 if (seat->op_moved) {
1166 cursor_send_pointer_motion(seat->cursor, 0);
1167 } 1047 }
1168 } else {
1169 cursor_set_image(seat->cursor, "left_ptr", NULL);
1170 } 1048 }
1049
1050 return NULL;
1171} 1051}
1172 1052
1173void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 1053void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
@@ -1197,4 +1077,49 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) {
1197 } else { 1077 } else {
1198 cursor_warp_to_workspace(seat->cursor, focus->sway_workspace); 1078 cursor_warp_to_workspace(seat->cursor, focus->sway_workspace);
1199 } 1079 }
1080 if (seat->cursor->hidden){
1081 cursor_unhide(seat->cursor);
1082 wl_event_source_timer_update(seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
1083 }
1084}
1085
1086bool seat_doing_seatop(struct sway_seat *seat) {
1087 return seat->seatop_impl != NULL;
1088}
1089
1090void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1091 if (seat->seatop_impl && seat->seatop_impl->unref) {
1092 seat->seatop_impl->unref(seat, con);
1093 }
1094}
1095
1096void seatop_motion(struct sway_seat *seat, uint32_t time_msec) {
1097 if (seat->seatop_impl && seat->seatop_impl->motion) {
1098 seat->seatop_impl->motion(seat, time_msec);
1099 }
1100}
1101
1102void seatop_finish(struct sway_seat *seat) {
1103 if (seat->seatop_impl && seat->seatop_impl->finish) {
1104 seat->seatop_impl->finish(seat);
1105 }
1106 free(seat->seatop_data);
1107 seat->seatop_data = NULL;
1108 seat->seatop_impl = NULL;
1109}
1110
1111void seatop_abort(struct sway_seat *seat) {
1112 if (seat->seatop_impl && seat->seatop_impl->abort) {
1113 seat->seatop_impl->abort(seat);
1114 }
1115 free(seat->seatop_data);
1116 seat->seatop_data = NULL;
1117 seat->seatop_impl = NULL;
1118}
1119
1120void seatop_render(struct sway_seat *seat, struct sway_output *output,
1121 pixman_region32_t *damage) {
1122 if (seat->seatop_impl && seat->seatop_impl->render) {
1123 seat->seatop_impl->render(seat, output, damage);
1124 }
1200} 1125}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
new file mode 100644
index 00000000..ad11c5ca
--- /dev/null
+++ b/sway/input/seatop_down.c
@@ -0,0 +1,77 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/input/cursor.h"
4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6
7struct seatop_down_event {
8 struct sway_container *con;
9 double ref_lx, ref_ly; // cursor's x/y at start of op
10 double ref_con_lx, ref_con_ly; // container's x/y at start of op
11 bool moved;
12};
13
14static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
15 struct seatop_down_event *e = seat->seatop_data;
16 struct sway_container *con = e->con;
17 if (seat_is_input_allowed(seat, con->view->surface)) {
18 double moved_x = seat->cursor->cursor->x - e->ref_lx;
19 double moved_y = seat->cursor->cursor->y - e->ref_ly;
20 double sx = e->ref_con_lx + moved_x;
21 double sy = e->ref_con_ly + moved_y;
22 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
23 }
24 e->moved = true;
25}
26
27static void handle_finish(struct sway_seat *seat) {
28 struct seatop_down_event *e = seat->seatop_data;
29 // Set the cursor's previous coords to the x/y at the start of the
30 // operation, so the container change will be detected if using
31 // focus_follows_mouse and the cursor moved off the original container
32 // during the operation.
33 seat->cursor->previous.x = e->ref_lx;
34 seat->cursor->previous.y = e->ref_ly;
35 if (e->moved) {
36 cursor_send_pointer_motion(seat->cursor, 0);
37 }
38}
39
40static void handle_abort(struct sway_seat *seat) {
41 cursor_set_image(seat->cursor, "left_ptr", NULL);
42}
43
44static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
45 struct seatop_down_event *e = seat->seatop_data;
46 if (e->con == con) {
47 seatop_abort(seat);
48 }
49}
50
51static const struct sway_seatop_impl seatop_impl = {
52 .motion = handle_motion,
53 .finish = handle_finish,
54 .abort = handle_abort,
55 .unref = handle_unref,
56};
57
58void seatop_begin_down(struct sway_seat *seat,
59 struct sway_container *con, uint32_t button, int sx, int sy) {
60 seatop_abort(seat);
61
62 struct seatop_down_event *e =
63 calloc(1, sizeof(struct seatop_down_event));
64 if (!e) {
65 return;
66 }
67 e->con = con;
68 e->ref_lx = seat->cursor->cursor->x;
69 e->ref_ly = seat->cursor->cursor->y;
70 e->ref_con_lx = sx;
71 e->ref_con_ly = sy;
72 e->moved = false;
73
74 seat->seatop_impl = &seatop_impl;
75 seat->seatop_data = e;
76 seat->seatop_button = button;
77}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
new file mode 100644
index 00000000..08e3a5a4
--- /dev/null
+++ b/sway/input/seatop_move_floating.c
@@ -0,0 +1,65 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h"
4#include "sway/input/cursor.h"
5#include "sway/input/seat.h"
6
7struct seatop_move_floating_event {
8 struct sway_container *con;
9};
10
11static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
12 struct seatop_move_floating_event *e = seat->seatop_data;
13 desktop_damage_whole_container(e->con);
14 container_floating_translate(e->con,
15 seat->cursor->cursor->x - seat->cursor->previous.x,
16 seat->cursor->cursor->y - seat->cursor->previous.y);
17 desktop_damage_whole_container(e->con);
18}
19
20static void handle_finish(struct sway_seat *seat) {
21 struct seatop_move_floating_event *e = seat->seatop_data;
22
23 // We "move" the container to its own location
24 // so it discovers its output again.
25 container_floating_move_to(e->con, e->con->x, e->con->y);
26 cursor_set_image(seat->cursor, "left_ptr", NULL);
27}
28
29static void handle_abort(struct sway_seat *seat) {
30 cursor_set_image(seat->cursor, "left_ptr", NULL);
31}
32
33static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
34 struct seatop_move_floating_event *e = seat->seatop_data;
35 if (e->con == con) {
36 seatop_abort(seat);
37 }
38}
39
40static const struct sway_seatop_impl seatop_impl = {
41 .motion = handle_motion,
42 .finish = handle_finish,
43 .abort = handle_abort,
44 .unref = handle_unref,
45};
46
47void seatop_begin_move_floating(struct sway_seat *seat,
48 struct sway_container *con, uint32_t button) {
49 seatop_abort(seat);
50
51 struct seatop_move_floating_event *e =
52 calloc(1, sizeof(struct seatop_move_floating_event));
53 if (!e) {
54 return;
55 }
56 e->con = con;
57
58 seat->seatop_impl = &seatop_impl;
59 seat->seatop_data = e;
60 seat->seatop_button = button;
61
62 container_raise_floating(con);
63
64 cursor_set_image(seat->cursor, "grab", NULL);
65}
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
new file mode 100644
index 00000000..8b541f80
--- /dev/null
+++ b/sway/input/seatop_move_tiling.c
@@ -0,0 +1,335 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h>
5#include "sway/desktop.h"
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/output.h"
9#include "sway/tree/arrange.h"
10#include "sway/tree/node.h"
11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
13
14// Thickness of the dropzone when dragging to the edge of a layout container
15#define DROP_LAYOUT_BORDER 30
16
17struct seatop_move_tiling_event {
18 struct sway_container *con;
19 struct sway_node *target_node;
20 enum wlr_edges target_edge;
21 struct wlr_box drop_box;
22 double ref_lx, ref_ly; // cursor's x/y at start of op
23 bool threshold_reached;
24};
25
26static void handle_render(struct sway_seat *seat,
27 struct sway_output *output, pixman_region32_t *damage) {
28 struct seatop_move_tiling_event *e = seat->seatop_data;
29 if (!e->threshold_reached) {
30 return;
31 }
32 if (e->target_node && node_get_output(e->target_node) == output) {
33 float color[4];
34 memcpy(&color, config->border_colors.focused.indicator,
35 sizeof(float) * 4);
36 premultiply_alpha(color, 0.5);
37 struct wlr_box box;
38 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
39 scale_box(&box, output->wlr_output->scale);
40 render_rect(output->wlr_output, damage, &box, color);
41 }
42}
43
44static void handle_motion_prethreshold(struct sway_seat *seat) {
45 struct seatop_move_tiling_event *e = seat->seatop_data;
46 double cx = seat->cursor->cursor->x;
47 double cy = seat->cursor->cursor->y;
48 double sx = e->ref_lx;
49 double sy = e->ref_ly;
50
51 // Get the scaled threshold for the output. Even if the operation goes
52 // across multiple outputs of varying scales, just use the scale for the
53 // output that the cursor is currently on for simplicity.
54 struct wlr_output *wlr_output = wlr_output_layout_output_at(
55 root->output_layout, cx, cy);
56 double output_scale = wlr_output ? wlr_output->scale : 1;
57 double threshold = config->tiling_drag_threshold * output_scale;
58 threshold *= threshold;
59
60 // If the threshold has been exceeded, start the actual drag
61 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
62 e->threshold_reached = true;
63 cursor_set_image(seat->cursor, "grab", NULL);
64 }
65}
66
67static void resize_box(struct wlr_box *box, enum wlr_edges edge,
68 int thickness) {
69 switch (edge) {
70 case WLR_EDGE_TOP:
71 box->height = thickness;
72 break;
73 case WLR_EDGE_LEFT:
74 box->width = thickness;
75 break;
76 case WLR_EDGE_RIGHT:
77 box->x = box->x + box->width - thickness;
78 box->width = thickness;
79 break;
80 case WLR_EDGE_BOTTOM:
81 box->y = box->y + box->height - thickness;
82 box->height = thickness;
83 break;
84 case WLR_EDGE_NONE:
85 box->x += thickness;
86 box->y += thickness;
87 box->width -= thickness * 2;
88 box->height -= thickness * 2;
89 break;
90 }
91}
92
93static void handle_motion_postthreshold(struct sway_seat *seat) {
94 struct seatop_move_tiling_event *e = seat->seatop_data;
95 struct wlr_surface *surface = NULL;
96 double sx, sy;
97 struct sway_cursor *cursor = seat->cursor;
98 struct sway_node *node = node_at_coords(seat,
99 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
100 // Damage the old location
101 desktop_damage_box(&e->drop_box);
102
103 if (!node) {
104 // Eg. hovered over a layer surface such as swaybar
105 e->target_node = NULL;
106 e->target_edge = WLR_EDGE_NONE;
107 return;
108 }
109
110 if (node->type == N_WORKSPACE) {
111 // Emtpy workspace
112 e->target_node = node;
113 e->target_edge = WLR_EDGE_NONE;
114 workspace_get_box(node->sway_workspace, &e->drop_box);
115 desktop_damage_box(&e->drop_box);
116 return;
117 }
118
119 // Deny moving within own workspace if this is the only child
120 struct sway_container *con = node->sway_container;
121 if (workspace_num_tiling_views(e->con->workspace) == 1 &&
122 con->workspace == e->con->workspace) {
123 e->target_node = NULL;
124 e->target_edge = WLR_EDGE_NONE;
125 return;
126 }
127
128 // Traverse the ancestors, trying to find a layout container perpendicular
129 // to the edge. Eg. close to the top or bottom of a horiz layout.
130 while (con) {
131 enum wlr_edges edge = WLR_EDGE_NONE;
132 enum sway_container_layout layout = container_parent_layout(con);
133 struct wlr_box parent;
134 con->parent ? container_get_box(con->parent, &parent) :
135 workspace_get_box(con->workspace, &parent);
136 if (layout == L_HORIZ || layout == L_TABBED) {
137 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
138 edge = WLR_EDGE_TOP;
139 } else if (cursor->cursor->y > parent.y + parent.height
140 - DROP_LAYOUT_BORDER) {
141 edge = WLR_EDGE_BOTTOM;
142 }
143 } else if (layout == L_VERT || layout == L_STACKED) {
144 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
145 edge = WLR_EDGE_LEFT;
146 } else if (cursor->cursor->x > parent.x + parent.width
147 - DROP_LAYOUT_BORDER) {
148 edge = WLR_EDGE_RIGHT;
149 }
150 }
151 if (edge) {
152 e->target_node = node_get_parent(&con->node);
153 e->target_edge = edge;
154 node_get_box(e->target_node, &e->drop_box);
155 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
156 desktop_damage_box(&e->drop_box);
157 return;
158 }
159 con = con->parent;
160 }
161
162 // Use the hovered view - but we must be over the actual surface
163 con = node->sway_container;
164 if (!con->view->surface || node == &e->con->node) {
165 e->target_node = NULL;
166 e->target_edge = WLR_EDGE_NONE;
167 return;
168 }
169
170 // Find the closest edge
171 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
172 size_t closest_dist = INT_MAX;
173 size_t dist;
174 e->target_edge = WLR_EDGE_NONE;
175 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
176 closest_dist = dist;
177 e->target_edge = WLR_EDGE_TOP;
178 }
179 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
180 closest_dist = dist;
181 e->target_edge = WLR_EDGE_LEFT;
182 }
183 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
184 closest_dist = dist;
185 e->target_edge = WLR_EDGE_RIGHT;
186 }
187 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
188 closest_dist = dist;
189 e->target_edge = WLR_EDGE_BOTTOM;
190 }
191
192 if (closest_dist > thickness) {
193 e->target_edge = WLR_EDGE_NONE;
194 }
195
196 e->target_node = node;
197 e->drop_box.x = con->content_x;
198 e->drop_box.y = con->content_y;
199 e->drop_box.width = con->content_width;
200 e->drop_box.height = con->content_height;
201 resize_box(&e->drop_box, e->target_edge, thickness);
202 desktop_damage_box(&e->drop_box);
203}
204
205static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
206 struct seatop_move_tiling_event *e = seat->seatop_data;
207 if (e->threshold_reached) {
208 handle_motion_postthreshold(seat);
209 } else {
210 handle_motion_prethreshold(seat);
211 }
212}
213
214static void handle_abort(struct sway_seat *seat) {
215 cursor_set_image(seat->cursor, "left_ptr", NULL);
216}
217
218static bool is_parallel(enum sway_container_layout layout,
219 enum wlr_edges edge) {
220 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
221 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
222 return layout_is_horiz == edge_is_horiz;
223}
224
225static void handle_finish(struct sway_seat *seat) {
226 struct seatop_move_tiling_event *e = seat->seatop_data;
227
228 if (!e->target_node) {
229 handle_abort(seat);
230 return;
231 }
232
233 struct sway_container *con = e->con;
234 struct sway_container *old_parent = con->parent;
235 struct sway_workspace *old_ws = con->workspace;
236 struct sway_node *target_node = e->target_node;
237 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
238 target_node->sway_workspace : target_node->sway_container->workspace;
239 enum wlr_edges edge = e->target_edge;
240 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
241
242 container_detach(con);
243
244 // Moving container into empty workspace
245 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
246 workspace_add_tiling(new_ws, con);
247 } else if (target_node->type == N_CONTAINER) {
248 // Moving container before/after another
249 struct sway_container *target = target_node->sway_container;
250 enum sway_container_layout layout = container_parent_layout(target);
251 if (edge && !is_parallel(layout, edge)) {
252 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
253 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
254 container_split(target, new_layout);
255 }
256 container_add_sibling(target, con, after);
257 } else {
258 // Target is a workspace which requires splitting
259 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
260 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
261 workspace_split(new_ws, new_layout);
262 workspace_insert_tiling(new_ws, con, after);
263 }
264
265 if (old_parent) {
266 container_reap_empty(old_parent);
267 }
268
269 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
270 // I don't think there's any other way to make it consistent without
271 // changing how we auto-size containers.
272 list_t *siblings = container_get_siblings(con);
273 if (siblings->length > 1) {
274 int index = list_find(siblings, con);
275 struct sway_container *sibling = index == 0 ?
276 siblings->items[1] : siblings->items[index - 1];
277 con->width = sibling->width;
278 con->height = sibling->height;
279 }
280
281 arrange_workspace(old_ws);
282 if (new_ws != old_ws) {
283 arrange_workspace(new_ws);
284 }
285
286 cursor_set_image(seat->cursor, "left_ptr", NULL);
287}
288
289static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
290 struct seatop_move_tiling_event *e = seat->seatop_data;
291 if (e->target_node == &con->node) { // Drop target
292 e->target_node = NULL;
293 }
294 if (e->con == con) { // The container being moved
295 seatop_abort(seat);
296 }
297}
298
299static const struct sway_seatop_impl seatop_impl = {
300 .motion = handle_motion,
301 .finish = handle_finish,
302 .abort = handle_abort,
303 .unref = handle_unref,
304 .render = handle_render,
305};
306
307void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
308 struct sway_container *con, uint32_t button) {
309 seatop_abort(seat);
310
311 struct seatop_move_tiling_event *e =
312 calloc(1, sizeof(struct seatop_move_tiling_event));
313 if (!e) {
314 return;
315 }
316 e->con = con;
317 e->ref_lx = seat->cursor->cursor->x;
318 e->ref_ly = seat->cursor->cursor->y;
319
320 seat->seatop_impl = &seatop_impl;
321 seat->seatop_data = e;
322 seat->seatop_button = button;
323
324 container_raise_floating(con);
325}
326
327void seatop_begin_move_tiling(struct sway_seat *seat,
328 struct sway_container *con, uint32_t button) {
329 seatop_begin_move_tiling_threshold(seat, con, button);
330 struct seatop_move_tiling_event *e = seat->seatop_data;
331 if (e) {
332 e->threshold_reached = true;
333 cursor_set_image(seat->cursor, "grab", NULL);
334 }
335}
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
new file mode 100644
index 00000000..12851b40
--- /dev/null
+++ b/sway/input/seatop_resize_floating.c
@@ -0,0 +1,199 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h>
5#include "sway/input/cursor.h"
6#include "sway/input/seat.h"
7#include "sway/tree/arrange.h"
8#include "sway/tree/view.h"
9#include "sway/tree/workspace.h"
10
11struct seatop_resize_floating_event {
12 struct sway_container *con;
13 enum wlr_edges edge;
14 bool preserve_ratio;
15 double ref_lx, ref_ly; // cursor's x/y at start of op
16 double ref_width, ref_height; // container's size at start of op
17 double ref_con_lx, ref_con_ly; // container's x/y at start of op
18};
19
20static void calculate_floating_constraints(struct sway_container *con,
21 int *min_width, int *max_width, int *min_height, int *max_height) {
22 if (config->floating_minimum_width == -1) { // no minimum
23 *min_width = 0;
24 } else if (config->floating_minimum_width == 0) { // automatic
25 *min_width = 75;
26 } else {
27 *min_width = config->floating_minimum_width;
28 }
29
30 if (config->floating_minimum_height == -1) { // no minimum
31 *min_height = 0;
32 } else if (config->floating_minimum_height == 0) { // automatic
33 *min_height = 50;
34 } else {
35 *min_height = config->floating_minimum_height;
36 }
37
38 if (config->floating_maximum_width == -1) { // no maximum
39 *max_width = INT_MAX;
40 } else if (config->floating_maximum_width == 0) { // automatic
41 *max_width = con->workspace->width;
42 } else {
43 *max_width = config->floating_maximum_width;
44 }
45
46 if (config->floating_maximum_height == -1) { // no maximum
47 *max_height = INT_MAX;
48 } else if (config->floating_maximum_height == 0) { // automatic
49 *max_height = con->workspace->height;
50 } else {
51 *max_height = config->floating_maximum_height;
52 }
53}
54
55static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
56 struct seatop_resize_floating_event *e = seat->seatop_data;
57 struct sway_container *con = e->con;
58 enum wlr_edges edge = e->edge;
59 struct sway_cursor *cursor = seat->cursor;
60
61 // The amount the mouse has moved since the start of the resize operation
62 // Positive is down/right
63 double mouse_move_x = cursor->cursor->x - e->ref_lx;
64 double mouse_move_y = cursor->cursor->y - e->ref_ly;
65
66 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
67 mouse_move_x = 0;
68 }
69 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
70 mouse_move_y = 0;
71 }
72
73 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
74 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
75
76 if (e->preserve_ratio) {
77 double x_multiplier = grow_width / e->ref_width;
78 double y_multiplier = grow_height / e->ref_height;
79 double max_multiplier = fmax(x_multiplier, y_multiplier);
80 grow_width = e->ref_width * max_multiplier;
81 grow_height = e->ref_height * max_multiplier;
82 }
83
84 // Determine new width/height, and accommodate for floating min/max values
85 double width = e->ref_width + grow_width;
86 double height = e->ref_height + grow_height;
87 int min_width, max_width, min_height, max_height;
88 calculate_floating_constraints(con, &min_width, &max_width,
89 &min_height, &max_height);
90 width = fmax(min_width, fmin(width, max_width));
91 height = fmax(min_height, fmin(height, max_height));
92
93 // Apply the view's min/max size
94 if (con->view) {
95 double view_min_width, view_max_width, view_min_height, view_max_height;
96 view_get_constraints(con->view, &view_min_width, &view_max_width,
97 &view_min_height, &view_max_height);
98 width = fmax(view_min_width, fmin(width, view_max_width));
99 height = fmax(view_min_height, fmin(height, view_max_height));
100 }
101
102 // Recalculate these, in case we hit a min/max limit
103 grow_width = width - e->ref_width;
104 grow_height = height - e->ref_height;
105
106 // Determine grow x/y values - these are relative to the container's x/y at
107 // the start of the resize operation.
108 double grow_x = 0, grow_y = 0;
109 if (edge & WLR_EDGE_LEFT) {
110 grow_x = -grow_width;
111 } else if (edge & WLR_EDGE_RIGHT) {
112 grow_x = 0;
113 } else {
114 grow_x = -grow_width / 2;
115 }
116 if (edge & WLR_EDGE_TOP) {
117 grow_y = -grow_height;
118 } else if (edge & WLR_EDGE_BOTTOM) {
119 grow_y = 0;
120 } else {
121 grow_y = -grow_height / 2;
122 }
123
124 // Determine the amounts we need to bump everything relative to the current
125 // size.
126 int relative_grow_width = width - con->width;
127 int relative_grow_height = height - con->height;
128 int relative_grow_x = (e->ref_con_lx + grow_x) - con->x;
129 int relative_grow_y = (e->ref_con_ly + grow_y) - con->y;
130
131 // Actually resize stuff
132 con->x += relative_grow_x;
133 con->y += relative_grow_y;
134 con->width += relative_grow_width;
135 con->height += relative_grow_height;
136
137 con->content_x += relative_grow_x;
138 con->content_y += relative_grow_y;
139 con->content_width += relative_grow_width;
140 con->content_height += relative_grow_height;
141
142 arrange_container(con);
143}
144
145static void handle_finish(struct sway_seat *seat) {
146 cursor_set_image(seat->cursor, "left_ptr", NULL);
147}
148
149static void handle_abort(struct sway_seat *seat) {
150 cursor_set_image(seat->cursor, "left_ptr", NULL);
151}
152
153static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
154 struct seatop_resize_floating_event *e = seat->seatop_data;
155 if (e->con == con) {
156 seatop_abort(seat);
157 }
158}
159
160static const struct sway_seatop_impl seatop_impl = {
161 .motion = handle_motion,
162 .finish = handle_finish,
163 .abort = handle_abort,
164 .unref = handle_unref,
165};
166
167void seatop_begin_resize_floating(struct sway_seat *seat,
168 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
169 seatop_abort(seat);
170
171 struct seatop_resize_floating_event *e =
172 calloc(1, sizeof(struct seatop_resize_floating_event));
173 if (!e) {
174 return;
175 }
176 e->con = con;
177
178 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
179 e->preserve_ratio = keyboard &&
180 (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
181
182 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
183 e->ref_lx = seat->cursor->cursor->x;
184 e->ref_ly = seat->cursor->cursor->y;
185 e->ref_con_lx = con->x;
186 e->ref_con_ly = con->y;
187 e->ref_width = con->width;
188 e->ref_height = con->height;
189
190 seat->seatop_impl = &seatop_impl;
191 seat->seatop_data = e;
192 seat->seatop_button = button;
193
194 container_raise_floating(con);
195
196 const char *image = edge == WLR_EDGE_NONE ?
197 "se-resize" : wlr_xcursor_get_resize_name(edge);
198 cursor_set_image(seat->cursor, image, NULL);
199}
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
new file mode 100644
index 00000000..30431f04
--- /dev/null
+++ b/sway/input/seatop_resize_tiling.c
@@ -0,0 +1,92 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/commands.h"
4#include "sway/input/cursor.h"
5#include "sway/input/seat.h"
6
7struct seatop_resize_tiling_event {
8 struct sway_container *con;
9 enum wlr_edges edge;
10 double ref_lx, ref_ly; // cursor's x/y at start of op
11 double ref_width, ref_height; // container's size at start of op
12 double ref_con_lx, ref_con_ly; // container's x/y at start of op
13};
14
15static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
16 struct seatop_resize_tiling_event *e = seat->seatop_data;
17 int amount_x = 0;
18 int amount_y = 0;
19 int moved_x = seat->cursor->cursor->x - e->ref_lx;
20 int moved_y = seat->cursor->cursor->y - e->ref_ly;
21 enum wlr_edges edge_x = WLR_EDGE_NONE;
22 enum wlr_edges edge_y = WLR_EDGE_NONE;
23 struct sway_container *con = e->con;
24
25 if (e->edge & WLR_EDGE_TOP) {
26 amount_y = (e->ref_height - moved_y) - con->height;
27 edge_y = WLR_EDGE_TOP;
28 } else if (e->edge & WLR_EDGE_BOTTOM) {
29 amount_y = (e->ref_height + moved_y) - con->height;
30 edge_y = WLR_EDGE_BOTTOM;
31 }
32 if (e->edge & WLR_EDGE_LEFT) {
33 amount_x = (e->ref_width - moved_x) - con->width;
34 edge_x = WLR_EDGE_LEFT;
35 } else if (e->edge & WLR_EDGE_RIGHT) {
36 amount_x = (e->ref_width + moved_x) - con->width;
37 edge_x = WLR_EDGE_RIGHT;
38 }
39
40 if (amount_x != 0) {
41 container_resize_tiled(e->con, edge_x, amount_x);
42 }
43 if (amount_y != 0) {
44 container_resize_tiled(e->con, edge_y, amount_y);
45 }
46}
47
48static void handle_finish(struct sway_seat *seat) {
49 cursor_set_image(seat->cursor, "left_ptr", NULL);
50}
51
52static void handle_abort(struct sway_seat *seat) {
53 cursor_set_image(seat->cursor, "left_ptr", NULL);
54}
55
56static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
57 struct seatop_resize_tiling_event *e = seat->seatop_data;
58 if (e->con == con) {
59 seatop_abort(seat);
60 }
61}
62
63static const struct sway_seatop_impl seatop_impl = {
64 .motion = handle_motion,
65 .finish = handle_finish,
66 .abort = handle_abort,
67 .unref = handle_unref,
68};
69
70void seatop_begin_resize_tiling(struct sway_seat *seat,
71 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
72 seatop_abort(seat);
73
74 struct seatop_resize_tiling_event *e =
75 calloc(1, sizeof(struct seatop_resize_tiling_event));
76 if (!e) {
77 return;
78 }
79 e->con = con;
80 e->edge = edge;
81
82 e->ref_lx = seat->cursor->cursor->x;
83 e->ref_ly = seat->cursor->cursor->y;
84 e->ref_con_lx = con->x;
85 e->ref_con_ly = con->y;
86 e->ref_width = con->width;
87 e->ref_height = con->height;
88
89 seat->seatop_impl = &seatop_impl;
90 seat->seatop_data = e;
91 seat->seatop_button = button;
92}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index e3450df1..6e5ba4fd 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -1,6 +1,8 @@
1#include <json-c/json.h> 1#include <json-c/json.h>
2#include <libevdev/libevdev.h>
2#include <stdio.h> 3#include <stdio.h>
3#include <ctype.h> 4#include <ctype.h>
5#include "config.h"
4#include "log.h" 6#include "log.h"
5#include "sway/config.h" 7#include "sway/config.h"
6#include "sway/ipc-json.h" 8#include "sway/ipc-json.h"
@@ -9,12 +11,17 @@
9#include "sway/tree/workspace.h" 11#include "sway/tree/workspace.h"
10#include "sway/output.h" 12#include "sway/output.h"
11#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
14#include "sway/input/cursor.h"
12#include "sway/input/seat.h" 15#include "sway/input/seat.h"
16#include <wlr/backend/libinput.h>
13#include <wlr/types/wlr_box.h> 17#include <wlr/types/wlr_box.h>
14#include <wlr/types/wlr_output.h> 18#include <wlr/types/wlr_output.h>
15#include <xkbcommon/xkbcommon.h> 19#include <xkbcommon/xkbcommon.h>
16#include "wlr-layer-shell-unstable-v1-protocol.h" 20#include "wlr-layer-shell-unstable-v1-protocol.h"
17 21
22static const int i3_output_id = INT32_MAX;
23static const int i3_scratch_id = INT32_MAX - 1;
24
18static const char *ipc_json_layout_description(enum sway_container_layout l) { 25static const char *ipc_json_layout_description(enum sway_container_layout l) {
19 switch (l) { 26 switch (l) {
20 case L_VERT: 27 case L_VERT:
@@ -32,15 +39,68 @@ static const char *ipc_json_layout_description(enum sway_container_layout l) {
32} 39}
33 40
34static const char *ipc_json_orientation_description(enum sway_container_layout l) { 41static const char *ipc_json_orientation_description(enum sway_container_layout l) {
35 if (l == L_VERT) { 42 switch (l) {
43 case L_VERT:
36 return "vertical"; 44 return "vertical";
45 case L_HORIZ:
46 return "horizontal";
47 default:
48 return "none";
37 } 49 }
50}
38 51
39 if (l == L_HORIZ) { 52static const char *ipc_json_border_description(enum sway_container_border border) {
40 return "horizontal"; 53 switch (border) {
54 case B_NONE:
55 return "none";
56 case B_PIXEL:
57 return "pixel";
58 case B_NORMAL:
59 return "normal";
60 case B_CSD:
61 return "csd";
41 } 62 }
63 return "unknown";
64}
42 65
43 return "none"; 66static const char *ipc_json_output_transform_description(enum wl_output_transform transform) {
67 switch (transform) {
68 case WL_OUTPUT_TRANSFORM_NORMAL:
69 return "normal";
70 case WL_OUTPUT_TRANSFORM_90:
71 return "90";
72 case WL_OUTPUT_TRANSFORM_180:
73 return "180";
74 case WL_OUTPUT_TRANSFORM_270:
75 return "270";
76 case WL_OUTPUT_TRANSFORM_FLIPPED:
77 return "flipped";
78 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
79 return "flipped-90";
80 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
81 return "flipped-180";
82 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
83 return "flipped-270";
84 }
85 return NULL;
86}
87
88static const char *ipc_json_device_type_description(struct sway_input_device *device) {
89 switch (device->wlr_device->type) {
90 case WLR_INPUT_DEVICE_POINTER:
91 return "pointer";
92 case WLR_INPUT_DEVICE_KEYBOARD:
93 return "keyboard";
94 case WLR_INPUT_DEVICE_TOUCH:
95 return "touch";
96 case WLR_INPUT_DEVICE_TABLET_TOOL:
97 return "tablet_tool";
98 case WLR_INPUT_DEVICE_TABLET_PAD:
99 return "tablet_pad";
100 case WLR_INPUT_DEVICE_SWITCH:
101 return "switch";
102 }
103 return "unknown";
44} 104}
45 105
46json_object *ipc_json_get_version(void) { 106json_object *ipc_json_get_version(void) {
@@ -76,30 +136,43 @@ static json_object *ipc_json_create_empty_rect(void) {
76 return ipc_json_create_rect(&empty); 136 return ipc_json_create_rect(&empty);
77} 137}
78 138
79static void ipc_json_describe_root(struct sway_root *root, json_object *object) { 139static json_object *ipc_json_create_node(int id, char *name,
80 json_object_object_add(object, "type", json_object_new_string("root")); 140 bool focused, json_object *focus, struct wlr_box *box) {
141 json_object *object = json_object_new_object();
142
143 json_object_object_add(object, "id", json_object_new_int(id));
144 json_object_object_add(object, "name",
145 name ? json_object_new_string(name) : NULL);
146 json_object_object_add(object, "rect", ipc_json_create_rect(box));
147 json_object_object_add(object, "focused", json_object_new_boolean(focused));
148 json_object_object_add(object, "focus", focus);
149
150 // set default values to be compatible with i3
151 json_object_object_add(object, "border",
152 json_object_new_string(
153 ipc_json_border_description(B_NONE)));
154 json_object_object_add(object, "current_border_width",
155 json_object_new_int(0));
156 json_object_object_add(object, "layout",
157 json_object_new_string(
158 ipc_json_layout_description(L_HORIZ)));
159 json_object_object_add(object, "orientation",
160 json_object_new_string(
161 ipc_json_orientation_description(L_HORIZ)));
162 json_object_object_add(object, "percent", NULL);
163 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
164 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
165 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
166 json_object_object_add(object, "window", NULL);
167 json_object_object_add(object, "urgent", json_object_new_boolean(false));
168 json_object_object_add(object, "floating_nodes", json_object_new_array());
169 json_object_object_add(object, "sticky", json_object_new_boolean(false));
170
171 return object;
81} 172}
82 173
83static const char *ipc_json_get_output_transform(enum wl_output_transform transform) { 174static void ipc_json_describe_root(struct sway_root *root, json_object *object) {
84 switch (transform) { 175 json_object_object_add(object, "type", json_object_new_string("root"));
85 case WL_OUTPUT_TRANSFORM_NORMAL:
86 return "normal";
87 case WL_OUTPUT_TRANSFORM_90:
88 return "90";
89 case WL_OUTPUT_TRANSFORM_180:
90 return "180";
91 case WL_OUTPUT_TRANSFORM_270:
92 return "270";
93 case WL_OUTPUT_TRANSFORM_FLIPPED:
94 return "flipped";
95 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
96 return "flipped-90";
97 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
98 return "flipped-180";
99 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
100 return "flipped-270";
101 }
102 return NULL;
103} 176}
104 177
105static void ipc_json_describe_output(struct sway_output *output, 178static void ipc_json_describe_output(struct sway_output *output,
@@ -110,7 +183,8 @@ static void ipc_json_describe_output(struct sway_output *output,
110 json_object_object_add(object, "primary", json_object_new_boolean(false)); 183 json_object_object_add(object, "primary", json_object_new_boolean(false));
111 json_object_object_add(object, "layout", json_object_new_string("output")); 184 json_object_object_add(object, "layout", json_object_new_string("output"));
112 json_object_object_add(object, "orientation", 185 json_object_object_add(object, "orientation",
113 json_object_new_string(ipc_json_orientation_description(L_NONE))); 186 json_object_new_string(
187 ipc_json_orientation_description(L_NONE)));
114 json_object_object_add(object, "make", 188 json_object_object_add(object, "make",
115 json_object_new_string(wlr_output->make)); 189 json_object_new_string(wlr_output->make));
116 json_object_object_add(object, "model", 190 json_object_object_add(object, "model",
@@ -121,7 +195,7 @@ static void ipc_json_describe_output(struct sway_output *output,
121 json_object_new_double(wlr_output->scale)); 195 json_object_new_double(wlr_output->scale));
122 json_object_object_add(object, "transform", 196 json_object_object_add(object, "transform",
123 json_object_new_string( 197 json_object_new_string(
124 ipc_json_get_output_transform(wlr_output->transform))); 198 ipc_json_output_transform_description(wlr_output->transform)));
125 199
126 struct sway_workspace *ws = output_get_active_workspace(output); 200 struct sway_workspace *ws = output_get_active_workspace(output);
127 json_object_object_add(object, "current_workspace", 201 json_object_object_add(object, "current_workspace",
@@ -187,6 +261,52 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
187 return object; 261 return object;
188} 262}
189 263
264static json_object *ipc_json_describe_scratchpad_output(void) {
265 struct wlr_box box;
266 root_get_box(root, &box);
267
268 // Create focus stack for __i3_scratch workspace
269 json_object *workspace_focus = json_object_new_array();
270 for (int i = root->scratchpad->length - 1; i >= 0; --i) {
271 struct sway_container *container = root->scratchpad->items[i];
272 json_object_array_add(workspace_focus,
273 json_object_new_int(container->node.id));
274 }
275
276 json_object *workspace = ipc_json_create_node(i3_scratch_id,
277 "__i3_scratch", false, workspace_focus, &box);
278 json_object_object_add(workspace, "type",
279 json_object_new_string("workspace"));
280
281 // List all hidden scratchpad containers as floating nodes
282 json_object *floating_array = json_object_new_array();
283 for (int i = 0; i < root->scratchpad->length; ++i) {
284 struct sway_container *container = root->scratchpad->items[i];
285 if (!container->workspace) {
286 json_object_array_add(floating_array,
287 ipc_json_describe_node_recursive(&container->node));
288 }
289 }
290 json_object_object_add(workspace, "floating_nodes", floating_array);
291
292 // Create focus stack for __i3 output
293 json_object *output_focus = json_object_new_array();
294 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
295
296 json_object *output = ipc_json_create_node(i3_output_id,
297 "__i3", false, output_focus, &box);
298 json_object_object_add(output, "type",
299 json_object_new_string("output"));
300 json_object_object_add(output, "layout",
301 json_object_new_string("output"));
302
303 json_object *nodes = json_object_new_array();
304 json_object_array_add(nodes, workspace);
305 json_object_object_add(output, "nodes", nodes);
306
307 return output;
308}
309
190static void ipc_json_describe_workspace(struct sway_workspace *workspace, 310static void ipc_json_describe_workspace(struct sway_workspace *workspace,
191 json_object *object) { 311 json_object *object) {
192 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; 312 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
@@ -200,11 +320,12 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
200 json_object_object_add(object, "representation", workspace->representation ? 320 json_object_object_add(object, "representation", workspace->representation ?
201 json_object_new_string(workspace->representation) : NULL); 321 json_object_new_string(workspace->representation) : NULL);
202 322
203 const char *layout = ipc_json_layout_description(workspace->layout); 323 json_object_object_add(object, "layout",
204 json_object_object_add(object, "layout", json_object_new_string(layout)); 324 json_object_new_string(
205 325 ipc_json_layout_description(workspace->layout)));
206 const char *orientation = ipc_json_orientation_description(workspace->layout); 326 json_object_object_add(object, "orientation",
207 json_object_object_add(object, "orientation", json_object_new_string(orientation)); 327 json_object_new_string(
328 ipc_json_orientation_description(workspace->layout)));
208 329
209 // Floating 330 // Floating
210 json_object *floating_array = json_object_new_array(); 331 json_object *floating_array = json_object_new_array();
@@ -216,20 +337,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
216 json_object_object_add(object, "floating_nodes", floating_array); 337 json_object_object_add(object, "floating_nodes", floating_array);
217} 338}
218 339
219static const char *describe_container_border(enum sway_container_border border) {
220 switch (border) {
221 case B_NONE:
222 return "none";
223 case B_PIXEL:
224 return "pixel";
225 case B_NORMAL:
226 return "normal";
227 case B_CSD:
228 return "csd";
229 }
230 return "unknown";
231}
232
233static void ipc_json_describe_view(struct sway_container *c, json_object *object) { 340static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
234 json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); 341 json_object_object_add(object, "pid", json_object_new_int(c->view->pid));
235 342
@@ -307,15 +414,18 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
307 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 414 json_object_new_string(container_is_floating(c) ? "floating_con" : "con"));
308 415
309 json_object_object_add(object, "layout", 416 json_object_object_add(object, "layout",
310 json_object_new_string(ipc_json_layout_description(c->layout))); 417 json_object_new_string(
418 ipc_json_layout_description(c->layout)));
311 419
312 json_object_object_add(object, "orientation", 420 json_object_object_add(object, "orientation",
313 json_object_new_string(ipc_json_orientation_description(c->layout))); 421 json_object_new_string(
422 ipc_json_orientation_description(c->layout)));
314 423
315 bool urgent = c->view ? 424 bool urgent = c->view ?
316 view_is_urgent(c->view) : container_has_urgent_child(c); 425 view_is_urgent(c->view) : container_has_urgent_child(c);
317 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 426 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
318 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 427 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
428 json_object_object_add(object, "fullscreen_mode", json_object_new_int(c->is_fullscreen));
319 429
320 struct sway_node *parent = node_get_parent(&c->node); 430 struct sway_node *parent = node_get_parent(&c->node);
321 struct wlr_box parent_box = {0, 0, 0, 0}; 431 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -331,7 +441,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
331 } 441 }
332 442
333 json_object_object_add(object, "border", 443 json_object_object_add(object, "border",
334 json_object_new_string(describe_container_border(c->current.border))); 444 json_object_new_string(
445 ipc_json_border_description(c->current.border)));
335 json_object_object_add(object, "current_border_width", 446 json_object_object_add(object, "current_border_width",
336 json_object_new_int(c->current.border_thickness)); 447 json_object_new_int(c->current.border_thickness));
337 json_object_object_add(object, "floating_nodes", json_object_new_array()); 448 json_object_object_add(object, "floating_nodes", json_object_new_array());
@@ -372,17 +483,10 @@ static void focus_inactive_children_iterator(struct sway_node *node,
372json_object *ipc_json_describe_node(struct sway_node *node) { 483json_object *ipc_json_describe_node(struct sway_node *node) {
373 struct sway_seat *seat = input_manager_get_default_seat(); 484 struct sway_seat *seat = input_manager_get_default_seat();
374 bool focused = seat_get_focus(seat) == node; 485 bool focused = seat_get_focus(seat) == node;
375
376 json_object *object = json_object_new_object();
377 char *name = node_get_name(node); 486 char *name = node_get_name(node);
378 487
379 struct wlr_box box; 488 struct wlr_box box;
380 node_get_box(node, &box); 489 node_get_box(node, &box);
381 json_object_object_add(object, "id", json_object_new_int((int)node->id));
382 json_object_object_add(object, "name",
383 name ? json_object_new_string(name) : NULL);
384 json_object_object_add(object, "rect", ipc_json_create_rect(&box));
385 json_object_object_add(object, "focused", json_object_new_boolean(focused));
386 490
387 json_object *focus = json_object_new_array(); 491 json_object *focus = json_object_new_array();
388 struct focus_inactive_data data = { 492 struct focus_inactive_data data = {
@@ -390,24 +494,9 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
390 .object = focus, 494 .object = focus,
391 }; 495 };
392 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 496 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
393 json_object_object_add(object, "focus", focus);
394 497
395 // set default values to be compatible with i3 498 json_object *object = ipc_json_create_node(
396 json_object_object_add(object, "border", 499 (int)node->id, name, focused, focus, &box);
397 json_object_new_string(describe_container_border(B_NONE)));
398 json_object_object_add(object, "current_border_width", json_object_new_int(0));
399 json_object_object_add(object, "layout",
400 json_object_new_string(ipc_json_layout_description(L_HORIZ)));
401 json_object_object_add(object, "orientation",
402 json_object_new_string(ipc_json_orientation_description(L_HORIZ)));
403 json_object_object_add(object, "percent", NULL);
404 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
405 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
406 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
407 json_object_object_add(object, "window", NULL);
408 json_object_object_add(object, "urgent", json_object_new_boolean(false));
409 json_object_object_add(object, "floating_nodes", json_object_new_array());
410 json_object_object_add(object, "sticky", json_object_new_boolean(false));
411 500
412 switch (node->type) { 501 switch (node->type) {
413 case N_ROOT: 502 case N_ROOT:
@@ -434,6 +523,8 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
434 json_object *children = json_object_new_array(); 523 json_object *children = json_object_new_array();
435 switch (node->type) { 524 switch (node->type) {
436 case N_ROOT: 525 case N_ROOT:
526 json_object_array_add(children,
527 ipc_json_describe_scratchpad_output());
437 for (i = 0; i < root->outputs->length; ++i) { 528 for (i = 0; i < root->outputs->length; ++i) {
438 struct sway_output *output = root->outputs->items[i]; 529 struct sway_output *output = root->outputs->items[i];
439 json_object_array_add(children, 530 json_object_array_add(children,
@@ -470,22 +561,6 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
470 return object; 561 return object;
471} 562}
472 563
473static const char *describe_device_type(struct sway_input_device *device) {
474 switch (device->wlr_device->type) {
475 case WLR_INPUT_DEVICE_POINTER:
476 return "pointer";
477 case WLR_INPUT_DEVICE_KEYBOARD:
478 return "keyboard";
479 case WLR_INPUT_DEVICE_TOUCH:
480 return "touch";
481 case WLR_INPUT_DEVICE_TABLET_TOOL:
482 return "tablet_tool";
483 case WLR_INPUT_DEVICE_TABLET_PAD:
484 return "tablet_pad";
485 }
486 return "unknown";
487}
488
489json_object *ipc_json_describe_input(struct sway_input_device *device) { 564json_object *ipc_json_describe_input(struct sway_input_device *device) {
490 if (!(sway_assert(device, "Device must not be null"))) { 565 if (!(sway_assert(device, "Device must not be null"))) {
491 return NULL; 566 return NULL;
@@ -502,7 +577,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
502 json_object_object_add(object, "product", 577 json_object_object_add(object, "product",
503 json_object_new_int(device->wlr_device->product)); 578 json_object_new_int(device->wlr_device->product));
504 json_object_object_add(object, "type", 579 json_object_object_add(object, "type",
505 json_object_new_string(describe_device_type(device))); 580 json_object_new_string(
581 ipc_json_device_type_description(device)));
506 582
507 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { 583 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
508 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 584 struct wlr_keyboard *keyboard = device->wlr_device->keyboard;
@@ -525,6 +601,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
525 } 601 }
526 } 602 }
527 603
604 if (wlr_input_device_is_libinput(device->wlr_device)) {
605 struct libinput_device *libinput_dev;
606 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
607
608 const char *events = "unknown";
609 switch (libinput_device_config_send_events_get_mode(libinput_dev)) {
610 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
611 events = "enabled";
612 break;
613 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
614 events = "disabled_on_external_mouse";
615 break;
616 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
617 events = "disabled";
618 break;
619 }
620 json_object_object_add(object, "libinput_send_events",
621 json_object_new_string(events));
622 }
623
528 return object; 624 return object;
529} 625}
530 626
@@ -553,6 +649,31 @@ json_object *ipc_json_describe_seat(struct sway_seat *seat) {
553 return object; 649 return object;
554} 650}
555 651
652static uint32_t event_to_x11_button(uint32_t event) {
653 switch (event) {
654 case BTN_LEFT:
655 return 1;
656 case BTN_MIDDLE:
657 return 2;
658 case BTN_RIGHT:
659 return 3;
660 case SWAY_SCROLL_UP:
661 return 4;
662 case SWAY_SCROLL_DOWN:
663 return 5;
664 case SWAY_SCROLL_LEFT:
665 return 6;
666 case SWAY_SCROLL_RIGHT:
667 return 7;
668 case BTN_SIDE:
669 return 8;
670 case BTN_EXTRA:
671 return 9;
672 default:
673 return 0;
674 }
675}
676
556json_object *ipc_json_describe_bar_config(struct bar_config *bar) { 677json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
557 if (!sway_assert(bar, "Bar must not be NULL")) { 678 if (!sway_assert(bar, "Bar must not be NULL")) {
558 return NULL; 679 return NULL;
@@ -569,18 +690,36 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
569 json_object_new_string(bar->status_command) : NULL); 690 json_object_new_string(bar->status_command) : NULL);
570 json_object_object_add(json, "font", 691 json_object_object_add(json, "font",
571 json_object_new_string((bar->font) ? bar->font : config->font)); 692 json_object_new_string((bar->font) ? bar->font : config->font));
693
694 json_object *gaps = json_object_new_object();
695 json_object_object_add(gaps, "top",
696 json_object_new_int(bar->gaps.top));
697 json_object_object_add(gaps, "right",
698 json_object_new_int(bar->gaps.right));
699 json_object_object_add(gaps, "bottom",
700 json_object_new_int(bar->gaps.bottom));
701 json_object_object_add(gaps, "left",
702 json_object_new_int(bar->gaps.left));
703 json_object_object_add(json, "gaps", gaps);
704
572 if (bar->separator_symbol) { 705 if (bar->separator_symbol) {
573 json_object_object_add(json, "separator_symbol", 706 json_object_object_add(json, "separator_symbol",
574 json_object_new_string(bar->separator_symbol)); 707 json_object_new_string(bar->separator_symbol));
575 } 708 }
576 json_object_object_add(json, "bar_height", 709 json_object_object_add(json, "bar_height",
577 json_object_new_int(bar->height)); 710 json_object_new_int(bar->height));
711 json_object_object_add(json, "status_padding",
712 json_object_new_int(bar->status_padding));
713 json_object_object_add(json, "status_edge_padding",
714 json_object_new_int(bar->status_edge_padding));
578 json_object_object_add(json, "wrap_scroll", 715 json_object_object_add(json, "wrap_scroll",
579 json_object_new_boolean(bar->wrap_scroll)); 716 json_object_new_boolean(bar->wrap_scroll));
580 json_object_object_add(json, "workspace_buttons", 717 json_object_object_add(json, "workspace_buttons",
581 json_object_new_boolean(bar->workspace_buttons)); 718 json_object_new_boolean(bar->workspace_buttons));
582 json_object_object_add(json, "strip_workspace_numbers", 719 json_object_object_add(json, "strip_workspace_numbers",
583 json_object_new_boolean(bar->strip_workspace_numbers)); 720 json_object_new_boolean(bar->strip_workspace_numbers));
721 json_object_object_add(json, "strip_workspace_name",
722 json_object_new_boolean(bar->strip_workspace_name));
584 json_object_object_add(json, "binding_mode_indicator", 723 json_object_object_add(json, "binding_mode_indicator",
585 json_object_new_boolean(bar->binding_mode_indicator)); 724 json_object_new_boolean(bar->binding_mode_indicator));
586 json_object_object_add(json, "verbose", 725 json_object_object_add(json, "verbose",
@@ -680,6 +819,8 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
680 struct bar_binding *binding = bar->bindings->items[i]; 819 struct bar_binding *binding = bar->bindings->items[i];
681 json_object *bind = json_object_new_object(); 820 json_object *bind = json_object_new_object();
682 json_object_object_add(bind, "input_code", 821 json_object_object_add(bind, "input_code",
822 json_object_new_int(event_to_x11_button(binding->button)));
823 json_object_object_add(bind, "event_code",
683 json_object_new_int(binding->button)); 824 json_object_new_int(binding->button));
684 json_object_object_add(bind, "command", 825 json_object_object_add(bind, "command",
685 json_object_new_string(binding->command)); 826 json_object_new_string(binding->command));
@@ -699,5 +840,41 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
699 } 840 }
700 json_object_object_add(json, "outputs", outputs); 841 json_object_object_add(json, "outputs", outputs);
701 } 842 }
843#if HAVE_TRAY
844 // Add tray outputs if defined
845 if (bar->tray_outputs && bar->tray_outputs->length > 0) {
846 json_object *tray_outputs = json_object_new_array();
847 for (int i = 0; i < bar->tray_outputs->length; ++i) {
848 const char *name = bar->tray_outputs->items[i];
849 json_object_array_add(tray_outputs, json_object_new_string(name));
850 }
851 json_object_object_add(json, "tray_outputs", tray_outputs);
852 }
853
854 json_object *tray_bindings = json_object_new_array();
855 for (int i = 0; i < 10; ++i) {
856 if (bar->tray_bindings[i]) {
857 json_object *bind = json_object_new_object();
858 json_object_object_add(bind, "input_code",
859 json_object_new_int(i));
860 json_object_object_add(bind, "command",
861 json_object_new_string(bar->tray_bindings[i]));
862 json_object_array_add(tray_bindings, bind);
863 }
864 }
865 if (json_object_array_length(tray_bindings) > 0) {
866 json_object_object_add(json, "tray_bindings", tray_bindings);
867 } else {
868 json_object_put(tray_bindings);
869 }
870
871 if (bar->icon_theme) {
872 json_object_object_add(json, "icon_theme",
873 json_object_new_string(bar->icon_theme));
874 }
875
876 json_object_object_add(json, "tray_padding",
877 json_object_new_int(bar->tray_padding));
878#endif
702 return json; 879 return json;
703} 880}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 6466d263..ff1bc89f 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,10 +1,6 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#define _POSIX_C_SOURCE 200112L 2#define _POSIX_C_SOURCE 200112L
3#ifdef __linux__
4#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
5#elif __FreeBSD__
6#include <dev/evdev/input-event-codes.h>
7#endif
8#include <assert.h> 4#include <assert.h>
9#include <errno.h> 5#include <errno.h>
10#include <fcntl.h> 6#include <fcntl.h>
@@ -73,14 +69,11 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
73 unlink(ipc_sockaddr->sun_path); 69 unlink(ipc_sockaddr->sun_path);
74 70
75 while (ipc_client_list->length) { 71 while (ipc_client_list->length) {
76 struct ipc_client *client = ipc_client_list->items[0]; 72 ipc_client_disconnect(ipc_client_list->items[ipc_client_list->length-1]);
77 ipc_client_disconnect(client);
78 } 73 }
79 list_free(ipc_client_list); 74 list_free(ipc_client_list);
80 75
81 if (ipc_sockaddr) { 76 free(ipc_sockaddr);
82 free(ipc_sockaddr);
83 }
84 77
85 wl_list_remove(&ipc_display_destroy.link); 78 wl_list_remove(&ipc_display_destroy.link);
86} 79}
@@ -452,8 +445,12 @@ void ipc_event_binding(struct sway_binding *binding) {
452 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code)); 445 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
453 json_object_object_add(json_binding, "symbols", symbols); 446 json_object_object_add(json_binding, "symbols", symbols);
454 json_object_object_add(json_binding, "symbol", symbol); 447 json_object_object_add(json_binding, "symbol", symbol);
455 json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ? 448
456 json_object_new_string("mouse") : json_object_new_string("keyboard")); 449 bool mouse = binding->type == BINDING_MOUSECODE ||
450 binding->type == BINDING_MOUSESYM;
451 json_object_object_add(json_binding, "input_type", mouse
452 ? json_object_new_string("mouse")
453 : json_object_new_string("keyboard"));
457 454
458 json_object *json = json_object_new_object(); 455 json_object *json = json_object_new_object();
459 json_object_object_add(json, "change", json_object_new_string("run")); 456 json_object_object_add(json, "change", json_object_new_string("run"));
@@ -597,13 +594,18 @@ void ipc_client_handle_command(struct ipc_client *client) {
597 switch (client->current_command) { 594 switch (client->current_command) {
598 case IPC_COMMAND: 595 case IPC_COMMAND:
599 { 596 {
600 struct cmd_results *results = execute_command(buf, NULL, NULL); 597 list_t *res_list = execute_command(buf, NULL, NULL);
601 transaction_commit_dirty(); 598 transaction_commit_dirty();
602 char *json = cmd_results_to_json(results); 599 char *json = cmd_results_to_json(res_list);
603 int length = strlen(json); 600 int length = strlen(json);
604 client_valid = ipc_send_reply(client, json, (uint32_t)length); 601 client_valid = ipc_send_reply(client, json, (uint32_t)length);
605 free(json); 602 free(json);
606 free_cmd_results(results); 603 while (res_list->length) {
604 struct cmd_results *results = res_list->items[0];
605 free_cmd_results(results);
606 list_del(res_list, 0);
607 }
608 list_free(res_list);
607 goto exit_cleanup; 609 goto exit_cleanup;
608 } 610 }
609 611
@@ -619,8 +621,19 @@ void ipc_client_handle_command(struct ipc_client *client) {
619 json_object *outputs = json_object_new_array(); 621 json_object *outputs = json_object_new_array();
620 for (int i = 0; i < root->outputs->length; ++i) { 622 for (int i = 0; i < root->outputs->length; ++i) {
621 struct sway_output *output = root->outputs->items[i]; 623 struct sway_output *output = root->outputs->items[i];
622 json_object_array_add(outputs, 624 json_object *output_json = ipc_json_describe_node(&output->node);
623 ipc_json_describe_node(&output->node)); 625
626 // override the default focused indicator because it's set
627 // differently for the get_outputs reply
628 struct sway_seat *seat = input_manager_get_default_seat();
629 struct sway_workspace *focused_ws =
630 seat_get_focused_workspace(seat);
631 bool focused = focused_ws && output == focused_ws->output;
632 json_object_object_del(output_json, "focused");
633 json_object_object_add(output_json, "focused",
634 json_object_new_boolean(focused));
635
636 json_object_array_add(outputs, output_json);
624 } 637 }
625 struct sway_output *output; 638 struct sway_output *output;
626 wl_list_for_each(output, &root->all_outputs, link) { 639 wl_list_for_each(output, &root->all_outputs, link) {
@@ -651,8 +664,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
651 { 664 {
652 // TODO: Check if they're permitted to use these events 665 // TODO: Check if they're permitted to use these events
653 struct json_object *request = json_tokener_parse(buf); 666 struct json_object *request = json_tokener_parse(buf);
654 if (request == NULL) { 667 if (request == NULL || !json_object_is_type(request, json_type_array)) {
655 client_valid = ipc_send_reply(client, "{\"success\": false}", 18); 668 const char msg[] = "{\"success\": false}";
669 client_valid = ipc_send_reply(client, msg, strlen(msg));
656 wlr_log(WLR_INFO, "Failed to parse subscribe request"); 670 wlr_log(WLR_INFO, "Failed to parse subscribe request");
657 goto exit_cleanup; 671 goto exit_cleanup;
658 } 672 }
@@ -679,8 +693,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
679 client->subscribed_events |= event_mask(IPC_EVENT_TICK); 693 client->subscribed_events |= event_mask(IPC_EVENT_TICK);
680 is_tick = true; 694 is_tick = true;
681 } else { 695 } else {
682 client_valid = 696 const char msg[] = "{\"success\": false}";
683 ipc_send_reply(client, "{\"success\": false}", 18); 697 client_valid = ipc_send_reply(client, msg, strlen(msg));
684 json_object_put(request); 698 json_object_put(request);
685 wlr_log(WLR_INFO, "Unsupported event type in subscribe request"); 699 wlr_log(WLR_INFO, "Unsupported event type in subscribe request");
686 goto exit_cleanup; 700 goto exit_cleanup;
@@ -688,10 +702,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
688 } 702 }
689 703
690 json_object_put(request); 704 json_object_put(request);
691 client_valid = ipc_send_reply(client, "{\"success\": true}", 17); 705 const char msg[] = "{\"success\": true}";
706 client_valid = ipc_send_reply(client, msg, strlen(msg));
692 if (is_tick) { 707 if (is_tick) {
693 client->current_command = IPC_EVENT_TICK; 708 client->current_command = IPC_EVENT_TICK;
694 ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30); 709 const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}";
710 ipc_send_reply(client, tickmsg, strlen(tickmsg));
695 } 711 }
696 goto exit_cleanup; 712 goto exit_cleanup;
697 } 713 }
@@ -820,6 +836,14 @@ void ipc_client_handle_command(struct ipc_client *client) {
820 goto exit_cleanup; 836 goto exit_cleanup;
821 } 837 }
822 838
839 case IPC_SYNC:
840 {
841 // It was decided sway will not support this, just return success:false
842 const char msg[] = "{\"success\": false}";
843 ipc_send_reply(client, msg, strlen(msg));
844 goto exit_cleanup;
845 }
846
823 default: 847 default:
824 wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command); 848 wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command);
825 goto exit_cleanup; 849 goto exit_cleanup;
diff --git a/sway/main.c b/sway/main.c
index 86374163..6e3f6b67 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,9 +1,9 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#define _POSIX_C_SOURCE 200112L
3#include <getopt.h> 2#include <getopt.h>
4#include <pango/pangocairo.h> 3#include <pango/pangocairo.h>
5#include <signal.h> 4#include <signal.h>
6#include <stdbool.h> 5#include <stdbool.h>
6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <stdio.h> 8#include <stdio.h>
9#include <string.h> 9#include <string.h>
@@ -23,7 +23,6 @@
23#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
24#include "ipc-client.h" 24#include "ipc-client.h"
25#include "log.h" 25#include "log.h"
26#include "readline.h"
27#include "stringop.h" 26#include "stringop.h"
28#include "util.h" 27#include "util.h"
29 28
@@ -48,31 +47,28 @@ void detect_raspi(void) {
48 if (!f) { 47 if (!f) {
49 return; 48 return;
50 } 49 }
51 char *line; 50 char *line = NULL;
52 while(!feof(f)) { 51 size_t line_size = 0;
53 if (!(line = read_line(f))) { 52 while (getline(&line, &line_size, f) != -1) {
54 break;
55 }
56 if (strstr(line, "Raspberry Pi")) { 53 if (strstr(line, "Raspberry Pi")) {
57 raspi = true; 54 raspi = true;
55 break;
58 } 56 }
59 free(line);
60 } 57 }
61 fclose(f); 58 fclose(f);
62 FILE *g = fopen("/proc/modules", "r"); 59 FILE *g = fopen("/proc/modules", "r");
63 if (!g) { 60 if (!g) {
61 free(line);
64 return; 62 return;
65 } 63 }
66 bool vc4 = false; 64 bool vc4 = false;
67 while (!feof(g)) { 65 while (getline(&line, &line_size, g) != -1) {
68 if (!(line = read_line(g))) {
69 break;
70 }
71 if (strstr(line, "vc4")) { 66 if (strstr(line, "vc4")) {
72 vc4 = true; 67 vc4 = true;
68 break;
73 } 69 }
74 free(line);
75 } 70 }
71 free(line);
76 fclose(g); 72 fclose(g);
77 if (!vc4 && raspi) { 73 if (!vc4 && raspi) {
78 fprintf(stderr, "\x1B[1;31mWarning: You have a " 74 fprintf(stderr, "\x1B[1;31mWarning: You have a "
@@ -87,13 +83,10 @@ void detect_proprietary(int allow_unsupported_gpu) {
87 if (!f) { 83 if (!f) {
88 return; 84 return;
89 } 85 }
90 while (!feof(f)) { 86 char *line = NULL;
91 char *line; 87 size_t line_size = 0;
92 if (!(line = read_line(f))) { 88 while (getline(&line, &line_size, f) != -1) {
93 break;
94 }
95 if (strstr(line, "nvidia")) { 89 if (strstr(line, "nvidia")) {
96 free(line);
97 if (allow_unsupported_gpu) { 90 if (allow_unsupported_gpu) {
98 wlr_log(WLR_ERROR, 91 wlr_log(WLR_ERROR,
99 "!!! Proprietary Nvidia drivers are in use !!!"); 92 "!!! Proprietary Nvidia drivers are in use !!!");
@@ -107,7 +100,6 @@ void detect_proprietary(int allow_unsupported_gpu) {
107 break; 100 break;
108 } 101 }
109 if (strstr(line, "fglrx")) { 102 if (strstr(line, "fglrx")) {
110 free(line);
111 if (allow_unsupported_gpu) { 103 if (allow_unsupported_gpu) {
112 wlr_log(WLR_ERROR, 104 wlr_log(WLR_ERROR,
113 "!!! Proprietary AMD drivers are in use !!!"); 105 "!!! Proprietary AMD drivers are in use !!!");
@@ -119,8 +111,8 @@ void detect_proprietary(int allow_unsupported_gpu) {
119 } 111 }
120 break; 112 break;
121 } 113 }
122 free(line);
123 } 114 }
115 free(line);
124 fclose(f); 116 fclose(f);
125} 117}
126 118
@@ -147,6 +139,19 @@ static void log_env(void) {
147 } 139 }
148} 140}
149 141
142static void log_file(FILE *f) {
143 char *line = NULL;
144 size_t line_size = 0;
145 ssize_t nread;
146 while ((nread = getline(&line, &line_size, f)) != -1) {
147 if (line[nread - 1] == '\n') {
148 line[nread - 1] = '\0';
149 }
150 wlr_log(WLR_INFO, "%s", line);
151 }
152 free(line);
153}
154
150static void log_distro(void) { 155static void log_distro(void) {
151 const char *paths[] = { 156 const char *paths[] = {
152 "/etc/lsb-release", 157 "/etc/lsb-release",
@@ -159,16 +164,7 @@ static void log_distro(void) {
159 FILE *f = fopen(paths[i], "r"); 164 FILE *f = fopen(paths[i], "r");
160 if (f) { 165 if (f) {
161 wlr_log(WLR_INFO, "Contents of %s:", paths[i]); 166 wlr_log(WLR_INFO, "Contents of %s:", paths[i]);
162 while (!feof(f)) { 167 log_file(f);
163 char *line;
164 if (!(line = read_line(f))) {
165 break;
166 }
167 if (*line) {
168 wlr_log(WLR_INFO, "%s", line);
169 }
170 free(line);
171 }
172 fclose(f); 168 fclose(f);
173 } 169 }
174 } 170 }
@@ -180,16 +176,7 @@ static void log_kernel(void) {
180 wlr_log(WLR_INFO, "Unable to determine kernel version"); 176 wlr_log(WLR_INFO, "Unable to determine kernel version");
181 return; 177 return;
182 } 178 }
183 while (!feof(f)) { 179 log_file(f);
184 char *line;
185 if (!(line = read_line(f))) {
186 break;
187 }
188 if (*line) {
189 wlr_log(WLR_INFO, "%s", line);
190 }
191 free(line);
192 }
193 pclose(f); 180 pclose(f);
194} 181}
195 182
@@ -393,11 +380,15 @@ int main(int argc, char **argv) {
393 wlr_log(WLR_DEBUG, "Running deferred commands"); 380 wlr_log(WLR_DEBUG, "Running deferred commands");
394 while (config->cmd_queue->length) { 381 while (config->cmd_queue->length) {
395 char *line = config->cmd_queue->items[0]; 382 char *line = config->cmd_queue->items[0];
396 struct cmd_results *res = execute_command(line, NULL, NULL); 383 list_t *res_list = execute_command(line, NULL, NULL);
397 if (res->status != CMD_SUCCESS) { 384 for (int i = 0; i < res_list->length; ++i) {
398 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); 385 struct cmd_results *res = res_list->items[i];
386 if (res->status != CMD_SUCCESS) {
387 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error);
388 }
389 free_cmd_results(res);
399 } 390 }
400 free_cmd_results(res); 391 list_free(res_list);
401 free(line); 392 free(line);
402 list_del(config->cmd_queue, 0); 393 list_del(config->cmd_queue, 0);
403 } 394 }
diff --git a/sway/meson.build b/sway/meson.build
index 4524bac9..c2ed6298 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -23,6 +23,11 @@ sway_sources = files(
23 23
24 'input/input-manager.c', 24 'input/input-manager.c',
25 'input/seat.c', 25 'input/seat.c',
26 'input/seatop_down.c',
27 'input/seatop_move_floating.c',
28 'input/seatop_move_tiling.c',
29 'input/seatop_resize_floating.c',
30 'input/seatop_resize_tiling.c',
26 'input/cursor.c', 31 'input/cursor.c',
27 'input/keyboard.c', 32 'input/keyboard.c',
28 33
@@ -78,6 +83,7 @@ sway_sources = files(
78 'commands/seat/attach.c', 83 'commands/seat/attach.c',
79 'commands/seat/cursor.c', 84 'commands/seat/cursor.c',
80 'commands/seat/fallback.c', 85 'commands/seat/fallback.c',
86 'commands/seat/hide_cursor.c',
81 'commands/set.c', 87 'commands/set.c',
82 'commands/show_marks.c', 88 'commands/show_marks.c',
83 'commands/smart_borders.c', 89 'commands/smart_borders.c',
@@ -88,7 +94,11 @@ sway_sources = files(
88 'commands/swaynag_command.c', 94 'commands/swaynag_command.c',
89 'commands/swap.c', 95 'commands/swap.c',
90 'commands/tiling_drag.c', 96 'commands/tiling_drag.c',
97 'commands/tiling_drag_threshold.c',
98 'commands/title_align.c',
91 'commands/title_format.c', 99 'commands/title_format.c',
100 'commands/titlebar_border_thickness.c',
101 'commands/titlebar_padding.c',
92 'commands/unmark.c', 102 'commands/unmark.c',
93 'commands/urgent.c', 103 'commands/urgent.c',
94 'commands/workspace.c', 104 'commands/workspace.c',
@@ -96,12 +106,11 @@ sway_sources = files(
96 'commands/ws_auto_back_and_forth.c', 106 'commands/ws_auto_back_and_forth.c',
97 'commands/xwayland.c', 107 'commands/xwayland.c',
98 108
99 'commands/bar/activate_button.c', 109 'commands/bar/bind.c',
100 'commands/bar/binding_mode_indicator.c', 110 'commands/bar/binding_mode_indicator.c',
101 'commands/bar/bindsym.c',
102 'commands/bar/colors.c', 111 'commands/bar/colors.c',
103 'commands/bar/context_button.c',
104 'commands/bar/font.c', 112 'commands/bar/font.c',
113 'commands/bar/gaps.c',
105 'commands/bar/height.c', 114 'commands/bar/height.c',
106 'commands/bar/hidden_state.c', 115 'commands/bar/hidden_state.c',
107 'commands/bar/icon_theme.c', 116 'commands/bar/icon_theme.c',
@@ -111,11 +120,14 @@ sway_sources = files(
111 'commands/bar/output.c', 120 'commands/bar/output.c',
112 'commands/bar/pango_markup.c', 121 'commands/bar/pango_markup.c',
113 'commands/bar/position.c', 122 'commands/bar/position.c',
114 'commands/bar/secondary_button.c',
115 'commands/bar/separator_symbol.c', 123 'commands/bar/separator_symbol.c',
116 'commands/bar/status_command.c', 124 'commands/bar/status_command.c',
125 'commands/bar/status_edge_padding.c',
126 'commands/bar/status_padding.c',
117 'commands/bar/strip_workspace_numbers.c', 127 'commands/bar/strip_workspace_numbers.c',
128 'commands/bar/strip_workspace_name.c',
118 'commands/bar/swaybar_command.c', 129 'commands/bar/swaybar_command.c',
130 'commands/bar/tray_bindsym.c',
119 'commands/bar/tray_output.c', 131 'commands/bar/tray_output.c',
120 'commands/bar/tray_padding.c', 132 'commands/bar/tray_padding.c',
121 'commands/bar/workspace_buttons.c', 133 'commands/bar/workspace_buttons.c',
@@ -170,6 +182,7 @@ sway_deps = [
170 cairo, 182 cairo,
171 gdk_pixbuf, 183 gdk_pixbuf,
172 jsonc, 184 jsonc,
185 libevdev,
173 libinput, 186 libinput,
174 math, 187 math,
175 pango, 188 pango,
diff --git a/sway/security.c b/sway/security.c
index cc0d3f66..6a00229e 100644
--- a/sway/security.c
+++ b/sway/security.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include "sway/security.h" 4#include "sway/security.h"
diff --git a/sway/server.c b/sway/server.c
index 68142e87..0529cab1 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -7,25 +7,26 @@
7#include <wlr/backend/session.h> 7#include <wlr/backend/session.h>
8#include <wlr/render/wlr_renderer.h> 8#include <wlr/render/wlr_renderer.h>
9#include <wlr/types/wlr_compositor.h> 9#include <wlr/types/wlr_compositor.h>
10#include <wlr/types/wlr_data_control_v1.h>
10#include <wlr/types/wlr_export_dmabuf_v1.h> 11#include <wlr/types/wlr_export_dmabuf_v1.h>
11#include <wlr/types/wlr_gamma_control.h>
12#include <wlr/types/wlr_gamma_control_v1.h> 12#include <wlr/types/wlr_gamma_control_v1.h>
13#include <wlr/types/wlr_gamma_control.h>
14#include <wlr/types/wlr_gtk_primary_selection.h>
13#include <wlr/types/wlr_idle.h> 15#include <wlr/types/wlr_idle.h>
14#include <wlr/types/wlr_layer_shell_v1.h> 16#include <wlr/types/wlr_layer_shell_v1.h>
15#include <wlr/types/wlr_primary_selection.h>
16#include <wlr/types/wlr_screencopy_v1.h> 17#include <wlr/types/wlr_screencopy_v1.h>
17#include <wlr/types/wlr_server_decoration.h> 18#include <wlr/types/wlr_server_decoration.h>
18#include <wlr/types/wlr_xcursor_manager.h> 19#include <wlr/types/wlr_xcursor_manager.h>
19#include <wlr/types/wlr_xdg_decoration_v1.h> 20#include <wlr/types/wlr_xdg_decoration_v1.h>
20#include <wlr/types/wlr_xdg_output_v1.h> 21#include <wlr/types/wlr_xdg_output_v1.h>
21#include <wlr/util/log.h> 22#include <wlr/util/log.h>
23#include "config.h"
22#include "list.h" 24#include "list.h"
23#include "sway/config.h" 25#include "sway/config.h"
24#include "sway/desktop/idle_inhibit_v1.h" 26#include "sway/desktop/idle_inhibit_v1.h"
25#include "sway/input/input-manager.h" 27#include "sway/input/input-manager.h"
26#include "sway/server.h" 28#include "sway/server.h"
27#include "sway/tree/root.h" 29#include "sway/tree/root.h"
28#include "config.h"
29#if HAVE_XWAYLAND 30#if HAVE_XWAYLAND
30#include "sway/xwayland.h" 31#include "sway/xwayland.h"
31#endif 32#endif
@@ -57,7 +58,7 @@ bool server_init(struct sway_server *server) {
57 58
58 wlr_gamma_control_manager_create(server->wl_display); 59 wlr_gamma_control_manager_create(server->wl_display);
59 wlr_gamma_control_manager_v1_create(server->wl_display); 60 wlr_gamma_control_manager_v1_create(server->wl_display);
60 wlr_primary_selection_device_manager_create(server->wl_display); 61 wlr_gtk_primary_selection_device_manager_create(server->wl_display);
61 62
62 server->new_output.notify = handle_new_output; 63 server->new_output.notify = handle_new_output;
63 wl_signal_add(&server->backend->events.new_output, &server->new_output); 64 wl_signal_add(&server->backend->events.new_output, &server->new_output);
@@ -106,6 +107,7 @@ bool server_init(struct sway_server *server) {
106 107
107 wlr_export_dmabuf_manager_v1_create(server->wl_display); 108 wlr_export_dmabuf_manager_v1_create(server->wl_display);
108 wlr_screencopy_manager_v1_create(server->wl_display); 109 wlr_screencopy_manager_v1_create(server->wl_display);
110 wlr_data_control_manager_v1_create(server->wl_display);
109 111
110 server->socket = wl_display_add_socket_auto(server->wl_display); 112 server->socket = wl_display_add_socket_auto(server->wl_display);
111 if (!server->socket) { 113 if (!server->socket) {
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 873741c0..3f6b4298 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -50,6 +50,10 @@ Sway allows configuring swaybar in the sway configuration file.
50*workspace\_buttons* yes|no 50*workspace\_buttons* yes|no
51 Enables or disables workspace buttons on the bar. Default is _yes_. 51 Enables or disables workspace buttons on the bar. Default is _yes_.
52 52
53*strip\_workspace\_name* yes|no
54 If set to _yes_, then workspace names will be omitted from the workspace
55 button and only the custom number will be shown. Default is _no_.
56
53*strip\_workspace\_numbers* yes|no 57*strip\_workspace\_numbers* yes|no
54 If set to _yes_, then workspace numbers will be omitted from the workspace 58 If set to _yes_, then workspace numbers will be omitted from the workspace
55 button and only the custom name will be shown. Default is _no_. 59 button and only the custom name will be shown. Default is _no_.
@@ -57,13 +61,28 @@ Sway allows configuring swaybar in the sway configuration file.
57*binding\_mode\_indicator* yes|no 61*binding\_mode\_indicator* yes|no
58 Enable or disable binding mode indicator. Default is _yes_. 62 Enable or disable binding mode indicator. Default is _yes_.
59 63
64*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
65 Sets the gaps from the edge of the screen for the bar. Gaps can either be
66 set all at once, per direction, or per side. Note that only sides that
67 touch an edge of the screen can have gaps. For the side that does not
68 touch an edge of the screen, per-side outer gaps for workspaces may be of
69 use.
70
60*height* <height> 71*height* <height>
61 Sets the height of the bar. Default height will match the font size. 72 Sets the height of the bar. Default height (0) will match the font size.
73
74*bindcode* [--release] <event-code> <command>
75 Executes _command_ when the mouse button has been pressed (or if _released_
76 is given, when the button has been released). The buttons can be given as
77 an event code, which can be obtaining from `libinput debug-events`. To
78 disable the default behavior for a button, use the command _nop_.
62 79
63*bindsym* [--release] button<n> <command> 80*bindsym* [--release] button[1-9]|<event-name> <command>
64 Executes _command_ when mouse button _n_ has been pressed (or if _released_ 81 Executes _command_ when the mouse button has been pressed (or if _released_
65 is given, when mouse button _n_ has been released). To disable the default 82 is given, when the button has been released). The buttons can be given as a
66 behavior for a button, use the command _nop_. 83 x11 button number or an event name, which can be obtained from `libinput
84 debug-events`. To disable the default behavior for a button, use the
85 command _nop_.
67 86
68*mode* dock|hide|invisible 87*mode* dock|hide|invisible
69 Specifies the visibility of the bar. In _dock_ mode, it is permanently 88 Specifies the visibility of the bar. In _dock_ mode, it is permanently
@@ -81,6 +100,16 @@ Sway allows configuring swaybar in the sway configuration file.
81*modifier* <Modifier>|none 100*modifier* <Modifier>|none
82 Specifies the modifier key that shows a hidden bar. Default is _Mod4_. 101 Specifies the modifier key that shows a hidden bar. Default is _Mod4_.
83 102
103*status\_padding* <padding>
104 Sets the vertical padding that is used for the status line. The default is
105 _1_. If _padding_ is _0_, blocks will be able to take up the full height of
106 the bar. This value will be multiplied by the output scale.
107
108*status\_edge\_padding* <padding>
109 Sets the padding that is used when the status line is at the right edge of
110 the bar. This value will be multiplied by the output scale. The default is
111 _3_.
112
84## TRAY 113## TRAY
85 114
86Swaybar provides a system tray where third-party applications may place icons. 115Swaybar provides a system tray where third-party applications may place icons.
@@ -89,27 +118,20 @@ The following commands configure the tray.
89The _button_ argument in all cases is a platform-specific button code. On Linux 118The _button_ argument in all cases is a platform-specific button code. On Linux
90you can find a list of these at linux/input-event-codes.h. 119you can find a list of these at linux/input-event-codes.h.
91 120
92*activate\_button* <button> 121*tray\_bindsym* button<n> ContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop
93 Sets the button to be used for the _activate_ (primary click) tray item 122 Binds mouse button _n_ (1 to 9) to the specified action. Use the command
94 event. The default is BTN\_LEFT (0x110). 123 _nop_ to disable the default action (Activate for button 1, ContextMenu for
95 124 button 2 and SecondaryActivate for button 3).
96*context\_button* <button>
97 Sets the button to be used for the _context menu_ (right click) tray item
98 event. The default is BTN\_RIGHT (0x111).
99
100*secondary\_button* <button>
101 Sets the button to be used for the _secondary_ (middle click) tray item
102 event. The default is BTN\_MIDDLE (0x112).
103
104*tray\_output* none|all|<output>
105 Sets the output that the tray will appear on or none. Unlike i3bar, swaybar
106 is able to show icons on any number of bars and outputs without races.
107 The default is _all_.
108 125
109*tray\_padding* <px> [px] 126*tray\_padding* <px> [px]
110 Sets the pixel padding of the system tray. This padding will surround the 127 Sets the pixel padding of the system tray. This padding will surround the
111 tray on all sides and between each item. The default value for _px_ is 2. 128 tray on all sides and between each item. The default value for _px_ is 2.
112 129
130*tray\_output* none|<output>
131 Restrict the tray to a certain output, can be specified multiple times. If
132 omitted, the tray will be displayed on all outputs. Unlike i3bar, swaybar
133 can show icons on any number of bars and outputs without races.
134
113*icon\_theme* <name> 135*icon\_theme* <name>
114 Sets the icon theme that sway will look for item icons in. This option has 136 Sets the icon theme that sway will look for item icons in. This option has
115 no default value, because sway will always default to the fallback theme, 137 no default value, because sway will always default to the fallback theme,
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 45994644..c2673f2a 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -82,9 +82,12 @@ The following commands may only be used in the configuration file.
82*input* <identifier> dwt enabled|disabled 82*input* <identifier> dwt enabled|disabled
83 Enables or disables disable-while-typing for the specified input device. 83 Enables or disables disable-while-typing for the specified input device.
84 84
85*input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse 85*input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse|toggle
86 Enables or disables send\_events for specified input device. (Disabling 86 Enables or disables send\_events for specified input device. Disabling
87 send\_events disables the input device) 87 send\_events disables the input device. The _toggle_ option cannot be used
88 in the config. The order is enabled, disabled\_on\_external\_mouse,
89 disabled, (loop back to enabled). Any mode which is not supported by the
90 device will be skipped during the toggle.
88 91
89*input* <identifier> left\_handed enabled|disabled 92*input* <identifier> left\_handed enabled|disabled
90 Enables or disables left handed mode for specified input device. 93 Enables or disables left handed mode for specified input device.
@@ -105,10 +108,11 @@ The following commands may only be used in the configuration file.
105*input* <identifier> repeat\_rate <characters per second> 108*input* <identifier> repeat\_rate <characters per second>
106 Sets the frequency of key repeats once the repeat\_delay has passed. 109 Sets the frequency of key repeats once the repeat\_delay has passed.
107 110
108*input* <identifier> scroll\_button <button\_identifier> 111*input* <identifier> scroll\_button disable|button[1-3,8,9]|<event-code-or-name>
109 Sets button used for scroll\_method on\_button\_down. The button identifier 112 Sets the button used for scroll\_method on\_button\_down. The button can
110 can be obtained from `libinput debug-events`. 113 be given as an event name or code, which can be obtained from `libinput
111 If set to 0, it disables the scroll\_button on\_button\_down. 114 debug-events`, or as a x11 mouse button (button[1-3,8,9]). If set to
115 _disable_, it disables the scroll\_method on\_button\_down.
112 116
113*input* <identifier> scroll\_factor <floating point value> 117*input* <identifier> scroll\_factor <floating point value>
114 Changes the scroll factor for the specified input device. Scroll speed will 118 Changes the scroll factor for the specified input device. Scroll speed will
@@ -141,10 +145,29 @@ in their own "seat").
141 Attach an input device to this seat by its input identifier. A special 145 Attach an input device to this seat by its input identifier. A special
142 value of "\*" will attach all devices to the seat. 146 value of "\*" will attach all devices to the seat.
143 147
148*seat* <seat> cursor move|set <x> <y>
149 Move specified seat's cursor relative to current position or wrap to
150 absolute coordinates (with respect to the global coordinate space).
151 Specifying either value as 0 will not update that coordinate.
152
153*seat* <seat> cursor press|release button[1-9]|<event-name-or-code>
154 Simulate pressing (or releasing) the specified mouse button on the
155 specified seat. The button can either be provided as a button event name or
156 event code, which can be obtained from `libinput debug-events`, or as an x11
157 mouse button (button[1-9]). If using button[4-7], which map to axes, an axis
158 event will be simulated, however _press_ and _release_ will be ignored and
159 both will occur.
160
144*seat* <name> fallback true|false 161*seat* <name> fallback true|false
145 Set this seat as the fallback seat. A fallback seat will attach any device 162 Set this seat as the fallback seat. A fallback seat will attach any device
146 not explicitly attached to another seat (similar to a "default" seat). 163 not explicitly attached to another seat (similar to a "default" seat).
147 164
165*seat* <name> hide\_cursor <timeout>
166 Hides the cursor image after the specified _timeout_ (in milliseconds)
167 has elapsed with no activity on that cursor. A timeout of 0 (default)
168 disables hiding the cursor. The minimal timeout is 100 and any value less
169 than that (aside from 0), will be increased to 100.
170
148# SEE ALSO 171# SEE ALSO
149 172
150*sway*(5) *sway-output*(5) 173*sway*(5) *sway-output*(5)
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 37b7108b..28524478 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -36,7 +36,22 @@ must be separated by one space. For example:
36 36
37*output* <name> position|pos <X> <Y> 37*output* <name> position|pos <X> <Y>
38 Places the specified output at the specific position in the global 38 Places the specified output at the specific position in the global
39 coordinate space. 39 coordinate space. If scaling is active, it has to be considered when
40 positioning. For example, if the scaling factor for the left output is 2,
41 the relative position for the right output has to be divided by 2. The
42 reference point is the top left corner so if you want the bottoms aligned
43 this has to be considered as well.
44
45 Example:
46
47 output HDMI1 scale 2
48
49 output HDMI1 pos 0 1020 res 3200x1800
50
51 output eDP1 pos 1600 0 res 1920x1080
52
53 Note that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is
54 1020 + (1800 / 2) = 1920 = 0 + 1920
40 55
41*output* <name> scale <factor> 56*output* <name> scale <factor>
42 Scales the specified output by the specified scale _factor_. An integer is 57 Scales the specified output by the specified scale _factor_. An integer is
@@ -45,7 +60,8 @@ must be separated by one space. For example:
45 represent the contents of your windows - they will be rendered at the next 60 represent the contents of your windows - they will be rendered at the next
46 highest integral scale factor and downscaled. You may be better served by 61 highest integral scale factor and downscaled. You may be better served by
47 setting an integral scale factor and adjusting the font size of your 62 setting an integral scale factor and adjusting the font size of your
48 applications to taste. 63 applications to taste. HiDPI isn't supported with Xwayland clients (windows
64 will blur).
49 65
50*output* <name> background|bg <file> <mode> [<fallback\_color>] 66*output* <name> background|bg <file> <mode> [<fallback\_color>]
51 Sets the wallpaper for the given output to the specified file, using the 67 Sets the wallpaper for the given output to the specified file, using the
@@ -65,7 +81,7 @@ must be separated by one space. For example:
65 to apply a rotation and flip, or "normal" to apply no transform. If a single 81 to apply a rotation and flip, or "normal" to apply no transform. If a single
66 output is chosen and a rotation direction is specified 82 output is chosen and a rotation direction is specified
67 (_clockwise_ or _anticlockwise_) then the transform is added or 83 (_clockwise_ or _anticlockwise_) then the transform is added or
68 subtracted from the current tranform. 84 subtracted from the current transform.
69 85
70*output* <name> disable|enable 86*output* <name> disable|enable
71 Enables or disables the specified output (all outputs are enabled by 87 Enables or disables the specified output (all outputs are enabled by
diff --git a/sway/sway.1.scd b/sway/sway.1.scd
index f66c4cdb..09c8ccfd 100644
--- a/sway/sway.1.scd
+++ b/sway/sway.1.scd
@@ -71,18 +71,14 @@ with *i3-msg*(1) or even with *i3*(1).
71 71
72The following environment variables have an effect on sway: 72The following environment variables have an effect on sway:
73 73
74_SWAY\_CURSOR\_THEME_
75 Specifies the name of the cursor theme to use.
76
77_SWAY\_CURSOR\_SIZE_
78 Specifies the size of the cursor to use.
79
80_SWAYSOCK_ 74_SWAYSOCK_
81 Specifies the path to the sway IPC socket. 75 Specifies the path to the sway IPC socket.
82 76
83_XKB\_DEFAULT\_RULES_, _XKB\_DEFAULT\_MODEL_, _XKB\_DEFAULT\_LAYOUT_, 77_XKB\_DEFAULT\_RULES_, _XKB\_DEFAULT\_MODEL_, _XKB\_DEFAULT\_LAYOUT_,
84_XKB\_DEFAULT\_VARIANT_, _XKB\_DEFAULT\_OPTIONS_ 78_XKB\_DEFAULT\_VARIANT_, _XKB\_DEFAULT\_OPTIONS_
85 Configures the xkb keyboard settings. See *xkeyboard-config*(7). 79 Configures the xkb keyboard settings. See *xkeyboard-config*(7). The
80 preferred way to configure the keyboard is via the configuration file, see
81 *sway-input*(5).
86 82
87# AUTHORS 83# AUTHORS
88 84
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 95376ccc..06bc0dbf 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -134,8 +134,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
134*focus* mode\_toggle 134*focus* mode\_toggle
135 Moves focus between the floating and tiled layers. 135 Moves focus between the floating and tiled layers.
136 136
137*fullscreen* 137*fullscreen* [enable|disable|toggle]
138 Toggles fullscreen for the focused view. 138 Makes focused view fullscreen, non-fullscreen, or the opposite of what it
139 is now. If no argument is given, it does the same as _toggle_.
139 140
140*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current 141*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current
141set|plus|minus <amount> 142set|plus|minus <amount>
@@ -280,14 +281,23 @@ runtime.
280 281
281 for\_window <criteria> move container to output <output> 282 for\_window <criteria> move container to output <output>
282 283
283*bindsym* [--release|--locked] [--input-device=<device>] <key combo> <command> 284*bindsym* [--release|--locked] [--input-device=<device>] [--no-warn] <key combo> <command>
284 Binds _key combo_ to execute the sway command _command_ when pressed. You 285 Binds _key combo_ to execute the sway command _command_ when pressed. You
285 may use XKB key names here (*xev*(1) is a good tool for discovering these). 286 may use XKB key names here (*xev*(1) is a good tool for discovering these).
286 With the flag _--release_, the command is executed when the key combo is 287 With the flag _--release_, the command is executed when the key combo is
287 released. Unless the flag _--locked_ is set, the command will not be run 288 released. Unless the flag _--locked_ is set, the command will not be run
288 when a screen locking program is active. If _input-device_ is given, the 289 when a screen locking program is active. If _input-device_ is given, the
289 binding will only be executed for that input device and will be executed 290 binding will only be executed for that input device and will be executed
290 instead of any binding that is generic to all devices. 291 instead of any binding that is generic to all devices. By default, if you
292 overwrite a binding, swaynag will give you a warning. To silence this, use
293 the _--no-warn_ flag.
294
295 Mouse buttons can either be specified in the form _button[1-9]_ or by using
296 the name of the event code (ex _BTN\_LEFT_ or _BTN\_RIGHT_). For the former
297 option, the buttons will be mapped to their values in X11 (1=left, 2=middle,
298 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back,
299 9=forward). For the latter option, you can find the event names using
300 _libinput debug-events_.
291 301
292 Example: 302 Example:
293``` 303```
@@ -295,8 +305,8 @@ runtime.
295 bindsym Mod1+Shift+f exec firefox 305 bindsym Mod1+Shift+f exec firefox
296``` 306```
297 307
298 *bindcode* [--release|--locked] [--input-device=<device>] <code> <command> 308 *bindcode* [--release|--locked] [--input-device=<device>] [--no-warn] <code> <command>
299 is also available for binding with key codes instead of key names. 309 is also available for binding with key/button codes instead of key/button names.
300 310
301*client.<class>* <border> <background> <text> <indicator> <child\_border> 311*client.<class>* <border> <background> <text> <indicator> <child\_border>
302 Configures the color of window borders and title bars. All 5 colors are 312 Configures the color of window borders and title bars. All 5 colors are
@@ -321,7 +331,8 @@ runtime.
321 A view that does not have focus. 331 A view that does not have focus.
322 332
323 *client.urgent* 333 *client.urgent*
324 A view with an urgency hint. *Note*: This is not currently implemented. 334 A view with an urgency hint. *Note*: Native Wayland windows do not
335 support urgency. Urgency only works for Xwayland windows.
325 336
326 The meaning of each color is: 337 The meaning of each color is:
327 338
@@ -440,6 +451,16 @@ The default colors are:
440*font* <font> 451*font* <font>
441 Sets font for use in title bars in Pango format. 452 Sets font for use in title bars in Pango format.
442 453
454*titlebar\_border\_thickness* <thickness>
455 Thickness of the titlebar border in pixels
456
457*titlebar\_padding* <horizontal> [<vertical>]
458 Padding of the text in the titlebar. _horizontal_ value affects horizontal
459 padding of the text while _vertical_ value affects vertical padding (space
460 above and below text). Padding includes titlebar borders so their value
461 should be greater than titlebar\_border\_thickness. If _vertical_ value is
462 not specified it is set to the _horizontal_ value.
463
443*for\_window* <criteria> <command> 464*for\_window* <criteria> <command>
444 Whenever a window that matches _criteria_ appears, run list of commands. 465 Whenever a window that matches _criteria_ appears, run list of commands.
445 See *CRITERIA* for more details. 466 See *CRITERIA* for more details.
@@ -474,15 +495,6 @@ The default colors are:
474*seat* <seat> <seat-subcommands...> 495*seat* <seat> <seat-subcommands...>
475 For details on seat subcommands, see *sway-input*(5). 496 For details on seat subcommands, see *sway-input*(5).
476 497
477*seat* <seat> cursor move|set <x> <y>
478 Move specified seat's cursor relative to current position or wrap to
479 absolute coordinates (with respect to the global coordinate space).
480 Specifying either value as 0 will not update that coordinate.
481
482*seat* <seat> cursor press|release left|right|1|2|3...
483 Simulate pressing (or releasing) the specified mouse button on the
484 specified seat.
485
486*kill* 498*kill*
487 Kills (closes) the currently focused container and all of its children. 499 Kills (closes) the currently focused container and all of its children.
488 500
@@ -549,6 +561,26 @@ The default colors are:
549 Set the opacity of the window between 0 (completely transparent) and 1 561 Set the opacity of the window between 0 (completely transparent) and 1
550 (completely opaque). 562 (completely opaque).
551 563
564*tiling\_drag* enable|disable|toggle
565 Sets whether or not tiling containers can be dragged with the mouse. If
566 enabled (default), the _floating\_mod_ can be used to drag tiling, as well
567 as floating, containers. Using the left mouse button on title bars without
568 the _floating\_mod_ will also allow the container to be dragged. _toggle_
569 should not be used in the config file.
570
571*tiling\_drag\_threshold* <threshold>
572 Sets the threshold that must be exceeded for a container to be dragged by
573 its titlebar. This has no effect if _floating\_mod_ is used or if
574 _tiling\_drag_ is set to _disable_. Once the threshold has been exceeded
575 once, the drag starts and the cursor can come back inside the threshold
576 without stopping the drag. _threshold_ is multiplied by the scale of the
577 output that the cursor on. The default is 9.
578
579*title\_align* left|center|right
580 Sets the title alignment. If _right_ is selected and _show\_marks_ is set
581 to _yes_, the marks will be shown on the _left_ side instead of the
582 _right_ side.
583
552*unmark* [<identifier>] 584*unmark* [<identifier>]
553 *unmark* will remove _identifier_ from the list of current marks on a 585 *unmark* will remove _identifier_ from the list of current marks on a
554 window. If _identifier_ is omitted, all marks are removed. 586 window. If _identifier_ is omitted, all marks are removed.
diff --git a/sway/tree/container.c b/sway/tree/container.c
index cf6f5b54..d9c721f5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -21,6 +21,7 @@
21#include "sway/tree/arrange.h" 21#include "sway/tree/arrange.h"
22#include "sway/tree/view.h" 22#include "sway/tree/view.h"
23#include "sway/tree/workspace.h" 23#include "sway/tree/workspace.h"
24#include "list.h"
24#include "log.h" 25#include "log.h"
25#include "stringop.h" 26#include "stringop.h"
26 27
@@ -67,8 +68,7 @@ void container_destroy(struct sway_container *con) {
67 list_free(con->current.children); 68 list_free(con->current.children);
68 list_free(con->outputs); 69 list_free(con->outputs);
69 70
70 list_foreach(con->marks, free); 71 list_free_items_and_destroy(con->marks);
71 list_free(con->marks);
72 wlr_texture_destroy(con->marks_focused); 72 wlr_texture_destroy(con->marks_focused);
73 wlr_texture_destroy(con->marks_focused_inactive); 73 wlr_texture_destroy(con->marks_focused_inactive);
74 wlr_texture_destroy(con->marks_unfocused); 74 wlr_texture_destroy(con->marks_unfocused);
@@ -453,19 +453,26 @@ static void update_title_texture(struct sway_container *con,
453 int width = 0; 453 int width = 0;
454 int height = con->title_height * scale; 454 int height = con->title_height * scale;
455 455
456 cairo_t *c = cairo_create(NULL); 456 // We must use a non-nil cairo_t for cairo_set_font_options to work.
457 // Therefore, we cannot use cairo_create(NULL).
458 cairo_surface_t *dummy_surface = cairo_image_surface_create(
459 CAIRO_FORMAT_ARGB32, 0, 0);
460 cairo_t *c = cairo_create(dummy_surface);
461 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
462 cairo_font_options_t *fo = cairo_font_options_create();
463 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
464 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
465 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
466 cairo_set_font_options(c, fo);
457 get_text_size(c, config->font, &width, NULL, NULL, scale, 467 get_text_size(c, config->font, &width, NULL, NULL, scale,
458 config->pango_markup, "%s", con->formatted_title); 468 config->pango_markup, "%s", con->formatted_title);
469 cairo_surface_destroy(dummy_surface);
459 cairo_destroy(c); 470 cairo_destroy(c);
460 471
461 cairo_surface_t *surface = cairo_image_surface_create( 472 cairo_surface_t *surface = cairo_image_surface_create(
462 CAIRO_FORMAT_ARGB32, width, height); 473 CAIRO_FORMAT_ARGB32, width, height);
463 cairo_t *cairo = cairo_create(surface); 474 cairo_t *cairo = cairo_create(surface);
464 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 475 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
465 cairo_font_options_t *fo = cairo_font_options_create();
466 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
467 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
468 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
469 cairo_set_font_options(cairo, fo); 476 cairo_set_font_options(cairo, fo);
470 cairo_font_options_destroy(fo); 477 cairo_font_options_destroy(fo);
471 cairo_set_source_rgba(cairo, class->background[0], class->background[1], 478 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
@@ -594,7 +601,7 @@ void container_update_representation(struct sway_container *con) {
594} 601}
595 602
596size_t container_titlebar_height(void) { 603size_t container_titlebar_height(void) {
597 return config->font_height + TITLEBAR_V_PADDING * 2; 604 return config->font_height + config->titlebar_v_padding * 2;
598} 605}
599 606
600void container_init_floating(struct sway_container *con) { 607void container_init_floating(struct sway_container *con) {
@@ -857,15 +864,7 @@ bool container_has_urgent_child(struct sway_container *container) {
857void container_end_mouse_operation(struct sway_container *container) { 864void container_end_mouse_operation(struct sway_container *container) {
858 struct sway_seat *seat; 865 struct sway_seat *seat;
859 wl_list_for_each(seat, &server.input->seats, link) { 866 wl_list_for_each(seat, &server.input->seats, link) {
860 if (seat->op_container == container) { 867 seatop_unref(seat, container);
861 seat->op_target_node = NULL; // ensure tiling move doesn't apply
862 seat_end_mouse_operation(seat);
863 }
864 // If the user is doing a tiling drag over this container,
865 // keep the operation active but unset the target container.
866 if (seat->op_target_node == &container->node) {
867 seat->op_target_node = NULL;
868 }
869 } 868 }
870} 869}
871 870
@@ -979,7 +978,7 @@ void container_discover_outputs(struct sway_container *con) {
979 output_get_box(output, &output_box); 978 output_get_box(output, &output_box);
980 struct wlr_box intersection; 979 struct wlr_box intersection;
981 bool intersects = 980 bool intersects =
982 wlr_box_intersection(&con_box, &output_box, &intersection); 981 wlr_box_intersection(&intersection, &con_box, &output_box);
983 int index = list_find(con->outputs, output); 982 int index = list_find(con->outputs, output);
984 983
985 if (intersects && index == -1) { 984 if (intersects && index == -1) {
@@ -1267,7 +1266,9 @@ bool container_find_and_unmark(char *mark) {
1267} 1266}
1268 1267
1269void container_clear_marks(struct sway_container *con) { 1268void container_clear_marks(struct sway_container *con) {
1270 list_foreach(con->marks, free); 1269 for (int i = 0; i < con->marks->length; ++i) {
1270 free(con->marks->items[i]);
1271 }
1271 con->marks->length = 0; 1272 con->marks->length = 0;
1272 ipc_event_window(con, "mark"); 1273 ipc_event_window(con, "mark");
1273} 1274}
@@ -1375,3 +1376,16 @@ void container_update_marks_textures(struct sway_container *con) {
1375 &config->border_colors.urgent); 1376 &config->border_colors.urgent);
1376 container_damage_whole(con); 1377 container_damage_whole(con);
1377} 1378}
1379
1380void container_raise_floating(struct sway_container *con) {
1381 // Bring container to front by putting it at the end of the floating list.
1382 struct sway_container *floater = con;
1383 while (floater->parent) {
1384 floater = floater->parent;
1385 }
1386 if (container_is_floating(floater)) {
1387 list_move_to_end(floater->workspace->floating, floater);
1388 node_set_dirty(&floater->workspace->node);
1389 }
1390}
1391
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 3c4614a8..f24be010 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -58,6 +58,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
58 wlr_output->data = output; 58 wlr_output->data = output;
59 59
60 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 60 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
61 wl_signal_init(&output->events.destroy);
61 62
62 wl_list_insert(&root->all_outputs, &output->link); 63 wl_list_insert(&root->all_outputs, &output->link);
63 64
@@ -76,7 +77,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
76 for (size_t i = 0; i < len; ++i) { 77 for (size_t i = 0; i < len; ++i) {
77 wl_list_init(&output->layers[i]); 78 wl_list_init(&output->layers[i]);
78 } 79 }
79 wl_signal_init(&output->events.destroy);
80 80
81 output->enabled = true; 81 output->enabled = true;
82 list_add(root->outputs, output); 82 list_add(root->outputs, output);
@@ -88,11 +88,12 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
88 88
89 restore_workspaces(output); 89 restore_workspaces(output);
90 90
91 struct sway_workspace *ws = NULL;
91 if (!output->workspaces->length) { 92 if (!output->workspaces->length) {
92 // Create workspace 93 // Create workspace
93 char *ws_name = workspace_next_name(wlr_output->name); 94 char *ws_name = workspace_next_name(wlr_output->name);
94 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); 95 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
95 struct sway_workspace *ws = workspace_create(output, ws_name); 96 ws = workspace_create(output, ws_name);
96 // Set each seat's focus if not already set 97 // Set each seat's focus if not already set
97 struct sway_seat *seat = NULL; 98 struct sway_seat *seat = NULL;
98 wl_list_for_each(seat, &server.input->seats, link) { 99 wl_list_for_each(seat, &server.input->seats, link) {
@@ -104,9 +105,15 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
104 ipc_event_workspace(NULL, ws, "init"); 105 ipc_event_workspace(NULL, ws, "init");
105 } 106 }
106 107
107
108 apply_output_config(oc, output); 108 apply_output_config(oc, output);
109 109
110 if (ws && config->default_orientation == L_NONE) {
111 // Since the output transformation and resolution could have changed
112 // due to applying the output config, the previously set layout for the
113 // created workspace may not be correct for `default_orientation auto`
114 ws->layout = output_get_default_layout(output);
115 }
116
110 input_manager_configure_xcursor(); 117 input_manager_configure_xcursor();
111 118
112 wl_signal_add(&wlr_output->events.mode, &output->mode); 119 wl_signal_add(&wlr_output->events.mode, &output->mode);
@@ -218,6 +225,11 @@ void output_disable(struct sway_output *output) {
218 225
219 root_for_each_container(untrack_output, output); 226 root_for_each_container(untrack_output, output);
220 227
228 if (output->bg_pid) {
229 terminate_swaybg(output->bg_pid);
230 output->bg_pid = 0;
231 }
232
221 int index = list_find(root->outputs, output); 233 int index = list_find(root->outputs, output);
222 list_del(root->outputs, index); 234 list_del(root->outputs, index);
223 235
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 544d666a..e1624863 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -5,6 +5,7 @@
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include "sway/desktop/transaction.h" 6#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h" 7#include "sway/input/seat.h"
8#include "sway/ipc-server.h"
8#include "sway/output.h" 9#include "sway/output.h"
9#include "sway/tree/arrange.h" 10#include "sway/tree/arrange.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
@@ -68,13 +69,18 @@ void root_scratchpad_add_container(struct sway_container *con) {
68 list_add(root->scratchpad, con); 69 list_add(root->scratchpad, con);
69 70
70 struct sway_seat *seat = input_manager_current_seat(); 71 struct sway_seat *seat = input_manager_current_seat();
72 struct sway_node *new_focus = NULL;
71 if (parent) { 73 if (parent) {
72 arrange_container(parent); 74 arrange_container(parent);
73 seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node)); 75 new_focus = seat_get_focus_inactive(seat, &parent->node);
74 } else { 76 }
77 if (!new_focus) {
75 arrange_workspace(workspace); 78 arrange_workspace(workspace);
76 seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node)); 79 new_focus = seat_get_focus_inactive(seat, &workspace->node);
77 } 80 }
81 seat_set_focus(seat, new_focus);
82
83 ipc_event_window(con, "move");
78} 84}
79 85
80void root_scratchpad_remove_container(struct sway_container *con) { 86void root_scratchpad_remove_container(struct sway_container *con) {
@@ -85,45 +91,51 @@ void root_scratchpad_remove_container(struct sway_container *con) {
85 int index = list_find(root->scratchpad, con); 91 int index = list_find(root->scratchpad, con);
86 if (index != -1) { 92 if (index != -1) {
87 list_del(root->scratchpad, index); 93 list_del(root->scratchpad, index);
94 ipc_event_window(con, "move");
88 } 95 }
89} 96}
90 97
91void root_scratchpad_show(struct sway_container *con) { 98void root_scratchpad_show(struct sway_container *con) {
92 struct sway_seat *seat = input_manager_current_seat(); 99 struct sway_seat *seat = input_manager_current_seat();
93 struct sway_workspace *ws = seat_get_focused_workspace(seat); 100 struct sway_workspace *new_ws = seat_get_focused_workspace(seat);
101 struct sway_workspace *old_ws = con->workspace;
94 102
95 // If the current con or any of its parents are in fullscreen mode, we 103 // If the current con or any of its parents are in fullscreen mode, we
96 // first need to disable it before showing the scratchpad con. 104 // first need to disable it before showing the scratchpad con.
97 if (ws->fullscreen) { 105 if (new_ws->fullscreen) {
98 container_set_fullscreen(ws->fullscreen, false); 106 container_set_fullscreen(new_ws->fullscreen, false);
99 } 107 }
100 108
101 // Show the container 109 // Show the container
102 if (con->workspace) { 110 if (old_ws) {
103 container_detach(con); 111 container_detach(con);
104 } 112 }
105 workspace_add_floating(ws, con); 113 workspace_add_floating(new_ws, con);
106 114
107 // Make sure the container's center point overlaps this workspace 115 // Make sure the container's center point overlaps this workspace
108 double center_lx = con->x + con->width / 2; 116 double center_lx = con->x + con->width / 2;
109 double center_ly = con->y + con->height / 2; 117 double center_ly = con->y + con->height / 2;
110 118
111 struct wlr_box workspace_box; 119 struct wlr_box workspace_box;
112 workspace_get_box(ws, &workspace_box); 120 workspace_get_box(new_ws, &workspace_box);
113 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { 121 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
114 // Maybe resize it 122 // Maybe resize it
115 if (con->width > ws->width || con->height > ws->height) { 123 if (con->width > new_ws->width || con->height > new_ws->height) {
116 container_init_floating(con); 124 container_init_floating(con);
117 } 125 }
118 126
119 // Center it 127 // Center it
120 double new_lx = ws->x + (ws->width - con->width) / 2; 128 double new_lx = new_ws->x + (new_ws->width - con->width) / 2;
121 double new_ly = ws->y + (ws->height - con->height) / 2; 129 double new_ly = new_ws->y + (new_ws->height - con->height) / 2;
122 container_floating_move_to(con, new_lx, new_ly); 130 container_floating_move_to(con, new_lx, new_ly);
123 } 131 }
124 132
125 arrange_workspace(ws); 133 arrange_workspace(new_ws);
126 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); 134 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
135
136 if (new_ws != old_ws) {
137 ipc_event_window(con, "move");
138 }
127} 139}
128 140
129void root_scratchpad_hide(struct sway_container *con) { 141void root_scratchpad_hide(struct sway_container *con) {
@@ -133,10 +145,12 @@ void root_scratchpad_hide(struct sway_container *con) {
133 145
134 container_detach(con); 146 container_detach(con);
135 arrange_workspace(ws); 147 arrange_workspace(ws);
136 if (&con->node == focus) { 148 if (&con->node == focus || node_has_ancestor(focus, &con->node)) {
137 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node)); 149 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
138 } 150 }
139 list_move_to_end(root->scratchpad, con); 151 list_move_to_end(root->scratchpad, con);
152
153 ipc_event_window(con, "move");
140} 154}
141 155
142struct pid_workspace { 156struct pid_workspace {
diff --git a/sway/tree/view.c b/sway/tree/view.c
index d7110619..5371ee20 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -437,9 +437,14 @@ void view_execute_criteria(struct sway_view *view) {
437 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 437 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
438 criteria->raw, view, criteria->cmdlist); 438 criteria->raw, view, criteria->cmdlist);
439 list_add(view->executed_criteria, criteria); 439 list_add(view->executed_criteria, criteria);
440 struct cmd_results *res = execute_command( 440 list_t *res_list = execute_command(
441 criteria->cmdlist, NULL, view->container); 441 criteria->cmdlist, NULL, view->container);
442 free_cmd_results(res); 442 while (res_list->length) {
443 struct cmd_results *res = res_list->items[0];
444 free_cmd_results(res);
445 list_del(res_list, 0);
446 }
447 list_free(res_list);
443 } 448 }
444 list_free(criterias); 449 list_free(criterias);
445} 450}
@@ -454,7 +459,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
454 for (int i = 0; i < criterias->length; ++i) { 459 for (int i = 0; i < criterias->length; ++i) {
455 struct criteria *criteria = criterias->items[i]; 460 struct criteria *criteria = criterias->items[i];
456 if (criteria->type == CT_ASSIGN_OUTPUT) { 461 if (criteria->type == CT_ASSIGN_OUTPUT) {
457 struct sway_output *output = output_by_name(criteria->target); 462 struct sway_output *output = output_by_name_or_id(criteria->target);
458 if (output) { 463 if (output) {
459 ws = output_get_active_workspace(output); 464 ws = output_get_active_workspace(output);
460 break; 465 break;
@@ -600,7 +605,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
600 605
601 view_update_title(view, false); 606 view_update_title(view, false);
602 container_update_representation(view->container); 607 container_update_representation(view->container);
603 view_execute_criteria(view);
604 608
605 if (decoration) { 609 if (decoration) {
606 view_update_csd_from_client(view, decoration); 610 view_update_csd_from_client(view, decoration);
@@ -617,6 +621,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
617 } 621 }
618 } 622 }
619 623
624 view_execute_criteria(view);
625
620 if (should_focus(view)) { 626 if (should_focus(view)) {
621 input_manager_set_focus(&view->container->node); 627 input_manager_set_focus(&view->container->node);
622 } 628 }
@@ -648,14 +654,8 @@ void view_unmap(struct sway_view *view) {
648 654
649 struct sway_seat *seat; 655 struct sway_seat *seat;
650 wl_list_for_each(seat, &server.input->seats, link) { 656 wl_list_for_each(seat, &server.input->seats, link) {
651 if (config->mouse_warping == WARP_CONTAINER) { 657 seat->cursor->image_surface = NULL;
652 struct sway_node *node = seat_get_focus(seat); 658 seat_consider_warp_to_focus(seat);
653 if (node && node->type == N_CONTAINER) {
654 cursor_warp_to_container(seat->cursor, node->sway_container);
655 } else if (node && node->type == N_WORKSPACE) {
656 cursor_warp_to_workspace(seat->cursor, node->sway_workspace);
657 }
658 }
659 } 659 }
660 660
661 transaction_commit_dirty(); 661 transaction_commit_dirty();
@@ -674,6 +674,8 @@ void view_update_size(struct sway_view *view, int width, int height) {
674 container_set_geometry_from_content(view->container); 674 container_set_geometry_from_content(view->container);
675} 675}
676 676
677static const struct sway_view_child_impl subsurface_impl;
678
677static void subsurface_get_root_coords(struct sway_view_child *child, 679static void subsurface_get_root_coords(struct sway_view_child *child,
678 int *root_sx, int *root_sy) { 680 int *root_sx, int *root_sy) {
679 struct wlr_surface *surface = child->surface; 681 struct wlr_surface *surface = child->surface;
@@ -689,18 +691,47 @@ static void subsurface_get_root_coords(struct sway_view_child *child,
689 } 691 }
690} 692}
691 693
694static void subsurface_destroy(struct sway_view_child *child) {
695 if (!sway_assert(child->impl == &subsurface_impl,
696 "Expected a subsurface")) {
697 return;
698 }
699 struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
700 wl_list_remove(&subsurface->destroy.link);
701 free(subsurface);
702}
703
692static const struct sway_view_child_impl subsurface_impl = { 704static const struct sway_view_child_impl subsurface_impl = {
693 .get_root_coords = subsurface_get_root_coords, 705 .get_root_coords = subsurface_get_root_coords,
706 .destroy = subsurface_destroy,
694}; 707};
695 708
709static void subsurface_handle_destroy(struct wl_listener *listener,
710 void *data) {
711 struct sway_subsurface *subsurface =
712 wl_container_of(listener, subsurface, destroy);
713 struct sway_view_child *child = &subsurface->child;
714 view_child_destroy(child);
715}
716
717static void view_child_damage(struct sway_view_child *child, bool whole);
718
696static void view_subsurface_create(struct sway_view *view, 719static void view_subsurface_create(struct sway_view *view,
697 struct wlr_subsurface *subsurface) { 720 struct wlr_subsurface *wlr_subsurface) {
698 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); 721 struct sway_subsurface *subsurface =
699 if (child == NULL) { 722 calloc(1, sizeof(struct sway_subsurface));
723 if (subsurface == NULL) {
700 wlr_log(WLR_ERROR, "Allocation failed"); 724 wlr_log(WLR_ERROR, "Allocation failed");
701 return; 725 return;
702 } 726 }
703 view_child_init(child, &subsurface_impl, view, subsurface->surface); 727 view_child_init(&subsurface->child, &subsurface_impl, view,
728 wlr_subsurface->surface);
729
730 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
731 subsurface->destroy.notify = subsurface_handle_destroy;
732
733 subsurface->child.mapped = true;
734 view_child_damage(&subsurface->child, true);
704} 735}
705 736
706static void view_child_damage(struct sway_view_child *child, bool whole) { 737static void view_child_damage(struct sway_view_child *child, bool whole) {
@@ -745,6 +776,7 @@ static void view_child_handle_surface_map(struct wl_listener *listener,
745 void *data) { 776 void *data) {
746 struct sway_view_child *child = 777 struct sway_view_child *child =
747 wl_container_of(listener, child, surface_map); 778 wl_container_of(listener, child, surface_map);
779 child->mapped = true;
748 view_child_damage(child, true); 780 view_child_damage(child, true);
749} 781}
750 782
@@ -753,6 +785,7 @@ static void view_child_handle_surface_unmap(struct wl_listener *listener,
753 struct sway_view_child *child = 785 struct sway_view_child *child =
754 wl_container_of(listener, child, surface_unmap); 786 wl_container_of(listener, child, surface_unmap);
755 view_child_damage(child, true); 787 view_child_damage(child, true);
788 child->mapped = false;
756} 789}
757 790
758void view_child_init(struct sway_view_child *child, 791void view_child_init(struct sway_view_child *child,
@@ -771,6 +804,7 @@ void view_child_init(struct sway_view_child *child,
771 wl_signal_add(&surface->events.destroy, &child->surface_destroy); 804 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
772 child->surface_destroy.notify = view_child_handle_surface_destroy; 805 child->surface_destroy.notify = view_child_handle_surface_destroy;
773 806
807 // Not all child views have a map/unmap event
774 child->surface_map.notify = view_child_handle_surface_map; 808 child->surface_map.notify = view_child_handle_surface_map;
775 child->surface_unmap.notify = view_child_handle_surface_unmap; 809 child->surface_unmap.notify = view_child_handle_surface_unmap;
776 810
@@ -781,6 +815,10 @@ void view_child_init(struct sway_view_child *child,
781} 815}
782 816
783void view_child_destroy(struct sway_view_child *child) { 817void view_child_destroy(struct sway_view_child *child) {
818 if (child->mapped && child->view->container != NULL) {
819 view_child_damage(child, true);
820 }
821
784 wl_list_remove(&child->surface_commit.link); 822 wl_list_remove(&child->surface_commit.link);
785 wl_list_remove(&child->surface_destroy.link); 823 wl_list_remove(&child->surface_destroy.link);
786 824
@@ -824,12 +862,29 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
824 return NULL; 862 return NULL;
825} 863}
826 864
865static char *escape_pango_markup(const char *buffer) {
866 size_t length = escape_markup_text(buffer, NULL);
867 char *escaped_title = calloc(length + 1, sizeof(char));
868 escape_markup_text(buffer, escaped_title);
869 return escaped_title;
870}
871
827static size_t append_prop(char *buffer, const char *value) { 872static size_t append_prop(char *buffer, const char *value) {
828 if (!value) { 873 if (!value) {
829 return 0; 874 return 0;
830 } 875 }
831 lenient_strcat(buffer, value); 876 // If using pango_markup in font, we need to escape all markup chars
832 return strlen(value); 877 // from values to make sure tags are not inserted by clients
878 if (config->pango_markup) {
879 char *escaped_value = escape_pango_markup(value);
880 lenient_strcat(buffer, escaped_value);
881 size_t len = strlen(escaped_value);
882 free(escaped_value);
883 return len;
884 } else {
885 lenient_strcat(buffer, value);
886 return strlen(value);
887 }
833} 888}
834 889
835/** 890/**
@@ -838,11 +893,7 @@ static size_t append_prop(char *buffer, const char *value) {
838 */ 893 */
839static size_t parse_title_format(struct sway_view *view, char *buffer) { 894static size_t parse_title_format(struct sway_view *view, char *buffer) {
840 if (!view->title_format || strcmp(view->title_format, "%title") == 0) { 895 if (!view->title_format || strcmp(view->title_format, "%title") == 0) {
841 const char *title = view_get_title(view); 896 return append_prop(buffer, view_get_title(view));
842 if (buffer && title) {
843 strcpy(buffer, title);
844 }
845 return title ? strlen(title) : 0;
846 } 897 }
847 898
848 size_t len = 0; 899 size_t len = 0;
@@ -882,14 +933,6 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
882 return len; 933 return len;
883} 934}
884 935
885static char *escape_title(char *buffer) {
886 size_t length = escape_markup_text(buffer, NULL);
887 char *escaped_title = calloc(length + 1, sizeof(char));
888 escape_markup_text(buffer, escaped_title);
889 free(buffer);
890 return escaped_title;
891}
892
893void view_update_title(struct sway_view *view, bool force) { 936void view_update_title(struct sway_view *view, bool force) {
894 const char *title = view_get_title(view); 937 const char *title = view_get_title(view);
895 938
@@ -912,10 +955,6 @@ void view_update_title(struct sway_view *view, bool force) {
912 return; 955 return;
913 } 956 }
914 parse_title_format(view, buffer); 957 parse_title_format(view, buffer);
915 // now we have the title, but needs to be escaped when using pango markup
916 if (config->pango_markup) {
917 buffer = escape_title(buffer);
918 }
919 958
920 view->container->title = strdup(title); 959 view->container->title = strdup(title);
921 view->container->formatted_title = buffer; 960 view->container->formatted_title = buffer;
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 4be63311..7f18046d 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -35,10 +35,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
35 struct workspace_config *wsc = workspace_find_config(name); 35 struct workspace_config *wsc = workspace_find_config(name);
36 if (wsc) { 36 if (wsc) {
37 for (int i = 0; i < wsc->outputs->length; i++) { 37 for (int i = 0; i < wsc->outputs->length; i++) {
38 struct sway_output *output = output_by_name(wsc->outputs->items[i]); 38 struct sway_output *output =
39 if (!output) { 39 output_by_name_or_id(wsc->outputs->items[i]);
40 output = output_by_identifier(wsc->outputs->items[i]);
41 }
42 if (output) { 40 if (output) {
43 return output; 41 return output;
44 } 42 }
@@ -113,7 +111,10 @@ struct sway_workspace *workspace_create(struct sway_output *output,
113 111
114 // Add output priorities 112 // Add output priorities
115 for (int i = 0; i < wsc->outputs->length; ++i) { 113 for (int i = 0; i < wsc->outputs->length; ++i) {
116 list_add(ws->output_priority, strdup(wsc->outputs->items[i])); 114 char *name = wsc->outputs->items[i];
115 if (strcmp(name, "*") != 0) {
116 list_add(ws->output_priority, strdup(name));
117 }
117 } 118 }
118 } 119 }
119 } 120 }
@@ -142,7 +143,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
142 143
143 free(workspace->name); 144 free(workspace->name);
144 free(workspace->representation); 145 free(workspace->representation);
145 free_flat_list(workspace->output_priority); 146 list_free_items_and_destroy(workspace->output_priority);
146 list_free(workspace->floating); 147 list_free(workspace->floating);
147 list_free(workspace->tiling); 148 list_free(workspace->tiling);
148 list_free(workspace->current.floating); 149 list_free(workspace->current.floating);
@@ -182,7 +183,11 @@ static bool workspace_valid_on_output(const char *output_name,
182 const char *ws_name) { 183 const char *ws_name) {
183 struct workspace_config *wsc = workspace_find_config(ws_name); 184 struct workspace_config *wsc = workspace_find_config(ws_name);
184 char identifier[128]; 185 char identifier[128];
185 struct sway_output *output = output_by_name(output_name); 186 struct sway_output *output = output_by_name_or_id(output_name);
187 if (!output) {
188 return false;
189 }
190 output_name = output->wlr_output->name;
186 output_get_identifier(identifier, sizeof(identifier), output); 191 output_get_identifier(identifier, sizeof(identifier), output);
187 192
188 if (!wsc) { 193 if (!wsc) {
@@ -190,7 +195,8 @@ static bool workspace_valid_on_output(const char *output_name,
190 } 195 }
191 196
192 for (int i = 0; i < wsc->outputs->length; i++) { 197 for (int i = 0; i < wsc->outputs->length; i++) {
193 if (strcmp(wsc->outputs->items[i], output_name) == 0 || 198 if (strcmp(wsc->outputs->items[i], "*") == 0 ||
199 strcmp(wsc->outputs->items[i], output_name) == 0 ||
194 strcmp(wsc->outputs->items[i], identifier) == 0) { 200 strcmp(wsc->outputs->items[i], identifier) == 0) {
195 return true; 201 return true;
196 } 202 }
@@ -286,6 +292,14 @@ char *workspace_next_name(const char *output_name) {
286 // assignments primarily, falling back to bindings and numbers. 292 // assignments primarily, falling back to bindings and numbers.
287 struct sway_mode *mode = config->current_mode; 293 struct sway_mode *mode = config->current_mode;
288 294
295 char identifier[128];
296 struct sway_output *output = output_by_name_or_id(output_name);
297 if (!output) {
298 return NULL;
299 }
300 output_name = output->wlr_output->name;
301 output_get_identifier(identifier, sizeof(identifier), output);
302
289 int order = INT_MAX; 303 int order = INT_MAX;
290 char *target = NULL; 304 char *target = NULL;
291 for (int i = 0; i < mode->keysym_bindings->length; ++i) { 305 for (int i = 0; i < mode->keysym_bindings->length; ++i) {
@@ -304,7 +318,9 @@ char *workspace_next_name(const char *output_name) {
304 } 318 }
305 bool found = false; 319 bool found = false;
306 for (int j = 0; j < wsc->outputs->length; ++j) { 320 for (int j = 0; j < wsc->outputs->length; ++j) {
307 if (strcmp(wsc->outputs->items[j], output_name) == 0) { 321 if (strcmp(wsc->outputs->items[j], "*") == 0 ||
322 strcmp(wsc->outputs->items[j], output_name) == 0 ||
323 strcmp(wsc->outputs->items[j], identifier) == 0) {
308 found = true; 324 found = true;
309 free(target); 325 free(target);
310 target = strdup(wsc->workspace); 326 target = strdup(wsc->workspace);
@@ -525,13 +541,19 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
525 541
526struct sway_output *workspace_output_get_highest_available( 542struct sway_output *workspace_output_get_highest_available(
527 struct sway_workspace *ws, struct sway_output *exclude) { 543 struct sway_workspace *ws, struct sway_output *exclude) {
544 char exclude_id[128] = {'\0'};
545 if (exclude) {
546 output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
547 }
548
528 for (int i = 0; i < ws->output_priority->length; i++) { 549 for (int i = 0; i < ws->output_priority->length; i++) {
529 char *name = ws->output_priority->items[i]; 550 char *name = ws->output_priority->items[i];
530 if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) { 551 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0
552 || strcmp(name, exclude_id) == 0)) {
531 continue; 553 continue;
532 } 554 }
533 555
534 struct sway_output *output = output_by_name(name); 556 struct sway_output *output = output_by_name_or_id(name);
535 if (output) { 557 if (output) {
536 return output; 558 return output;
537 } 559 }
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 08c386a7..d36367fc 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <errno.h> 3#include <errno.h>
4#include <fcntl.h> 4#include <fcntl.h>
@@ -11,6 +11,7 @@
11#include <wayland-client.h> 11#include <wayland-client.h>
12#include <wayland-cursor.h> 12#include <wayland-cursor.h>
13#include <wlr/util/log.h> 13#include <wlr/util/log.h>
14#include "config.h"
14#include "swaybar/bar.h" 15#include "swaybar/bar.h"
15#include "swaybar/config.h" 16#include "swaybar/config.h"
16#include "swaybar/i3bar.h" 17#include "swaybar/i3bar.h"
@@ -18,6 +19,9 @@
18#include "swaybar/ipc.h" 19#include "swaybar/ipc.h"
19#include "swaybar/status_line.h" 20#include "swaybar/status_line.h"
20#include "swaybar/render.h" 21#include "swaybar/render.h"
22#if HAVE_TRAY
23#include "swaybar/tray/tray.h"
24#endif
21#include "ipc-client.h" 25#include "ipc-client.h"
22#include "list.h" 26#include "list.h"
23#include "log.h" 27#include "log.h"
@@ -31,6 +35,7 @@ void free_workspaces(struct wl_list *list) {
31 wl_list_for_each_safe(ws, tmp, list, link) { 35 wl_list_for_each_safe(ws, tmp, list, link) {
32 wl_list_remove(&ws->link); 36 wl_list_remove(&ws->link);
33 free(ws->name); 37 free(ws->name);
38 free(ws->label);
34 free(ws); 39 free(ws);
35 } 40 }
36} 41}
@@ -54,6 +59,7 @@ static void swaybar_output_free(struct swaybar_output *output) {
54 free_workspaces(&output->workspaces); 59 free_workspaces(&output->workspaces);
55 wl_list_remove(&output->link); 60 wl_list_remove(&output->link);
56 free(output->name); 61 free(output->name);
62 free(output->identifier);
57 free(output); 63 free(output);
58} 64}
59 65
@@ -119,7 +125,7 @@ static void destroy_layer_surface(struct swaybar_output *output) {
119 output->frame_scheduled = false; 125 output->frame_scheduled = false;
120} 126}
121 127
122static void set_bar_dirty(struct swaybar *bar) { 128void set_bar_dirty(struct swaybar *bar) {
123 struct swaybar_output *output; 129 struct swaybar_output *output;
124 wl_list_for_each(output, &bar->outputs, link) { 130 wl_list_for_each(output, &bar->outputs, link) {
125 set_output_dirty(output); 131 set_output_dirty(output);
@@ -161,13 +167,15 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
161 return visible; 167 return visible;
162} 168}
163 169
164static bool bar_uses_output(struct swaybar *bar, const char *name) { 170static bool bar_uses_output(struct swaybar_output *output) {
165 if (bar->config->all_outputs) { 171 if (output->bar->config->all_outputs) {
166 return true; 172 return true;
167 } 173 }
174 char *identifier = output->identifier;
168 struct config_output *coutput; 175 struct config_output *coutput;
169 wl_list_for_each(coutput, &bar->config->outputs, link) { 176 wl_list_for_each(coutput, &output->bar->config->outputs, link) {
170 if (strcmp(coutput->name, name) == 0) { 177 if (strcmp(coutput->name, output->name) == 0 ||
178 (identifier && strcmp(coutput->name, identifier) == 0)) {
171 return true; 179 return true;
172 } 180 }
173 } 181 }
@@ -195,6 +203,10 @@ static void output_scale(void *data, struct wl_output *wl_output,
195 int32_t factor) { 203 int32_t factor) {
196 struct swaybar_output *output = data; 204 struct swaybar_output *output = data;
197 output->scale = factor; 205 output->scale = factor;
206 if (output == output->bar->pointer.current) {
207 update_cursor(output->bar);
208 render_frame(output);
209 }
198} 210}
199 211
200struct wl_output_listener output_listener = { 212struct wl_output_listener output_listener = {
@@ -206,12 +218,16 @@ struct wl_output_listener output_listener = {
206 218
207static void xdg_output_handle_logical_position(void *data, 219static void xdg_output_handle_logical_position(void *data,
208 struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { 220 struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) {
209 // Who cares 221 struct swaybar_output *output = data;
222 output->output_x = x;
223 output->output_y = y;
210} 224}
211 225
212static void xdg_output_handle_logical_size(void *data, 226static void xdg_output_handle_logical_size(void *data,
213 struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { 227 struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) {
214 // Who cares 228 struct swaybar_output *output = data;
229 output->output_height = height;
230 output->output_width = width;
215} 231}
216 232
217static void xdg_output_handle_done(void *data, 233static void xdg_output_handle_done(void *data,
@@ -220,7 +236,7 @@ static void xdg_output_handle_done(void *data,
220 struct swaybar *bar = output->bar; 236 struct swaybar *bar = output->bar;
221 237
222 assert(output->name != NULL); 238 assert(output->name != NULL);
223 if (!bar_uses_output(bar, output->name)) { 239 if (!bar_uses_output(output)) {
224 swaybar_output_free(output); 240 swaybar_output_free(output);
225 return; 241 return;
226 } 242 }
@@ -245,7 +261,22 @@ static void xdg_output_handle_name(void *data,
245 261
246static void xdg_output_handle_description(void *data, 262static void xdg_output_handle_description(void *data,
247 struct zxdg_output_v1 *xdg_output, const char *description) { 263 struct zxdg_output_v1 *xdg_output, const char *description) {
248 // Who cares 264 // wlroots currently sets the description to `make model serial (name)`
265 // If this changes in the future, this will need to be modified.
266 struct swaybar_output *output = data;
267 free(output->identifier);
268 output->identifier = NULL;
269 char *paren = strrchr(description, '(');
270 if (paren) {
271 size_t length = paren - description;
272 output->identifier = malloc(length);
273 if (!output->identifier) {
274 wlr_log(WLR_ERROR, "Failed to allocate output identifier");
275 return;
276 }
277 strncpy(output->identifier, description, length);
278 output->identifier[length - 1] = '\0';
279 }
249} 280}
250 281
251struct zxdg_output_v1_listener xdg_output_listener = { 282struct zxdg_output_v1_listener xdg_output_listener = {
@@ -272,7 +303,7 @@ static void handle_global(void *data, struct wl_registry *registry,
272 struct swaybar *bar = data; 303 struct swaybar *bar = data;
273 if (strcmp(interface, wl_compositor_interface.name) == 0) { 304 if (strcmp(interface, wl_compositor_interface.name) == 0) {
274 bar->compositor = wl_registry_bind(registry, name, 305 bar->compositor = wl_registry_bind(registry, name,
275 &wl_compositor_interface, 3); 306 &wl_compositor_interface, 4);
276 } else if (strcmp(interface, wl_seat_interface.name) == 0) { 307 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
277 bar->seat = wl_registry_bind(registry, name, 308 bar->seat = wl_registry_bind(registry, name,
278 &wl_seat_interface, 3); 309 &wl_seat_interface, 3);
@@ -354,25 +385,15 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
354 wl_display_roundtrip(bar->display); 385 wl_display_roundtrip(bar->display);
355 386
356 struct swaybar_pointer *pointer = &bar->pointer; 387 struct swaybar_pointer *pointer = &bar->pointer;
357
358 int max_scale = 1;
359 struct swaybar_output *output;
360 wl_list_for_each(output, &bar->outputs, link) {
361 if (output->scale > max_scale) {
362 max_scale = output->scale;
363 }
364 }
365
366 pointer->cursor_theme =
367 wl_cursor_theme_load(NULL, 24 * max_scale, bar->shm);
368 assert(pointer->cursor_theme);
369 struct wl_cursor *cursor;
370 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
371 assert(cursor);
372 pointer->cursor_image = cursor->images[0];
373 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); 388 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
374 assert(pointer->cursor_surface); 389 assert(pointer->cursor_surface);
375 390
391#if HAVE_TRAY
392 if (!bar->config->tray_hidden) {
393 bar->tray = create_tray(bar);
394 }
395#endif
396
376 if (bar->config->workspace_buttons) { 397 if (bar->config->workspace_buttons) {
377 ipc_get_workspaces(bar); 398 ipc_get_workspaces(bar);
378 } 399 }
@@ -414,6 +435,11 @@ void bar_run(struct swaybar *bar) {
414 loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN, 435 loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN,
415 status_in, bar); 436 status_in, bar);
416 } 437 }
438#if HAVE_TRAY
439 if (bar->tray) {
440 loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus);
441 }
442#endif
417 while (1) { 443 while (1) {
418 errno = 0; 444 errno = 0;
419 if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) { 445 if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) {
@@ -431,6 +457,9 @@ static void free_outputs(struct wl_list *list) {
431} 457}
432 458
433void bar_teardown(struct swaybar *bar) { 459void bar_teardown(struct swaybar *bar) {
460#if HAVE_TRAY
461 destroy_tray(bar->tray);
462#endif
434 free_outputs(&bar->outputs); 463 free_outputs(&bar->outputs);
435 if (bar->config) { 464 if (bar->config) {
436 free_config(bar->config); 465 free_config(bar->config);
diff --git a/swaybar/config.c b/swaybar/config.c
index 0fd1f02e..d4cc9b1a 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,9 +1,10 @@
1#define _XOPEN_SOURCE 500 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/util/log.h> 4#include <wlr/util/log.h>
5#include "swaybar/config.h" 5#include "swaybar/config.h"
6#include "wlr-layer-shell-unstable-v1-client-protocol.h" 6#include "wlr-layer-shell-unstable-v1-client-protocol.h"
7#include "config.h"
7#include "stringop.h" 8#include "stringop.h"
8#include "list.h" 9#include "list.h"
9 10
@@ -30,15 +31,24 @@ struct swaybar_config *init_config(void) {
30 config->hidden_state = strdup("hide"); 31 config->hidden_state = strdup("hide");
31 config->sep_symbol = NULL; 32 config->sep_symbol = NULL;
32 config->strip_workspace_numbers = false; 33 config->strip_workspace_numbers = false;
34 config->strip_workspace_name = false;
33 config->binding_mode_indicator = true; 35 config->binding_mode_indicator = true;
34 config->wrap_scroll = false; 36 config->wrap_scroll = false;
35 config->workspace_buttons = true; 37 config->workspace_buttons = true;
36 config->bindings = create_list(); 38 config->bindings = create_list();
37 wl_list_init(&config->outputs); 39 wl_list_init(&config->outputs);
40 config->status_padding = 1;
41 config->status_edge_padding = 3;
38 42
39 /* height */ 43 /* height */
40 config->height = 0; 44 config->height = 0;
41 45
46 /* gaps */
47 config->gaps.top = 0;
48 config->gaps.right = 0;
49 config->gaps.bottom = 0;
50 config->gaps.left = 0;
51
42 /* colors */ 52 /* colors */
43 config->colors.background = 0x000000FF; 53 config->colors.background = 0x000000FF;
44 config->colors.focused_background = 0x000000FF; 54 config->colors.focused_background = 0x000000FF;
@@ -66,6 +76,10 @@ struct swaybar_config *init_config(void) {
66 config->colors.binding_mode.background = 0x900000FF; 76 config->colors.binding_mode.background = 0x900000FF;
67 config->colors.binding_mode.text = 0xFFFFFFFF; 77 config->colors.binding_mode.text = 0xFFFFFFFF;
68 78
79#if HAVE_TRAY
80 config->tray_padding = 2;
81#endif
82
69 return config; 83 return config;
70} 84}
71 85
@@ -95,5 +109,12 @@ void free_config(struct swaybar_config *config) {
95 free(coutput->name); 109 free(coutput->name);
96 free(coutput); 110 free(coutput);
97 } 111 }
112#if HAVE_TRAY
113 list_free_items_and_destroy(config->tray_outputs);
114 for (int i = 0; i < 10; ++i) {
115 free(config->tray_bindings[i]);
116 }
117 free(config->icon_theme);
118#endif
98 free(config); 119 free(config);
99} 120}
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 3ea74e13..116c8f6e 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -259,8 +259,34 @@ bool i3bar_handle_readable(struct status_line *status) {
259 } 259 }
260} 260}
261 261
262static uint32_t event_to_x11_button(uint32_t event) {
263 switch (event) {
264 case BTN_LEFT:
265 return 1;
266 case BTN_MIDDLE:
267 return 2;
268 case BTN_RIGHT:
269 return 3;
270 case SWAY_SCROLL_UP:
271 return 4;
272 case SWAY_SCROLL_DOWN:
273 return 5;
274 case SWAY_SCROLL_LEFT:
275 return 6;
276 case SWAY_SCROLL_RIGHT:
277 return 7;
278 case BTN_SIDE:
279 return 8;
280 case BTN_EXTRA:
281 return 9;
282 default:
283 return 0;
284 }
285}
286
262enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 287enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
263 struct i3bar_block *block, int x, int y, enum x11_button button) { 288 struct i3bar_block *block, int x, int y, int rx, int ry, int w, int h,
289 uint32_t button) {
264 wlr_log(WLR_DEBUG, "block %s clicked", block->name); 290 wlr_log(WLR_DEBUG, "block %s clicked", block->name);
265 if (!block->name || !status->click_events) { 291 if (!block->name || !status->click_events) {
266 return HOTSPOT_PROCESS; 292 return HOTSPOT_PROCESS;
@@ -274,9 +300,15 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
274 json_object_new_string(block->instance)); 300 json_object_new_string(block->instance));
275 } 301 }
276 302
277 json_object_object_add(event_json, "button", json_object_new_int(button)); 303 json_object_object_add(event_json, "button",
304 json_object_new_int(event_to_x11_button(button)));
305 json_object_object_add(event_json, "event", json_object_new_int(button));
278 json_object_object_add(event_json, "x", json_object_new_int(x)); 306 json_object_object_add(event_json, "x", json_object_new_int(x));
279 json_object_object_add(event_json, "y", json_object_new_int(y)); 307 json_object_object_add(event_json, "y", json_object_new_int(y));
308 json_object_object_add(event_json, "relative_x", json_object_new_int(rx));
309 json_object_object_add(event_json, "relative_y", json_object_new_int(ry));
310 json_object_object_add(event_json, "width", json_object_new_int(w));
311 json_object_object_add(event_json, "height", json_object_new_int(h));
280 if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "", 312 if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "",
281 json_object_to_json_string(event_json)) < 0) { 313 json_object_to_json_string(event_json)) < 0) {
282 status_error(status, "[failed to write click event]"); 314 status_error(status, "[failed to write click event]");
diff --git a/swaybar/input.c b/swaybar/input.c
index 263d0253..bdd55e58 100644
--- a/swaybar/input.c
+++ b/swaybar/input.c
@@ -1,9 +1,5 @@
1#include <assert.h> 1#include <assert.h>
2#ifdef __FreeBSD__
3#include <dev/evdev/input-event-codes.h>
4#else
5#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
6#endif
7#include <stdlib.h> 3#include <stdlib.h>
8#include <wayland-client.h> 4#include <wayland-client.h>
9#include <wayland-cursor.h> 5#include <wayland-cursor.h>
@@ -26,33 +22,39 @@ void free_hotspots(struct wl_list *list) {
26 } 22 }
27} 23}
28 24
29static enum x11_button wl_button_to_x11_button(uint32_t button) { 25static uint32_t wl_axis_to_button(uint32_t axis, wl_fixed_t value) {
30 switch (button) { 26 bool negative = wl_fixed_to_double(value) < 0;
31 case BTN_LEFT:
32 return LEFT;
33 case BTN_MIDDLE:
34 return MIDDLE;
35 case BTN_RIGHT:
36 return RIGHT;
37 case BTN_SIDE:
38 return BACK;
39 case BTN_EXTRA:
40 return FORWARD;
41 default:
42 return NONE;
43 }
44}
45
46static enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value) {
47 switch (axis) { 27 switch (axis) {
48 case WL_POINTER_AXIS_VERTICAL_SCROLL: 28 case WL_POINTER_AXIS_VERTICAL_SCROLL:
49 return wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN; 29 return negative ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
50 case WL_POINTER_AXIS_HORIZONTAL_SCROLL: 30 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
51 return wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT; 31 return negative ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
52 default: 32 default:
53 wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll"); 33 wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll");
54 return NONE; 34 return 0;
35 }
36}
37
38void update_cursor(struct swaybar *bar) {
39 struct swaybar_pointer *pointer = &bar->pointer;
40 if (pointer->cursor_theme) {
41 wl_cursor_theme_destroy(pointer->cursor_theme);
55 } 42 }
43 int scale = pointer->current ? pointer->current->scale : 1;
44 pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * scale, bar->shm);
45 struct wl_cursor *cursor;
46 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
47 pointer->cursor_image = cursor->images[0];
48 wl_surface_set_buffer_scale(pointer->cursor_surface, scale);
49 wl_surface_attach(pointer->cursor_surface,
50 wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
51 wl_pointer_set_cursor(pointer->pointer, pointer->serial,
52 pointer->cursor_surface,
53 pointer->cursor_image->hotspot_x / scale,
54 pointer->cursor_image->hotspot_y / scale);
55 wl_surface_damage_buffer(pointer->cursor_surface, 0, 0,
56 INT32_MAX, INT32_MAX);
57 wl_surface_commit(pointer->cursor_surface);
56} 58}
57 59
58static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, 60static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
@@ -60,6 +62,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
60 wl_fixed_t surface_x, wl_fixed_t surface_y) { 62 wl_fixed_t surface_x, wl_fixed_t surface_y) {
61 struct swaybar *bar = data; 63 struct swaybar *bar = data;
62 struct swaybar_pointer *pointer = &bar->pointer; 64 struct swaybar_pointer *pointer = &bar->pointer;
65 pointer->serial = serial;
63 struct swaybar_output *output; 66 struct swaybar_output *output;
64 wl_list_for_each(output, &bar->outputs, link) { 67 wl_list_for_each(output, &bar->outputs, link) {
65 if (output->surface == surface) { 68 if (output->surface == surface) {
@@ -67,20 +70,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
67 break; 70 break;
68 } 71 }
69 } 72 }
70 int max_scale = 1; 73 update_cursor(bar);
71 struct swaybar_output *_output;
72 wl_list_for_each(_output, &bar->outputs, link) {
73 if (_output->scale > max_scale) {
74 max_scale = _output->scale;
75 }
76 }
77 wl_surface_set_buffer_scale(pointer->cursor_surface, max_scale);
78 wl_surface_attach(pointer->cursor_surface,
79 wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
80 wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface,
81 pointer->cursor_image->hotspot_x / max_scale,
82 pointer->cursor_image->hotspot_y / max_scale);
83 wl_surface_commit(pointer->cursor_surface);
84} 74}
85 75
86static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, 76static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
@@ -96,12 +86,12 @@ static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
96 bar->pointer.y = wl_fixed_to_int(surface_y); 86 bar->pointer.y = wl_fixed_to_int(surface_y);
97} 87}
98 88
99static bool check_bindings(struct swaybar *bar, uint32_t x11_button, 89static bool check_bindings(struct swaybar *bar, uint32_t button,
100 uint32_t state) { 90 uint32_t state) {
101 bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; 91 bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
102 for (int i = 0; i < bar->config->bindings->length; i++) { 92 for (int i = 0; i < bar->config->bindings->length; i++) {
103 struct swaybar_binding *binding = bar->config->bindings->items[i]; 93 struct swaybar_binding *binding = bar->config->bindings->items[i];
104 if (binding->button == x11_button && binding->release == released) { 94 if (binding->button == button && binding->release == released) {
105 ipc_execute_binding(bar, binding); 95 ipc_execute_binding(bar, binding);
106 return true; 96 return true;
107 } 97 }
@@ -118,7 +108,7 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
118 return; 108 return;
119 } 109 }
120 110
121 if (check_bindings(bar, wl_button_to_x11_button(button), state)) { 111 if (check_bindings(bar, button, state)) {
122 return; 112 return;
123 } 113 }
124 114
@@ -133,8 +123,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
133 && y >= hotspot->y 123 && y >= hotspot->y
134 && x < hotspot->x + hotspot->width 124 && x < hotspot->x + hotspot->width
135 && y < hotspot->y + hotspot->height) { 125 && y < hotspot->y + hotspot->height) {
136 if (HOTSPOT_IGNORE == hotspot->callback(output, pointer->x, pointer->y, 126 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot,
137 wl_button_to_x11_button(button), hotspot->data)) { 127 pointer->x, pointer->y, button, hotspot->data)) {
138 return; 128 return;
139 } 129 }
140 } 130 }
@@ -152,7 +142,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
152 142
153 // If there is a button press binding, execute it, skip default behavior, 143 // If there is a button press binding, execute it, skip default behavior,
154 // and check button release bindings 144 // and check button release bindings
155 enum x11_button button = wl_axis_to_x11_button(axis, value); 145 uint32_t button = wl_axis_to_button(axis, value);
156 if (check_bindings(bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { 146 if (check_bindings(bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
157 check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED); 147 check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
158 return; 148 return;
@@ -166,8 +156,8 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
166 && y >= hotspot->y 156 && y >= hotspot->y
167 && x < hotspot->x + hotspot->width 157 && x < hotspot->x + hotspot->width
168 && y < hotspot->y + hotspot->height) { 158 && y < hotspot->y + hotspot->height) {
169 if (HOTSPOT_IGNORE == hotspot->callback( 159 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot,
170 output, pointer->x, pointer->y, button, hotspot->data)) { 160 pointer->x, pointer->y, button, hotspot->data)) {
171 return; 161 return;
172 } 162 }
173 } 163 }
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 706f968d..097f9161 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -6,6 +6,7 @@
6#include <wlr/util/log.h> 6#include <wlr/util/log.h>
7#include "swaybar/config.h" 7#include "swaybar/config.h"
8#include "swaybar/ipc.h" 8#include "swaybar/ipc.h"
9#include "config.h"
9#include "ipc-client.h" 10#include "ipc-client.h"
10#include "list.h" 11#include "list.h"
11 12
@@ -153,18 +154,21 @@ static bool ipc_parse_config(
153 return false; 154 return false;
154 } 155 }
155 json_object *markup, *mode, *hidden_state, *position, *status_command; 156 json_object *markup, *mode, *hidden_state, *position, *status_command;
156 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; 157 json_object *font, *gaps, *bar_height, *wrap_scroll, *workspace_buttons;
157 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; 158 json_object *strip_workspace_numbers, *strip_workspace_name;
158 json_object *bindings; 159 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol;
160 json_object *outputs, *bindings, *status_padding, *status_edge_padding;
159 json_object_object_get_ex(bar_config, "mode", &mode); 161 json_object_object_get_ex(bar_config, "mode", &mode);
160 json_object_object_get_ex(bar_config, "hidden_state", &hidden_state); 162 json_object_object_get_ex(bar_config, "hidden_state", &hidden_state);
161 json_object_object_get_ex(bar_config, "position", &position); 163 json_object_object_get_ex(bar_config, "position", &position);
162 json_object_object_get_ex(bar_config, "status_command", &status_command); 164 json_object_object_get_ex(bar_config, "status_command", &status_command);
163 json_object_object_get_ex(bar_config, "font", &font); 165 json_object_object_get_ex(bar_config, "font", &font);
166 json_object_object_get_ex(bar_config, "gaps", &gaps);
164 json_object_object_get_ex(bar_config, "bar_height", &bar_height); 167 json_object_object_get_ex(bar_config, "bar_height", &bar_height);
165 json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); 168 json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll);
166 json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); 169 json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons);
167 json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); 170 json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers);
171 json_object_object_get_ex(bar_config, "strip_workspace_name", &strip_workspace_name);
168 json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator); 172 json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator);
169 json_object_object_get_ex(bar_config, "verbose", &verbose); 173 json_object_object_get_ex(bar_config, "verbose", &verbose);
170 json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); 174 json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol);
@@ -172,6 +176,9 @@ static bool ipc_parse_config(
172 json_object_object_get_ex(bar_config, "outputs", &outputs); 176 json_object_object_get_ex(bar_config, "outputs", &outputs);
173 json_object_object_get_ex(bar_config, "pango_markup", &markup); 177 json_object_object_get_ex(bar_config, "pango_markup", &markup);
174 json_object_object_get_ex(bar_config, "bindings", &bindings); 178 json_object_object_get_ex(bar_config, "bindings", &bindings);
179 json_object_object_get_ex(bar_config, "status_padding", &status_padding);
180 json_object_object_get_ex(bar_config, "status_edge_padding",
181 &status_edge_padding);
175 if (status_command) { 182 if (status_command) {
176 free(config->status_command); 183 free(config->status_command);
177 config->status_command = strdup(json_object_get_string(status_command)); 184 config->status_command = strdup(json_object_get_string(status_command));
@@ -190,6 +197,9 @@ static bool ipc_parse_config(
190 if (strip_workspace_numbers) { 197 if (strip_workspace_numbers) {
191 config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); 198 config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers);
192 } 199 }
200 if (strip_workspace_name) {
201 config->strip_workspace_name = json_object_get_boolean(strip_workspace_name);
202 }
193 if (binding_mode_indicator) { 203 if (binding_mode_indicator) {
194 config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); 204 config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator);
195 } 205 }
@@ -202,6 +212,30 @@ static bool ipc_parse_config(
202 if (bar_height) { 212 if (bar_height) {
203 config->height = json_object_get_int(bar_height); 213 config->height = json_object_get_int(bar_height);
204 } 214 }
215 if (status_padding) {
216 config->status_padding = json_object_get_int(status_padding);
217 }
218 if (status_edge_padding) {
219 config->status_edge_padding = json_object_get_int(status_edge_padding);
220 }
221 if (gaps) {
222 json_object *top = json_object_object_get(gaps, "top");
223 if (top) {
224 config->gaps.top = json_object_get_int(top);
225 }
226 json_object *right = json_object_object_get(gaps, "right");
227 if (right) {
228 config->gaps.right = json_object_get_int(right);
229 }
230 json_object *bottom = json_object_object_get(gaps, "bottom");
231 if (bottom) {
232 config->gaps.bottom = json_object_get_int(bottom);
233 }
234 json_object *left = json_object_object_get(gaps, "left");
235 if (left) {
236 config->gaps.left = json_object_get_int(left);
237 }
238 }
205 if (markup) { 239 if (markup) {
206 config->pango_markup = json_object_get_boolean(markup); 240 config->pango_markup = json_object_get_boolean(markup);
207 } 241 }
@@ -212,7 +246,7 @@ static bool ipc_parse_config(
212 struct swaybar_binding *binding = 246 struct swaybar_binding *binding =
213 calloc(1, sizeof(struct swaybar_binding)); 247 calloc(1, sizeof(struct swaybar_binding));
214 binding->button = json_object_get_int( 248 binding->button = json_object_get_int(
215 json_object_object_get(bindobj, "input_code")); 249 json_object_object_get(bindobj, "event_code"));
216 binding->command = strdup(json_object_get_string( 250 binding->command = strdup(json_object_get_string(
217 json_object_object_get(bindobj, "command"))); 251 json_object_object_get(bindobj, "command")));
218 binding->release = json_object_get_boolean( 252 binding->release = json_object_get_boolean(
@@ -258,6 +292,40 @@ static bool ipc_parse_config(
258 ipc_parse_colors(config, colors); 292 ipc_parse_colors(config, colors);
259 } 293 }
260 294
295#if HAVE_TRAY
296 json_object *tray_outputs, *tray_padding, *tray_bindings, *icon_theme;
297
298 if ((json_object_object_get_ex(bar_config, "tray_outputs", &tray_outputs))) {
299 config->tray_outputs = create_list();
300 int length = json_object_array_length(tray_outputs);
301 for (int i = 0; i < length; ++i) {
302 json_object *o = json_object_array_get_idx(tray_outputs, i);
303 list_add(config->tray_outputs, strdup(json_object_get_string(o)));
304 }
305 config->tray_hidden = strcmp(config->tray_outputs->items[0], "none") == 0;
306 }
307
308 if ((json_object_object_get_ex(bar_config, "tray_padding", &tray_padding))) {
309 config->tray_padding = json_object_get_int(tray_padding);
310 }
311
312 if ((json_object_object_get_ex(bar_config, "tray_bindings", &tray_bindings))) {
313 int length = json_object_array_length(tray_bindings);
314 for (int i = 0; i < length; ++i) {
315 json_object *bind = json_object_array_get_idx(tray_bindings, i);
316 json_object *button, *command;
317 json_object_object_get_ex(bind, "input_code", &button);
318 json_object_object_get_ex(bind, "command", &command);
319 config->tray_bindings[json_object_get_int(button)] =
320 strdup(json_object_get_string(command));
321 }
322 }
323
324 if ((json_object_object_get_ex(bar_config, "icon_theme", &icon_theme))) {
325 config->icon_theme = strdup(json_object_get_string(icon_theme));
326 }
327#endif
328
261 json_object_put(bar_config); 329 json_object_put(bar_config);
262 return true; 330 return true;
263} 331}
@@ -298,6 +366,24 @@ bool ipc_get_workspaces(struct swaybar *bar) {
298 calloc(1, sizeof(struct swaybar_workspace)); 366 calloc(1, sizeof(struct swaybar_workspace));
299 ws->num = json_object_get_int(num); 367 ws->num = json_object_get_int(num);
300 ws->name = strdup(json_object_get_string(name)); 368 ws->name = strdup(json_object_get_string(name));
369 ws->label = strdup(ws->name);
370 // ws->num will be -1 if workspace name doesn't begin with int.
371 if (ws->num != -1) {
372 size_t len_offset = numlen(ws->num);
373 if (bar->config->strip_workspace_name) {
374 free(ws->label);
375 ws->label = malloc(len_offset + 1 * sizeof(char));
376 ws->label[len_offset] = '\0';
377 strncpy(ws->label, ws->name, len_offset);
378 } else if (bar->config->strip_workspace_numbers) {
379 len_offset += ws->label[len_offset] == ':';
380 if (strlen(ws->name) > len_offset) {
381 free(ws->label);
382 // Strip number prefix [1-?:] using len_offset.
383 ws->label = strdup(ws->name + len_offset);
384 }
385 }
386 }
301 ws->visible = json_object_get_boolean(visible); 387 ws->visible = json_object_get_boolean(visible);
302 ws->focused = json_object_get_boolean(focused); 388 ws->focused = json_object_get_boolean(focused);
303 if (ws->focused) { 389 if (ws->focused) {
@@ -423,6 +509,27 @@ static bool handle_barconfig_update(struct swaybar *bar,
423 config->mode = strdup(json_object_get_string(json_mode)); 509 config->mode = strdup(json_object_get_string(json_mode));
424 wlr_log(WLR_DEBUG, "Changing bar mode to %s", config->mode); 510 wlr_log(WLR_DEBUG, "Changing bar mode to %s", config->mode);
425 511
512 json_object *gaps;
513 json_object_object_get_ex(json_config, "gaps", &gaps);
514 if (gaps) {
515 json_object *top = json_object_object_get(gaps, "top");
516 if (top) {
517 config->gaps.top = json_object_get_int(top);
518 }
519 json_object *right = json_object_object_get(gaps, "right");
520 if (right) {
521 config->gaps.right = json_object_get_int(right);
522 }
523 json_object *bottom = json_object_object_get(gaps, "bottom");
524 if (bottom) {
525 config->gaps.bottom = json_object_get_int(bottom);
526 }
527 json_object *left = json_object_object_get(gaps, "left");
528 if (left) {
529 config->gaps.left = json_object_get_int(left);
530 }
531 }
532
426 return determine_bar_visibility(bar, true); 533 return determine_bar_visibility(bar, true);
427} 534}
428 535
diff --git a/swaybar/main.c b/swaybar/main.c
index 2672abef..fa99b1ba 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <stdlib.h> 3#include <stdlib.h>
4#include <string.h> 4#include <string.h>
@@ -76,7 +76,7 @@ int main(int argc, char **argv) {
76 if (debug) { 76 if (debug) {
77 wlr_log_init(WLR_DEBUG, NULL); 77 wlr_log_init(WLR_DEBUG, NULL);
78 } else { 78 } else {
79 wlr_log_init(WLR_ERROR, NULL); 79 wlr_log_init(WLR_INFO, NULL);
80 } 80 }
81 81
82 if (!swaybar.id) { 82 if (!swaybar.id) {
diff --git a/swaybar/meson.build b/swaybar/meson.build
index c27cf2c2..312ca97b 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -1,3 +1,32 @@
1tray_files = get_option('enable-tray') ? [
2 'tray/host.c',
3 'tray/icon.c',
4 'tray/item.c',
5 'tray/tray.c',
6 'tray/watcher.c'
7] : []
8
9swaybar_deps = [
10 cairo,
11 client_protos,
12 gdk_pixbuf,
13 jsonc,
14 math,
15 pango,
16 pangocairo,
17 rt,
18 wayland_client,
19 wayland_cursor,
20 wlroots,
21]
22if get_option('enable-tray')
23 if systemd.found()
24 swaybar_deps += systemd
25 elif elogind.found()
26 swaybar_deps += elogind
27 endif
28endif
29
1executable( 30executable(
2 'swaybar', [ 31 'swaybar', [
3 'bar.c', 32 'bar.c',
@@ -8,21 +37,10 @@ executable(
8 'main.c', 37 'main.c',
9 'render.c', 38 'render.c',
10 'status_line.c', 39 'status_line.c',
40 tray_files
11 ], 41 ],
12 include_directories: [sway_inc], 42 include_directories: [sway_inc],
13 dependencies: [ 43 dependencies: swaybar_deps,
14 cairo,
15 client_protos,
16 gdk_pixbuf,
17 jsonc,
18 math,
19 pango,
20 pangocairo,
21 rt,
22 wayland_client,
23 wayland_cursor,
24 wlroots,
25 ],
26 link_with: [lib_sway_common, lib_sway_client], 44 link_with: [lib_sway_common, lib_sway_client],
27 install_rpath : rpathdir, 45 install_rpath : rpathdir,
28 install: true 46 install: true
diff --git a/swaybar/render.c b/swaybar/render.c
index 4ebf922e..55f680ed 100644
--- a/swaybar/render.c
+++ b/swaybar/render.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 <linux/input-event-codes.h>
3#include <limits.h> 4#include <limits.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <stdint.h> 6#include <stdint.h>
@@ -14,6 +15,9 @@
14#include "swaybar/ipc.h" 15#include "swaybar/ipc.h"
15#include "swaybar/render.h" 16#include "swaybar/render.h"
16#include "swaybar/status_line.h" 17#include "swaybar/status_line.h"
18#if HAVE_TRAY
19#include "swaybar/tray/tray.h"
20#endif
17#include "wlr-layer-shell-unstable-v1-client-protocol.h" 21#include "wlr-layer-shell-unstable-v1-client-protocol.h"
18 22
19static const int WS_HORIZONTAL_PADDING = 5; 23static const int WS_HORIZONTAL_PADDING = 5;
@@ -32,7 +36,8 @@ static uint32_t render_status_line_error(cairo_t *cairo,
32 cairo_set_source_u32(cairo, 0xFF0000FF); 36 cairo_set_source_u32(cairo, 0xFF0000FF);
33 37
34 int margin = 3 * output->scale; 38 int margin = 3 * output->scale;
35 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 39 double ws_vertical_padding =
40 output->bar->config->status_padding * output->scale;
36 41
37 char *font = output->bar->config->font; 42 char *font = output->bar->config->font;
38 int text_width, text_height; 43 int text_width, text_height;
@@ -41,7 +46,8 @@ static uint32_t render_status_line_error(cairo_t *cairo,
41 46
42 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 47 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
43 uint32_t ideal_surface_height = ideal_height / output->scale; 48 uint32_t ideal_surface_height = ideal_height / output->scale;
44 if (output->height < ideal_surface_height) { 49 if (!output->bar->config->height &&
50 output->height < ideal_surface_height) {
45 return ideal_surface_height; 51 return ideal_surface_height;
46 } 52 }
47 *x -= text_width + margin; 53 *x -= text_width + margin;
@@ -68,12 +74,13 @@ static uint32_t render_status_line_text(cairo_t *cairo,
68 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 74 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
69 output->scale, config->pango_markup, "%s", text); 75 output->scale, config->pango_markup, "%s", text);
70 76
71 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 77 double ws_vertical_padding = config->status_padding * output->scale;
72 int margin = 3 * output->scale; 78 int margin = 3 * output->scale;
73 79
74 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 80 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
75 uint32_t ideal_surface_height = ideal_height / output->scale; 81 uint32_t ideal_surface_height = ideal_height / output->scale;
76 if (output->height < ideal_surface_height) { 82 if (!output->bar->config->height &&
83 output->height < ideal_surface_height) {
77 return ideal_surface_height; 84 return ideal_surface_height;
78 } 85 }
79 86
@@ -87,13 +94,24 @@ static uint32_t render_status_line_text(cairo_t *cairo,
87 return output->height; 94 return output->height;
88} 95}
89 96
90static void render_sharp_line(cairo_t *cairo, uint32_t color, 97static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
91 double x, double y, double width, double height) { 98 double x, double y, double width, double height) {
99 cairo_save(cairo);
92 cairo_set_source_u32(cairo, color); 100 cairo_set_source_u32(cairo, color);
101 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
102 cairo_rectangle(cairo, x, y, width, height);
103 cairo_fill(cairo);
104 cairo_restore(cairo);
105}
106
107static void render_sharp_line(cairo_t *cairo, uint32_t color,
108 double x, double y, double width, double height) {
93 if (width > 1 && height > 1) { 109 if (width > 1 && height > 1) {
94 cairo_rectangle(cairo, x, y, width, height); 110 render_sharp_rectangle(cairo, color, x, y, width, height);
95 cairo_fill(cairo);
96 } else { 111 } else {
112 cairo_save(cairo);
113 cairo_set_source_u32(cairo, color);
114 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
97 if (width == 1) { 115 if (width == 1) {
98 x += 0.5; 116 x += 0.5;
99 height += y; 117 height += y;
@@ -108,14 +126,17 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
108 cairo_set_line_width(cairo, 1.0); 126 cairo_set_line_width(cairo, 1.0);
109 cairo_line_to(cairo, width, height); 127 cairo_line_to(cairo, width, height);
110 cairo_stroke(cairo); 128 cairo_stroke(cairo);
129 cairo_restore(cairo);
111 } 130 }
112} 131}
113 132
114static enum hotspot_event_handling block_hotspot_callback(struct swaybar_output *output, 133static enum hotspot_event_handling block_hotspot_callback(
115 int x, int y, enum x11_button button, void *data) { 134 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
135 int x, int y, uint32_t button, void *data) {
116 struct i3bar_block *block = data; 136 struct i3bar_block *block = data;
117 struct status_line *status = output->bar->status; 137 struct status_line *status = output->bar->status;
118 return i3bar_block_send_click(status, block, x, y, button); 138 return i3bar_block_send_click(status, block, x, y, x - hotspot->x,
139 y - hotspot->y, hotspot->width, hotspot->height, button);
119} 140}
120 141
121static void i3bar_block_unref_callback(void *data) { 142static void i3bar_block_unref_callback(void *data) {
@@ -136,7 +157,7 @@ static uint32_t render_status_block(cairo_t *cairo,
136 output->scale, block->markup, "%s", block->full_text); 157 output->scale, block->markup, "%s", block->full_text);
137 158
138 int margin = 3 * output->scale; 159 int margin = 3 * output->scale;
139 int ws_vertical_padding = WS_VERTICAL_PADDING * 2; 160 double ws_vertical_padding = config->status_padding * output->scale;
140 161
141 int width = text_width; 162 int width = text_width;
142 if (width < block->min_width) { 163 if (width < block->min_width) {
@@ -146,37 +167,40 @@ static uint32_t render_status_block(cairo_t *cairo,
146 double block_width = width; 167 double block_width = width;
147 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 168 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
148 uint32_t ideal_surface_height = ideal_height / output->scale; 169 uint32_t ideal_surface_height = ideal_height / output->scale;
149 if (output->height < ideal_surface_height) { 170 if (!output->bar->config->height &&
171 output->height < ideal_surface_height) {
150 return ideal_surface_height; 172 return ideal_surface_height;
151 } 173 }
152 174
153 *x -= width; 175 *x -= width;
154 if (block->border && block->border_left > 0) { 176 if ((block->border || block->urgent) && block->border_left > 0) {
155 *x -= (block->border_left + margin); 177 *x -= (block->border_left * output->scale + margin);
156 block_width += block->border_left + margin; 178 block_width += block->border_left * output->scale + margin;
157 } 179 }
158 if (block->border && block->border_right > 0) { 180 if ((block->border || block->urgent) && block->border_right > 0) {
159 *x -= (block->border_right + margin); 181 *x -= (block->border_right * output->scale + margin);
160 block_width += block->border_right + margin; 182 block_width += block->border_right * output->scale + margin;
161 } 183 }
162 184
163 int sep_width, sep_height; 185 int sep_width, sep_height;
186 int sep_block_width = block->separator_block_width;
164 if (!edge) { 187 if (!edge) {
165 if (config->sep_symbol) { 188 if (config->sep_symbol) {
166 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 189 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL,
167 output->scale, false, "%s", config->sep_symbol); 190 output->scale, false, "%s", config->sep_symbol);
168 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 191 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
169 uint32_t _ideal_surface_height = _ideal_height / output->scale; 192 uint32_t _ideal_surface_height = _ideal_height / output->scale;
170 if (output->height < _ideal_surface_height) { 193 if (!output->bar->config->height &&
194 output->height < _ideal_surface_height) {
171 return _ideal_surface_height; 195 return _ideal_surface_height;
172 } 196 }
173 if (sep_width > block->separator_block_width) { 197 if (sep_width > sep_block_width) {
174 block->separator_block_width = sep_width + margin * 2; 198 sep_block_width = sep_width + margin * 2;
175 } 199 }
176 } 200 }
177 *x -= block->separator_block_width; 201 *x -= sep_block_width;
178 } else { 202 } else if (config->status_edge_padding) {
179 *x -= margin; 203 *x -= config->status_edge_padding * output->scale;
180 } 204 }
181 205
182 uint32_t height = output->height * output->scale; 206 uint32_t height = output->height * output->scale;
@@ -193,53 +217,55 @@ static uint32_t render_status_block(cairo_t *cairo,
193 wl_list_insert(&output->hotspots, &hotspot->link); 217 wl_list_insert(&output->hotspots, &hotspot->link);
194 } 218 }
195 219
196 double pos = *x; 220 double x_pos = *x;
197 if (block->background) { 221 double y_pos = ws_vertical_padding;
198 cairo_set_source_u32(cairo, block->background); 222 double render_height = height - ws_vertical_padding * 2;
199 cairo_rectangle(cairo, pos - 0.5 * output->scale, 223
200 output->scale, width, height); 224 uint32_t bg_color = block->urgent
201 cairo_fill(cairo); 225 ? config->colors.urgent_workspace.background : block->background;
226 if (bg_color) {
227 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
228 block_width, render_height);
202 } 229 }
203 230
204 if (block->border && block->border_top > 0) { 231 uint32_t border_color = block->urgent
205 render_sharp_line(cairo, block->border, 232 ? config->colors.urgent_workspace.border : block->border;
206 pos - 0.5 * output->scale, output->scale, 233 if (border_color && block->border_top > 0) {
207 block_width, block->border_top); 234 render_sharp_line(cairo, border_color, x_pos, y_pos,
235 block_width, block->border_top * output->scale);
208 } 236 }
209 if (block->border && block->border_bottom > 0) { 237 if (border_color && block->border_bottom > 0) {
210 render_sharp_line(cairo, block->border, 238 render_sharp_line(cairo, border_color, x_pos,
211 pos - 0.5 * output->scale, 239 y_pos + render_height - block->border_bottom * output->scale,
212 height - output->scale - block->border_bottom, 240 block_width, block->border_bottom * output->scale);
213 block_width, block->border_bottom);
214 } 241 }
215 if (block->border != 0 && block->border_left > 0) { 242 if (border_color && block->border_left > 0) {
216 render_sharp_line(cairo, block->border, 243 render_sharp_line(cairo, border_color, x_pos, y_pos,
217 pos - 0.5 * output->scale, output->scale, 244 block->border_left * output->scale, render_height);
218 block->border_left, height); 245 x_pos += block->border_left * output->scale + margin;
219 pos += block->border_left + margin;
220 } 246 }
221 247
222 double offset = 0; 248 double offset = 0;
223 if (strncmp(block->align, "left", 5) == 0) { 249 if (strncmp(block->align, "left", 5) == 0) {
224 offset = pos; 250 offset = x_pos;
225 } else if (strncmp(block->align, "right", 5) == 0) { 251 } else if (strncmp(block->align, "right", 5) == 0) {
226 offset = pos + width - text_width; 252 offset = x_pos + width - text_width;
227 } else if (strncmp(block->align, "center", 6) == 0) { 253 } else if (strncmp(block->align, "center", 6) == 0) {
228 offset = pos + (width - text_width) / 2; 254 offset = x_pos + (width - text_width) / 2;
229 } 255 }
230 cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); 256 cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0);
231 uint32_t color = block->color ? *block->color : config->colors.statusline; 257 uint32_t color = block->color ? *block->color : config->colors.statusline;
258 color = block->urgent ? config->colors.urgent_workspace.text : color;
232 cairo_set_source_u32(cairo, color); 259 cairo_set_source_u32(cairo, color);
233 pango_printf(cairo, config->font, output->scale, 260 pango_printf(cairo, config->font, output->scale,
234 block->markup, "%s", block->full_text); 261 block->markup, "%s", block->full_text);
235 pos += width; 262 x_pos += width;
236 263
237 if (block->border && block->border_right > 0) { 264 if (block->border && block->border_right > 0) {
238 pos += margin; 265 x_pos += margin;
239 render_sharp_line(cairo, block->border, 266 render_sharp_line(cairo, border_color, x_pos, y_pos,
240 pos - 0.5 * output->scale, output->scale, 267 block->border_right * output->scale, render_height);
241 block->border_right, height); 268 x_pos += block->border_right * output->scale;
242 pos += block->border_right;
243 } 269 }
244 270
245 if (!edge && block->separator) { 271 if (!edge && block->separator) {
@@ -249,16 +275,14 @@ static uint32_t render_status_block(cairo_t *cairo,
249 cairo_set_source_u32(cairo, config->colors.separator); 275 cairo_set_source_u32(cairo, config->colors.separator);
250 } 276 }
251 if (config->sep_symbol) { 277 if (config->sep_symbol) {
252 offset = pos + (block->separator_block_width - sep_width) / 2; 278 offset = x_pos + (sep_block_width - sep_width) / 2;
253 cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0); 279 cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0);
254 pango_printf(cairo, config->font, output->scale, false, 280 pango_printf(cairo, config->font, output->scale, false,
255 "%s", config->sep_symbol); 281 "%s", config->sep_symbol);
256 } else { 282 } else {
257 cairo_set_line_width(cairo, 1); 283 cairo_set_line_width(cairo, 1);
258 cairo_move_to(cairo, 284 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
259 pos + block->separator_block_width / 2, margin); 285 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
260 cairo_line_to(cairo,
261 pos + block->separator_block_width / 2, height - margin);
262 cairo_stroke(cairo); 286 cairo_stroke(cairo);
263 } 287 }
264 } 288 }
@@ -268,7 +292,7 @@ static uint32_t render_status_block(cairo_t *cairo,
268static uint32_t render_status_line_i3bar(cairo_t *cairo, 292static uint32_t render_status_line_i3bar(cairo_t *cairo,
269 struct swaybar_output *output, double *x) { 293 struct swaybar_output *output, double *x) {
270 uint32_t max_height = 0; 294 uint32_t max_height = 0;
271 bool edge = true; 295 bool edge = *x == output->width * output->scale;
272 struct i3bar_block *block; 296 struct i3bar_block *block;
273 wl_list_for_each(block, &output->bar->status->blocks, link) { 297 wl_list_for_each(block, &output->bar->status->blocks, link) {
274 uint32_t h = render_status_block(cairo, output, block, x, edge); 298 uint32_t h = render_status_block(cairo, output, block, x, edge);
@@ -314,7 +338,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
314 uint32_t ideal_height = text_height + ws_vertical_padding * 2 338 uint32_t ideal_height = text_height + ws_vertical_padding * 2
315 + border_width * 2; 339 + border_width * 2;
316 uint32_t ideal_surface_height = ideal_height / output->scale; 340 uint32_t ideal_surface_height = ideal_height / output->scale;
317 if (output->height < ideal_surface_height) { 341 if (!output->bar->config->height &&
342 output->height < ideal_surface_height) {
318 return ideal_surface_height; 343 return ideal_surface_height;
319 } 344 }
320 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 345 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
@@ -342,22 +367,10 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
342 return output->height; 367 return output->height;
343} 368}
344 369
345static const char *strip_workspace_number(const char *ws_name) { 370static enum hotspot_event_handling workspace_hotspot_callback(
346 size_t len = strlen(ws_name); 371 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
347 for (size_t i = 0; i < len; ++i) { 372 int x, int y, uint32_t button, void *data) {
348 if (ws_name[i] < '0' || ws_name[i] > '9') { 373 if (button != BTN_LEFT) {
349 if (':' == ws_name[i] && i < len - 1 && i > 0) {
350 return ws_name + i + 1;
351 }
352 return ws_name;
353 }
354 }
355 return ws_name;
356}
357
358static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_output *output,
359 int x, int y, enum x11_button button, void *data) {
360 if (button != LEFT) {
361 return HOTSPOT_PROCESS; 374 return HOTSPOT_PROCESS;
362 } 375 }
363 ipc_send_workspace_command(output->bar, (const char *)data); 376 ipc_send_workspace_command(output->bar, (const char *)data);
@@ -368,11 +381,6 @@ static uint32_t render_workspace_button(cairo_t *cairo,
368 struct swaybar_output *output, 381 struct swaybar_output *output,
369 struct swaybar_workspace *ws, double *x) { 382 struct swaybar_workspace *ws, double *x) {
370 struct swaybar_config *config = output->bar->config; 383 struct swaybar_config *config = output->bar->config;
371 const char *name = ws->name;
372 if (config->strip_workspace_numbers) {
373 name = strip_workspace_number(ws->name);
374 }
375
376 struct box_colors box_colors; 384 struct box_colors box_colors;
377 if (ws->urgent) { 385 if (ws->urgent) {
378 box_colors = config->colors.urgent_workspace; 386 box_colors = config->colors.urgent_workspace;
@@ -388,7 +396,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
388 396
389 int text_width, text_height; 397 int text_width, text_height;
390 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 398 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
391 output->scale, config->pango_markup, "%s", name); 399 output->scale, config->pango_markup, "%s", ws->label);
392 400
393 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 401 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
394 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 402 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale;
@@ -397,7 +405,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
397 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 405 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
398 + border_width * 2; 406 + border_width * 2;
399 uint32_t ideal_surface_height = ideal_height / output->scale; 407 uint32_t ideal_surface_height = ideal_height / output->scale;
400 if (output->height < ideal_surface_height) { 408 if (!output->bar->config->height &&
409 output->height < ideal_surface_height) {
401 return ideal_surface_height; 410 return ideal_surface_height;
402 } 411 }
403 412
@@ -421,7 +430,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
421 cairo_set_source_u32(cairo, box_colors.text); 430 cairo_set_source_u32(cairo, box_colors.text);
422 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 431 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
423 pango_printf(cairo, config->font, output->scale, config->pango_markup, 432 pango_printf(cairo, config->font, output->scale, config->pango_markup,
424 "%s", name); 433 "%s", ws->label);
425 434
426 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 435 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
427 hotspot->x = *x; 436 hotspot->x = *x;
@@ -459,6 +468,12 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
459 * utilize the available space. 468 * utilize the available space.
460 */ 469 */
461 double x = output->width * output->scale; 470 double x = output->width * output->scale;
471#if HAVE_TRAY
472 if (bar->tray) {
473 uint32_t h = render_tray(cairo, output, &x);
474 max_height = h > max_height ? h : max_height;
475 }
476#endif
462 if (bar->status) { 477 if (bar->status) {
463 uint32_t h = render_status_line(cairo, output, &x); 478 uint32_t h = render_status_line(cairo, output, &x);
464 max_height = h > max_height ? h : max_height; 479 max_height = h > max_height ? h : max_height;
@@ -518,12 +533,17 @@ void render_frame(struct swaybar_output *output) {
518 cairo_restore(cairo); 533 cairo_restore(cairo);
519 uint32_t height = render_to_cairo(cairo, output); 534 uint32_t height = render_to_cairo(cairo, output);
520 int config_height = output->bar->config->height; 535 int config_height = output->bar->config->height;
521 if (config_height >= 0 && height < (uint32_t)config_height) { 536 if (config_height > 0) {
522 height = config_height; 537 height = config_height;
523 } 538 }
524 if (height != output->height || output->width == 0) { 539 if (height != output->height || output->width == 0) {
525 // Reconfigure surface 540 // Reconfigure surface
526 zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height); 541 zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height);
542 zwlr_layer_surface_v1_set_margin(output->layer_surface,
543 output->bar->config->gaps.top,
544 output->bar->config->gaps.right,
545 output->bar->config->gaps.bottom,
546 output->bar->config->gaps.left);
527 if (strcmp(output->bar->config->mode, "dock") == 0) { 547 if (strcmp(output->bar->config->mode, "dock") == 0) {
528 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); 548 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height);
529 } 549 }
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 744d2785..f0e2c300 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -12,7 +12,6 @@
12#include "swaybar/config.h" 12#include "swaybar/config.h"
13#include "swaybar/i3bar.h" 13#include "swaybar/i3bar.h"
14#include "swaybar/status_line.h" 14#include "swaybar/status_line.h"
15#include "readline.h"
16 15
17static void status_line_close_fds(struct status_line *status) { 16static void status_line_close_fds(struct status_line *status) {
18 if (status->read_fd != -1) { 17 if (status->read_fd != -1) {
@@ -185,7 +184,6 @@ void status_line_free(struct status_line *status) {
185 } 184 }
186 free(status->read); 185 free(status->read);
187 free(status->write); 186 free(status->write);
188 free((char*) status->text);
189 free(status->buffer); 187 free(status->buffer);
190 free(status); 188 free(status);
191} 189}
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
new file mode 100644
index 00000000..cc8dd188
--- /dev/null
+++ b/swaybar/tray/host.c
@@ -0,0 +1,210 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7#include "swaybar/bar.h"
8#include "swaybar/tray/host.h"
9#include "swaybar/tray/item.h"
10#include "swaybar/tray/tray.h"
11#include "list.h"
12#include "log.h"
13
14static const char *watcher_path = "/StatusNotifierWatcher";
15
16static int cmp_sni_id(const void *item, const void *cmp_to) {
17 const struct swaybar_sni *sni = item;
18 return strcmp(sni->watcher_id, cmp_to);
19}
20
21static void add_sni(struct swaybar_tray *tray, char *id) {
22 int idx = list_seq_find(tray->items, cmp_sni_id, id);
23 if (idx == -1) {
24 wlr_log(WLR_INFO, "Registering Status Notifier Item '%s'", id);
25 struct swaybar_sni *sni = create_sni(id, tray);
26 if (sni) {
27 list_add(tray->items, sni);
28 }
29 }
30}
31
32static int handle_sni_registered(sd_bus_message *msg, void *data,
33 sd_bus_error *error) {
34 char *id;
35 int ret = sd_bus_message_read(msg, "s", &id);
36 if (ret < 0) {
37 wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret));
38 }
39
40 struct swaybar_tray *tray = data;
41 add_sni(tray, id);
42
43 return ret;
44}
45
46static int handle_sni_unregistered(sd_bus_message *msg, void *data,
47 sd_bus_error *error) {
48 char *id;
49 int ret = sd_bus_message_read(msg, "s", &id);
50 if (ret < 0) {
51 wlr_log(WLR_ERROR, "Failed to parse unregister SNI message: %s", strerror(-ret));
52 }
53
54 struct swaybar_tray *tray = data;
55 int idx = list_seq_find(tray->items, cmp_sni_id, id);
56 if (idx != -1) {
57 wlr_log(WLR_INFO, "Unregistering Status Notifier Item '%s'", id);
58 destroy_sni(tray->items->items[idx]);
59 list_del(tray->items, idx);
60 set_bar_dirty(tray->bar);
61 }
62 return ret;
63}
64
65static int get_registered_snis_callback(sd_bus_message *msg, void *data,
66 sd_bus_error *error) {
67 if (sd_bus_message_is_method_error(msg, NULL)) {
68 sd_bus_error err = *sd_bus_message_get_error(msg);
69 wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", err.message);
70 return -sd_bus_error_get_errno(&err);
71 }
72
73 int ret = sd_bus_message_enter_container(msg, 'v', NULL);
74 if (ret < 0) {
75 wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret));
76 return ret;
77 }
78
79 char **ids;
80 ret = sd_bus_message_read_strv(msg, &ids);
81 if (ret < 0) {
82 wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret));
83 return ret;
84 }
85
86 if (ids) {
87 struct swaybar_tray *tray = data;
88 for (char **id = ids; *id; ++id) {
89 add_sni(tray, *id);
90 }
91 }
92
93 return ret;
94}
95
96static bool register_to_watcher(struct swaybar_host *host) {
97 // this is called asynchronously in case the watcher is owned by this process
98 int ret = sd_bus_call_method_async(host->tray->bus, NULL,
99 host->watcher_interface, watcher_path, host->watcher_interface,
100 "RegisterStatusNotifierHost", NULL, NULL, "s", host->service);
101 if (ret < 0) {
102 wlr_log(WLR_ERROR, "Failed to send register call: %s", strerror(-ret));
103 return false;
104 }
105
106 ret = sd_bus_call_method_async(host->tray->bus, NULL,
107 host->watcher_interface, watcher_path,
108 "org.freedesktop.DBus.Properties", "Get",
109 get_registered_snis_callback, host->tray, "ss",
110 host->watcher_interface, "RegisteredStatusNotifierItems");
111 if (ret < 0) {
112 wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", strerror(-ret));
113 }
114
115 return ret >= 0;
116}
117
118static int handle_new_watcher(sd_bus_message *msg,
119 void *data, sd_bus_error *error) {
120 char *service, *old_owner, *new_owner;
121 int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner);
122 if (ret < 0) {
123 wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret));
124 return ret;
125 }
126
127 if (!*old_owner) {
128 struct swaybar_host *host = data;
129 if (strcmp(service, host->watcher_interface) == 0) {
130 register_to_watcher(host);
131 }
132 }
133
134 return 0;
135}
136
137bool init_host(struct swaybar_host *host, char *protocol,
138 struct swaybar_tray *tray) {
139 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1;
140 host->watcher_interface = malloc(len);
141 if (!host->watcher_interface) {
142 return false;
143 }
144 snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol);
145
146 sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL;
147 int ret = sd_bus_match_signal(tray->bus, &reg_slot, host->watcher_interface,
148 watcher_path, host->watcher_interface,
149 "StatusNotifierItemRegistered", handle_sni_registered, tray);
150 if (ret < 0) {
151 wlr_log(WLR_ERROR, "Failed to subscribe to registering events: %s",
152 strerror(-ret));
153 goto error;
154 }
155 ret = sd_bus_match_signal(tray->bus, &unreg_slot, host->watcher_interface,
156 watcher_path, host->watcher_interface,
157 "StatusNotifierItemUnregistered", handle_sni_unregistered, tray);
158 if (ret < 0) {
159 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
160 strerror(-ret));
161 goto error;
162 }
163
164 ret = sd_bus_match_signal(tray->bus, &watcher_slot, "org.freedesktop.DBus",
165 "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged",
166 handle_new_watcher, host);
167 if (ret < 0) {
168 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
169 strerror(-ret));
170 goto error;
171 }
172
173 pid_t pid = getpid();
174 size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d",
175 protocol, pid) + 1;
176 host->service = malloc(service_len);
177 if (!host->service) {
178 goto error;
179 }
180 snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid);
181 ret = sd_bus_request_name(tray->bus, host->service, 0);
182 if (ret < 0) {
183 wlr_log(WLR_DEBUG, "Failed to acquire service name: %s", strerror(-ret));
184 goto error;
185 }
186
187 host->tray = tray;
188 if (!register_to_watcher(host)) {
189 goto error;
190 }
191
192 sd_bus_slot_set_floating(reg_slot, 1);
193 sd_bus_slot_set_floating(unreg_slot, 1);
194 sd_bus_slot_set_floating(watcher_slot, 1);
195
196 wlr_log(WLR_DEBUG, "Registered %s", host->service);
197 return true;
198error:
199 sd_bus_slot_unref(reg_slot);
200 sd_bus_slot_unref(unreg_slot);
201 sd_bus_slot_unref(watcher_slot);
202 finish_host(host);
203 return false;
204}
205
206void finish_host(struct swaybar_host *host) {
207 sd_bus_release_name(host->tray->bus, host->service);
208 free(host->service);
209 free(host->watcher_interface);
210}
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
new file mode 100644
index 00000000..67805858
--- /dev/null
+++ b/swaybar/tray/icon.c
@@ -0,0 +1,462 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h>
3#include <dirent.h>
4#include <stdbool.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <sys/stat.h>
9#include <unistd.h>
10#include <wordexp.h>
11#include "swaybar/tray/icon.h"
12#include "config.h"
13#include "list.h"
14#include "log.h"
15#include "stringop.h"
16
17static bool dir_exists(char *path) {
18 struct stat sb;
19 return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
20}
21
22static list_t *get_basedirs(void) {
23 list_t *basedirs = create_list();
24 list_add(basedirs, strdup("$HOME/.icons")); // deprecated
25
26 char *data_home = getenv("XDG_DATA_HOME");
27 list_add(basedirs, strdup(data_home && *data_home ?
28 "$XDG_DATA_HOME/icons" : "$HOME/.local/share/icons"));
29
30 list_add(basedirs, strdup("/usr/share/pixmaps"));
31
32 char *data_dirs = getenv("XDG_DATA_DIRS");
33 if (!(data_dirs && *data_dirs)) {
34 data_dirs = "/usr/local/share:/usr/share";
35 }
36 data_dirs = strdup(data_dirs);
37 char *dir = strtok(data_dirs, ":");
38 do {
39 size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1;
40 char *path = malloc(path_len);
41 snprintf(path, path_len, "%s/icons", dir);
42 list_add(basedirs, path);
43 } while ((dir = strtok(NULL, ":")));
44 free(data_dirs);
45
46 list_t *basedirs_expanded = create_list();
47 for (int i = 0; i < basedirs->length; ++i) {
48 wordexp_t p;
49 if (wordexp(basedirs->items[i], &p, WRDE_UNDEF) == 0) {
50 if (dir_exists(p.we_wordv[0])) {
51 list_add(basedirs_expanded, strdup(p.we_wordv[0]));
52 }
53 wordfree(&p);
54 }
55 }
56
57 list_free_items_and_destroy(basedirs);
58
59 return basedirs_expanded;
60}
61
62static void destroy_theme(struct icon_theme *theme) {
63 if (!theme) {
64 return;
65 }
66 free(theme->name);
67 free(theme->comment);
68 free(theme->inherits);
69 list_free_items_and_destroy(theme->directories);
70 free(theme->dir);
71
72 for (int i = 0; i < theme->subdirs->length; ++i) {
73 struct icon_theme_subdir *subdir = theme->subdirs->items[i];
74 free(subdir->name);
75 free(subdir);
76 }
77 list_free(theme->subdirs);
78 free(theme);
79}
80
81static int cmp_group(const void *item, const void *cmp_to) {
82 return strcmp(item, cmp_to);
83}
84
85static bool group_handler(char *old_group, char *new_group,
86 struct icon_theme *theme) {
87 if (!old_group) { // first group must be "Icon Theme"
88 return strcmp(new_group, "Icon Theme");
89 }
90
91 if (strcmp(old_group, "Icon Theme") == 0) {
92 if (!(theme->name && theme->comment && theme->directories)) {
93 return true;
94 }
95 } else {
96 if (theme->subdirs->length == 0) { // skip
97 return false;
98 }
99
100 struct icon_theme_subdir *subdir =
101 theme->subdirs->items[theme->subdirs->length - 1];
102 if (!subdir->size) return true;
103
104 switch (subdir->type) {
105 case FIXED: subdir->max_size = subdir->min_size = subdir->size;
106 break;
107 case SCALABLE: {
108 if (!subdir->max_size) subdir->max_size = subdir->size;
109 if (!subdir->min_size) subdir->min_size = subdir->size;
110 break;
111 }
112 case THRESHOLD:
113 subdir->max_size = subdir->size + subdir->threshold;
114 subdir->min_size = subdir->size - subdir->threshold;
115 }
116 }
117
118 if (new_group && list_seq_find(theme->directories, cmp_group, new_group) != -1) {
119 struct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir));
120 if (!subdir) {
121 return true;
122 }
123 subdir->name = strdup(new_group);
124 subdir->threshold = 2;
125 list_add(theme->subdirs, subdir);
126 }
127
128 return false;
129}
130
131static int entry_handler(char *group, char *key, char *value,
132 struct icon_theme *theme) {
133 if (strcmp(group, "Icon Theme") == 0) {
134 if (strcmp(key, "Name") == 0) {
135 theme->name = strdup(value);
136 } else if (strcmp(key, "Comment") == 0) {
137 theme->comment = strdup(value);
138 } else if (strcmp(key, "Inherists") == 0) {
139 theme->inherits = strdup(value);
140 } else if (strcmp(key, "Directories") == 0) {
141 theme->directories = split_string(value, ",");
142 } // Ignored: ScaledDirectories, Hidden, Example
143 } else {
144 if (theme->subdirs->length == 0) { // skip
145 return false;
146 }
147
148 struct icon_theme_subdir *subdir =
149 theme->subdirs->items[theme->subdirs->length - 1];
150 if (strcmp(subdir->name, group) != 0) { // skip
151 return false;
152 }
153
154 char *end;
155 int n = strtol(value, &end, 10);
156 if (strcmp(key, "Size") == 0) {
157 subdir->size = n;
158 return *end != '\0';
159 } else if (strcmp(key, "Type") == 0) {
160 if (strcmp(value, "Fixed") == 0) {
161 subdir->type = FIXED;
162 } else if (strcmp(value, "Scalable") == 0) {
163 subdir->type = SCALABLE;
164 } else if (strcmp(value, "Threshold") == 0) {
165 subdir->type = THRESHOLD;
166 } else {
167 return true;
168 }
169 } else if (strcmp(key, "MaxSize") == 0) {
170 subdir->max_size = n;
171 return *end != '\0';
172 } else if (strcmp(key, "MinSize") == 0) {
173 subdir->min_size = n;
174 return *end != '\0';
175 } else if (strcmp(key, "Threshold") == 0) {
176 subdir->threshold = n;
177 return *end != '\0';
178 } // Ignored: Scale, Applications
179 }
180 return false;
181}
182
183/*
184 * This is a Freedesktop Desktop Entry parser (essentially INI)
185 * It calls entry_handler for every entry
186 * and group_handler between every group (as well as at both ends)
187 * Handlers return whether an error occured, which stops parsing
188 */
189static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
190 // look for index.theme file
191 size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir,
192 theme_name) + 1;
193 char *path = malloc(path_len);
194 snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name);
195 FILE *theme_file = fopen(path, "r");
196 if (!theme_file) {
197 return NULL;
198 }
199
200 struct icon_theme *theme = calloc(1, sizeof(struct icon_theme));
201 if (!theme) {
202 return NULL;
203 }
204 theme->subdirs = create_list();
205
206 bool error = false;
207 char *group = NULL;
208 char *full_line = NULL;
209 size_t full_len = 0;
210 ssize_t nread;
211 while ((nread = getline(&full_line, &full_len, theme_file)) != -1) {
212 char *line = full_line - 1;
213 while (isspace(*++line)) {} // remove leading whitespace
214 if (!*line || line[0] == '#') continue; // ignore blank lines & comments
215
216 int len = nread - (line - full_line);
217 while (isspace(line[--len])) {}
218 line[++len] = '\0'; // remove trailing whitespace
219
220 if (line[0] == '[') { // group header
221 // check well-formed
222 if (line[--len] != ']') {
223 error = true;
224 break;
225 }
226 int i = 1;
227 for (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {}
228 if (i < len) {
229 error = true;
230 break;
231 }
232
233 // call handler
234 line[len] = '\0';
235 error = group_handler(group, &line[1], theme);
236 if (error) {
237 break;
238 }
239 free(group);
240 group = strdup(&line[1]);
241 } else { // key-value pair
242 // check well-formed
243 int eok = 0;
244 for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale?
245 int i = eok - 1;
246 while (isspace(line[++i])) {}
247 if (line[i] != '=') {
248 error = true;
249 break;
250 }
251
252 line[eok] = '\0'; // split into key-value pair
253 char *value = &line[i];
254 while (isspace(*++value)) {}
255 // TODO unescape value
256 error = entry_handler(group, line, value, theme);
257 if (error) {
258 break;
259 }
260 }
261 }
262
263 if (!error && group) {
264 error = group_handler(group, NULL, theme);
265 }
266
267 free(group);
268 free(full_line);
269 fclose(theme_file);
270
271 if (!error) {
272 theme->dir = strdup(theme_name);
273 return theme;
274 } else {
275 destroy_theme(theme);
276 return NULL;
277 }
278}
279
280static list_t *load_themes_in_dir(char *basedir) {
281 DIR *dir;
282 if (!(dir = opendir(basedir))) {
283 return NULL;
284 }
285
286 list_t *themes = create_list();
287 struct dirent *entry;
288 while ((entry = readdir(dir))) {
289 if (entry->d_name[0] == '.') continue;
290
291 struct icon_theme *theme = read_theme_file(basedir, entry->d_name);
292 if (theme) {
293 list_add(themes, theme);
294 }
295 }
296 return themes;
297}
298
299void init_themes(list_t **themes, list_t **basedirs) {
300 *basedirs = get_basedirs();
301
302 *themes = create_list();
303 for (int i = 0; i < (*basedirs)->length; ++i) {
304 list_t *dir_themes = load_themes_in_dir((*basedirs)->items[i]);
305 list_cat(*themes, dir_themes);
306 list_free(dir_themes);
307 }
308
309 list_t *theme_names = create_list();
310 for (int i = 0; i < (*themes)->length; ++i) {
311 struct icon_theme *theme = (*themes)->items[i];
312 list_add(theme_names, theme->name);
313 }
314 wlr_log(WLR_DEBUG, "Loaded themes: %s", join_list(theme_names, ", "));
315 list_free(theme_names);
316}
317
318void finish_themes(list_t *themes, list_t *basedirs) {
319 for (int i = 0; i < themes->length; ++i) {
320 destroy_theme(themes->items[i]);
321 }
322 list_free(themes);
323 list_free_items_and_destroy(basedirs);
324}
325
326static char *find_icon_in_subdir(char *name, char *basedir, char *theme,
327 char *subdir) {
328 static const char *extensions[] = {
329#if HAVE_GDK_PIXBUF
330 "svg",
331#endif
332 "png",
333#if HAVE_GDK_PIXBUF
334 "xpm"
335#endif
336 };
337
338 size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme,
339 subdir, name) + 1;
340 char *path = malloc(path_len);
341
342 for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) {
343 snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir,
344 name, extensions[i]);
345 if (access(path, R_OK) == 0) {
346 return path;
347 }
348 }
349
350 free(path);
351 return NULL;
352}
353
354static bool theme_exists_in_basedir(char *theme, char *basedir) {
355 size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1;
356 char *path = malloc(path_len);
357 snprintf(path, path_len, "%s/%s", basedir, theme);
358 bool ret = dir_exists(path);
359 free(path);
360 return ret;
361}
362
363static char *find_icon_with_theme(list_t *basedirs, list_t *themes, char *name,
364 int size, char *theme_name, int *min_size, int *max_size) {
365 struct icon_theme *theme = NULL;
366 for (int i = 0; i < themes->length; ++i) {
367 theme = themes->items[i];
368 if (strcmp(theme->name, theme_name) == 0) {
369 break;
370 }
371 theme = NULL;
372 }
373 if (!theme) return NULL;
374
375 char *icon = NULL;
376 for (int i = 0; i < basedirs->length; ++i) {
377 if (!theme_exists_in_basedir(theme->dir, basedirs->items[i])) {
378 continue;
379 }
380 // search backwards to hopefully hit scalable/larger icons first
381 for (int j = theme->subdirs->length - 1; j >= 0; --j) {
382 struct icon_theme_subdir *subdir = theme->subdirs->items[j];
383 if (size >= subdir->min_size && size <= subdir->max_size) {
384 if ((icon = find_icon_in_subdir(name, basedirs->items[i],
385 theme->dir, subdir->name))) {
386 *min_size = subdir->min_size;
387 *max_size = subdir->max_size;
388 return icon;
389 }
390 }
391 }
392 }
393
394 // inexact match
395 unsigned smallest_error = -1; // UINT_MAX
396 for (int i = 0; i < basedirs->length; ++i) {
397 if (!theme_exists_in_basedir(theme->dir, basedirs->items[i])) {
398 continue;
399 }
400 for (int j = theme->subdirs->length - 1; j >= 0; --j) {
401 struct icon_theme_subdir *subdir = theme->subdirs->items[j];
402 unsigned error = (size > subdir->max_size ? size - subdir->max_size : 0)
403 + (size < subdir->min_size ? subdir->min_size - size : 0);
404 if (error < smallest_error) {
405 char *test_icon = find_icon_in_subdir(name, basedirs->items[i],
406 theme->dir, subdir->name);
407 if (test_icon) {
408 icon = test_icon;
409 smallest_error = error;
410 *min_size = subdir->min_size;
411 *max_size = subdir->max_size;
412 }
413 }
414 }
415 }
416
417 if (!icon && theme->inherits) {
418 icon = find_icon_with_theme(basedirs, themes, name, size,
419 theme->inherits, min_size, max_size);
420 }
421
422 return icon;
423}
424
425char *find_icon_in_dir(char *name, char *dir, int *min_size, int *max_size) {
426 char *icon = find_icon_in_subdir(name, dir, "", "");
427 if (icon) {
428 *min_size = 1;
429 *max_size = 512;
430 }
431 return icon;
432
433}
434
435static char *find_fallback_icon(list_t *basedirs, char *name, int *min_size,
436 int *max_size) {
437 for (int i = 0; i < basedirs->length; ++i) {
438 char *icon = find_icon_in_dir(name, basedirs->items[i], min_size, max_size);
439 if (icon) {
440 return icon;
441 }
442 }
443 return NULL;
444}
445
446char *find_icon(list_t *themes, list_t *basedirs, char *name, int size,
447 char *theme, int *min_size, int *max_size) {
448 // TODO https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#implementation_notes
449 char *icon = NULL;
450 if (theme) {
451 icon = find_icon_with_theme(basedirs, themes, name, size, theme,
452 min_size, max_size);
453 }
454 if (!icon) {
455 icon = find_icon_with_theme(basedirs, themes, name, size, "Hicolor",
456 min_size, max_size);
457 }
458 if (!icon) {
459 icon = find_fallback_icon(basedirs, name, min_size, max_size);
460 }
461 return icon;
462}
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
new file mode 100644
index 00000000..0833dcb9
--- /dev/null
+++ b/swaybar/tray/item.c
@@ -0,0 +1,472 @@
1#define _POSIX_C_SOURCE 200809L
2#include <cairo.h>
3#include <stdbool.h>
4#include <stdlib.h>
5#include <string.h>
6#include "swaybar/bar.h"
7#include "swaybar/config.h"
8#include "swaybar/input.h"
9#include "swaybar/tray/host.h"
10#include "swaybar/tray/icon.h"
11#include "swaybar/tray/item.h"
12#include "swaybar/tray/tray.h"
13#include "background-image.h"
14#include "cairo.h"
15#include "list.h"
16#include "log.h"
17#include "wlr-layer-shell-unstable-v1-client-protocol.h"
18
19// TODO menu
20
21static bool sni_ready(struct swaybar_sni *sni) {
22 return sni->status && (sni->status[0] == 'N' ? // NeedsAttention
23 sni->attention_icon_name || sni->attention_icon_pixmap :
24 sni->icon_name || sni->icon_pixmap);
25}
26
27static void set_sni_dirty(struct swaybar_sni *sni) {
28 if (sni_ready(sni)) {
29 sni->min_size = sni->max_size = 0; // invalidate previous icon
30 set_bar_dirty(sni->tray->bar);
31 }
32}
33
34static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni,
35 const char *prop, list_t **dest) {
36 int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)");
37 if (ret < 0) {
38 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
39 return ret;
40 }
41
42 if (sd_bus_message_at_end(msg, 0)) {
43 wlr_log(WLR_DEBUG, "%s %s no. of icons = 0", sni->watcher_id, prop);
44 return ret;
45 }
46
47 list_t *pixmaps = create_list();
48 if (!pixmaps) {
49 return -12; // -ENOMEM
50 }
51
52 while (!sd_bus_message_at_end(msg, 0)) {
53 ret = sd_bus_message_enter_container(msg, 'r', "iiay");
54 if (ret < 0) {
55 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
56 goto error;
57 }
58
59 int size;
60 ret = sd_bus_message_read(msg, "ii", NULL, &size);
61 if (ret < 0) {
62 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
63 goto error;
64 }
65
66 const void *pixels;
67 size_t npixels;
68 ret = sd_bus_message_read_array(msg, 'y', &pixels, &npixels);
69 if (ret < 0) {
70 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
71 goto error;
72 }
73
74 struct swaybar_pixmap *pixmap =
75 malloc(sizeof(struct swaybar_pixmap) + npixels);
76 pixmap->size = size;
77 memcpy(pixmap->pixels, pixels, npixels);
78 list_add(pixmaps, pixmap);
79
80 sd_bus_message_exit_container(msg);
81 }
82 list_free_items_and_destroy(*dest);
83 *dest = pixmaps;
84 wlr_log(WLR_DEBUG, "%s %s no. of icons = %d", sni->watcher_id, prop,
85 pixmaps->length);
86
87 return ret;
88error:
89 list_free_items_and_destroy(pixmaps);
90 return ret;
91}
92
93struct get_property_data {
94 struct swaybar_sni *sni;
95 const char *prop;
96 const char *type;
97 void *dest;
98};
99
100static int get_property_callback(sd_bus_message *msg, void *data,
101 sd_bus_error *error) {
102 struct get_property_data *d = data;
103 struct swaybar_sni *sni = d->sni;
104 const char *prop = d->prop;
105 const char *type = d->type;
106 void *dest = d->dest;
107
108 int ret;
109 if (sd_bus_message_is_method_error(msg, NULL)) {
110 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop,
111 sd_bus_message_get_error(msg)->message);
112 ret = sd_bus_message_get_errno(msg);
113 goto cleanup;
114 }
115
116 ret = sd_bus_message_enter_container(msg, 'v', type);
117 if (ret < 0) {
118 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
119 goto cleanup;
120 }
121
122 if (!type) {
123 ret = read_pixmap(msg, sni, prop, dest);
124 if (ret < 0) {
125 goto cleanup;
126 }
127 } else {
128 if (*type == 's' || *type == 'o') {
129 free(*(char **)dest);
130 }
131
132 ret = sd_bus_message_read(msg, type, dest);
133 if (ret < 0) {
134 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
135 goto cleanup;
136 }
137
138 if (*type == 's' || *type == 'o') {
139 char **str = dest;
140 *str = strdup(*str);
141 wlr_log(WLR_DEBUG, "%s %s = '%s'", sni->watcher_id, prop, *str);
142 } else if (*type == 'b') {
143 wlr_log(WLR_DEBUG, "%s %s = %s", sni->watcher_id, prop,
144 *(bool *)dest ? "true" : "false");
145 }
146 }
147
148 if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ?
149 prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) {
150 set_sni_dirty(sni);
151 }
152cleanup:
153 free(data);
154 return ret;
155}
156
157static void sni_get_property_async(struct swaybar_sni *sni, const char *prop,
158 const char *type, void *dest) {
159 struct get_property_data *data = malloc(sizeof(struct get_property_data));
160 data->sni = sni;
161 data->prop = prop;
162 data->type = type;
163 data->dest = dest;
164 int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service,
165 sni->path, "org.freedesktop.DBus.Properties", "Get",
166 get_property_callback, data, "ss", sni->interface, prop);
167 if (ret < 0) {
168 wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret));
169 }
170}
171
172/*
173 * There is a quirk in sd-bus that in some systems, it is unable to get the
174 * well-known names on the bus, so it cannot identify if an incoming signal,
175 * which uses the sender's unique name, actually matches the callback's matching
176 * sender if the callback uses a well-known name, in which case it just calls
177 * the callback and hopes for the best, resulting in false positives. In the
178 * case of NewIcon & NewAttentionIcon, this doesn't affect anything, but it
179 * means that for NewStatus, if the SNI does not definitely match the sender,
180 * then the safe thing to do is to query the status independently.
181 * This function returns 1 if the SNI definitely matches the signal sender,
182 * which is returned by the calling function to indicate that signal matching
183 * can stop since it has already found the required callback, otherwise, it
184 * returns 0, which allows matching to continue.
185 */
186static int sni_check_msg_sender(struct swaybar_sni *sni, sd_bus_message *msg,
187 const char *signal) {
188 bool has_well_known_names =
189 sd_bus_creds_get_mask(sd_bus_message_get_creds(msg)) & SD_BUS_CREDS_WELL_KNOWN_NAMES;
190 if (sni->service[0] == ':' || has_well_known_names) {
191 wlr_log(WLR_DEBUG, "%s has new %s", sni->watcher_id, signal);
192 return 1;
193 } else {
194 wlr_log(WLR_DEBUG, "%s may have new %s", sni->watcher_id, signal);
195 return 0;
196 }
197}
198
199static int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) {
200 struct swaybar_sni *sni = data;
201 sni_get_property_async(sni, "IconName", "s", &sni->icon_name);
202 sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap);
203 return sni_check_msg_sender(sni, msg, "icon");
204}
205
206static int handle_new_attention_icon(sd_bus_message *msg, void *data,
207 sd_bus_error *error) {
208 struct swaybar_sni *sni = data;
209 sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name);
210 sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap);
211 return sni_check_msg_sender(sni, msg, "attention icon");
212}
213
214static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) {
215 struct swaybar_sni *sni = data;
216 int ret = sni_check_msg_sender(sni, msg, "status");
217 if (ret == 1) {
218 char *status;
219 int r = sd_bus_message_read(msg, "s", &status);
220 if (r < 0) {
221 wlr_log(WLR_ERROR, "%s new status error: %s", sni->watcher_id, strerror(-ret));
222 ret = r;
223 } else {
224 free(sni->status);
225 sni->status = strdup(status);
226 wlr_log(WLR_DEBUG, "%s has new status = '%s'", sni->watcher_id, status);
227 set_sni_dirty(sni);
228 }
229 } else {
230 sni_get_property_async(sni, "Status", "s", &sni->status);
231 }
232
233 return ret;
234}
235
236static void sni_match_signal(struct swaybar_sni *sni, sd_bus_slot **slot,
237 char *signal, sd_bus_message_handler_t callback) {
238 int ret = sd_bus_match_signal(sni->tray->bus, slot, sni->service, sni->path,
239 sni->interface, signal, callback, sni);
240 if (ret < 0) {
241 wlr_log(WLR_ERROR, "Failed to subscribe to signal %s: %s", signal,
242 strerror(-ret));
243 }
244}
245
246struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) {
247 struct swaybar_sni *sni = calloc(1, sizeof(struct swaybar_sni));
248 if (!sni) {
249 return NULL;
250 }
251 sni->tray = tray;
252 sni->watcher_id = strdup(id);
253 char *path_ptr = strchr(id, '/');
254 if (!path_ptr) {
255 sni->service = strdup(id);
256 sni->path = strdup("/StatusNotifierItem");
257 sni->interface = "org.freedesktop.StatusNotifierItem";
258 } else {
259 sni->service = strndup(id, path_ptr - id);
260 sni->path = strdup(path_ptr);
261 sni->interface = "org.kde.StatusNotifierItem";
262 sni_get_property_async(sni, "IconThemePath", "s", &sni->icon_theme_path);
263 }
264
265 // Ignored: Category, Id, Title, WindowId, OverlayIconName,
266 // OverlayIconPixmap, AttentionMovieName, ToolTip
267 sni_get_property_async(sni, "Status", "s", &sni->status);
268 sni_get_property_async(sni, "IconName", "s", &sni->icon_name);
269 sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap);
270 sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name);
271 sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap);
272 sni_get_property_async(sni, "ItemIsMenu", "b", &sni->item_is_menu);
273 sni_get_property_async(sni, "Menu", "o", &sni->menu);
274
275 sni_match_signal(sni, &sni->new_icon_slot, "NewIcon", handle_new_icon);
276 sni_match_signal(sni, &sni->new_attention_icon_slot, "NewAttentionIcon",
277 handle_new_attention_icon);
278 sni_match_signal(sni, &sni->new_status_slot, "NewStatus", handle_new_status);
279
280 return sni;
281}
282
283void destroy_sni(struct swaybar_sni *sni) {
284 if (!sni) {
285 return;
286 }
287
288 sd_bus_slot_unref(sni->new_icon_slot);
289 sd_bus_slot_unref(sni->new_attention_icon_slot);
290 sd_bus_slot_unref(sni->new_status_slot);
291
292 free(sni->watcher_id);
293 free(sni->service);
294 free(sni->path);
295 free(sni->status);
296 free(sni->icon_name);
297 free(sni->icon_pixmap);
298 free(sni->attention_icon_name);
299 free(sni->menu);
300 free(sni);
301}
302
303static void handle_click(struct swaybar_sni *sni, int x, int y,
304 enum x11_button button, int delta) {
305 const char *method = sni->tray->bar->config->tray_bindings[button];
306 if (!method) {
307 static const char *default_bindings[10] = {
308 "nop",
309 "Activate",
310 "SecondaryActivate",
311 "ContextMenu",
312 "ScrollUp",
313 "ScrollDown",
314 "ScrollLeft",
315 "ScrollRight",
316 "nop",
317 "nop"
318 };
319 method = default_bindings[button];
320 }
321 if (strcmp(method, "nop") == 0) {
322 return;
323 }
324 if (sni->item_is_menu && strcmp(method, "Activate") == 0) {
325 method = "ContextMenu";
326 }
327
328 if (strncmp(method, "Scroll", strlen("Scroll")) == 0) {
329 char dir = method[strlen("Scroll")];
330 char *orientation = (dir = 'U' || dir == 'D') ? "vertical" : "horizontal";
331 int sign = (dir == 'U' || dir == 'L') ? -1 : 1;
332
333 sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->path,
334 sni->interface, "Scroll", NULL, NULL, "is", delta*sign, orientation);
335 } else {
336 sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->path,
337 sni->interface, method, NULL, NULL, "ii", x, y);
338 }
339}
340
341static int cmp_sni_id(const void *item, const void *cmp_to) {
342 const struct swaybar_sni *sni = item;
343 return strcmp(sni->watcher_id, cmp_to);
344}
345
346static enum hotspot_event_handling icon_hotspot_callback(
347 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
348 int x, int y, enum x11_button button, void *data) {
349 wlr_log(WLR_DEBUG, "Clicked on %s", (char *)data);
350
351 struct swaybar_tray *tray = output->bar->tray;
352 int idx = list_seq_find(tray->items, cmp_sni_id, data);
353
354 if (idx != -1) {
355 struct swaybar_sni *sni = tray->items->items[idx];
356 // guess global position since wayland doesn't expose it
357 struct swaybar_config *config = tray->bar->config;
358 int global_x = output->output_x + config->gaps.left + x;
359 bool top_bar = config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
360 int global_y = output->output_y + (top_bar ? config->gaps.top + y:
361 (int) output->output_height - config->gaps.bottom - y);
362
363 wlr_log(WLR_DEBUG, "Guessing click position at (%d, %d)", global_x, global_y);
364 handle_click(sni, global_x, global_y, button, 1); // TODO get delta from event
365 return HOTSPOT_IGNORE;
366 } else {
367 wlr_log(WLR_DEBUG, "but it doesn't exist");
368 }
369
370 return HOTSPOT_PROCESS;
371}
372
373uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
374 struct swaybar_sni *sni) {
375 uint32_t height = output->height * output->scale;
376 int padding = output->bar->config->tray_padding;
377 int ideal_size = height - 2*padding;
378 if ((ideal_size < sni->min_size || ideal_size > sni->max_size) && sni_ready(sni)) {
379 bool icon_found = false;
380 char *icon_name = sni->status[0] == 'N' ?
381 sni->attention_icon_name : sni->icon_name;
382 if (icon_name) {
383 char *icon_path = find_icon(sni->tray->themes, sni->tray->basedirs,
384 icon_name, ideal_size, output->bar->config->icon_theme,
385 &sni->min_size, &sni->max_size);
386 if (!icon_path && sni->icon_theme_path) {
387 icon_path = find_icon_in_dir(icon_name, sni->icon_theme_path,
388 &sni->min_size, &sni->max_size);
389 }
390 if (icon_path) {
391 cairo_surface_destroy(sni->icon);
392 sni->icon = load_background_image(icon_path);
393 free(icon_path);
394 icon_found = true;
395 }
396 }
397 if (!icon_found) {
398 list_t *pixmaps = sni->status[0] == 'N' ?
399 sni->attention_icon_pixmap : sni->icon_pixmap;
400 if (pixmaps) {
401 int idx = -1;
402 unsigned smallest_error = -1; // UINT_MAX
403 for (int i = 0; i < pixmaps->length; ++i) {
404 struct swaybar_pixmap *pixmap = pixmaps->items[i];
405 unsigned error = (ideal_size - pixmap->size) *
406 (ideal_size < pixmap->size ? -1 : 1);
407 if (error < smallest_error) {
408 smallest_error = error;
409 idx = i;
410 }
411 }
412 struct swaybar_pixmap *pixmap = pixmaps->items[idx];
413 cairo_surface_destroy(sni->icon);
414 sni->icon = cairo_image_surface_create_for_data(pixmap->pixels,
415 CAIRO_FORMAT_ARGB32, pixmap->size, pixmap->size,
416 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixmap->size));
417 }
418 }
419 }
420
421 int icon_size;
422 cairo_surface_t *icon;
423 if (sni->icon) {
424 int actual_size = cairo_image_surface_get_height(sni->icon);
425 icon_size = actual_size < ideal_size ?
426 actual_size*(ideal_size/actual_size) : ideal_size;
427 icon = cairo_image_surface_scale(sni->icon, icon_size, icon_size);
428 } else { // draw a :(
429 icon_size = ideal_size*0.8;
430 icon = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, icon_size, icon_size);
431 cairo_t *cairo_icon = cairo_create(icon);
432 cairo_set_source_u32(cairo_icon, 0xFF0000FF);
433 cairo_translate(cairo_icon, icon_size/2, icon_size/2);
434 cairo_scale(cairo_icon, icon_size/2, icon_size/2);
435 cairo_arc(cairo_icon, 0, 0, 1, 0, 7);
436 cairo_fill(cairo_icon);
437 cairo_set_operator(cairo_icon, CAIRO_OPERATOR_CLEAR);
438 cairo_arc(cairo_icon, 0.35, -0.3, 0.1, 0, 7);
439 cairo_fill(cairo_icon);
440 cairo_arc(cairo_icon, -0.35, -0.3, 0.1, 0, 7);
441 cairo_fill(cairo_icon);
442 cairo_arc(cairo_icon, 0, 0.75, 0.5, 3.71238898038469, 5.71238898038469);
443 cairo_set_line_width(cairo_icon, 0.1);
444 cairo_stroke(cairo_icon);
445 cairo_destroy(cairo_icon);
446 }
447
448 int padded_size = icon_size + 2*padding;
449 *x -= padded_size;
450 int y = floor((height - padded_size) / 2.0);
451
452 cairo_operator_t op = cairo_get_operator(cairo);
453 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
454 cairo_set_source_surface(cairo, icon, *x + padding, y + padding);
455 cairo_rectangle(cairo, *x, y, padded_size, padded_size);
456 cairo_fill(cairo);
457 cairo_set_operator(cairo, op);
458
459 cairo_surface_destroy(icon);
460
461 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
462 hotspot->x = *x;
463 hotspot->y = 0;
464 hotspot->width = height;
465 hotspot->height = height;
466 hotspot->callback = icon_hotspot_callback;
467 hotspot->destroy = free;
468 hotspot->data = strdup(sni->watcher_id);
469 wl_list_insert(&output->hotspots, &hotspot->link);
470
471 return output->height;
472}
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
new file mode 100644
index 00000000..d5d0c84e
--- /dev/null
+++ b/swaybar/tray/tray.c
@@ -0,0 +1,131 @@
1#include <cairo.h>
2#include <stdint.h>
3#include <stdlib.h>
4#include <string.h>
5#include "swaybar/config.h"
6#include "swaybar/bar.h"
7#include "swaybar/tray/icon.h"
8#include "swaybar/tray/host.h"
9#include "swaybar/tray/item.h"
10#include "swaybar/tray/tray.h"
11#include "swaybar/tray/watcher.h"
12#include "list.h"
13#include "log.h"
14
15static int handle_lost_watcher(sd_bus_message *msg,
16 void *data, sd_bus_error *error) {
17 char *service, *old_owner, *new_owner;
18 int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner);
19 if (ret < 0) {
20 wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret));
21 return ret;
22 }
23
24 if (!*new_owner) {
25 struct swaybar_tray *tray = data;
26 if (strcmp(service, "org.freedesktop.StatusNotifierWatcher") == 0) {
27 tray->watcher_xdg = create_watcher("freedesktop", tray->bus);
28 } else if (strcmp(service, "org.kde.StatusNotifierWatcher") == 0) {
29 tray->watcher_kde = create_watcher("kde", tray->bus);
30 }
31 }
32
33 return 0;
34}
35
36struct swaybar_tray *create_tray(struct swaybar *bar) {
37 wlr_log(WLR_DEBUG, "Initializing tray");
38
39 sd_bus *bus;
40 int ret = sd_bus_open_user(&bus);
41 if (ret < 0) {
42 wlr_log(WLR_ERROR, "Failed to connect to user bus: %s", strerror(-ret));
43 return NULL;
44 }
45
46 struct swaybar_tray *tray = calloc(1, sizeof(struct swaybar_tray));
47 if (!tray) {
48 return NULL;
49 }
50 tray->bar = bar;
51 tray->bus = bus;
52 tray->fd = sd_bus_get_fd(tray->bus);
53
54 tray->watcher_xdg = create_watcher("freedesktop", tray->bus);
55 tray->watcher_kde = create_watcher("kde", tray->bus);
56
57 ret = sd_bus_match_signal(bus, NULL, "org.freedesktop.DBus",
58 "/org/freedesktop/DBus", "org.freedesktop.DBus",
59 "NameOwnerChanged", handle_lost_watcher, tray);
60 if (ret < 0) {
61 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
62 strerror(-ret));
63 }
64
65 tray->items = create_list();
66
67 init_host(&tray->host_xdg, "freedesktop", tray);
68 init_host(&tray->host_kde, "kde", tray);
69
70 init_themes(&tray->themes, &tray->basedirs);
71
72 return tray;
73}
74
75void destroy_tray(struct swaybar_tray *tray) {
76 if (!tray) {
77 return;
78 }
79 finish_host(&tray->host_xdg);
80 finish_host(&tray->host_kde);
81 for (int i = 0; i < tray->items->length; ++i) {
82 destroy_sni(tray->items->items[i]);
83 }
84 list_free(tray->items);
85 destroy_watcher(tray->watcher_xdg);
86 destroy_watcher(tray->watcher_kde);
87 sd_bus_flush_close_unref(tray->bus);
88 finish_themes(tray->themes, tray->basedirs);
89 free(tray);
90}
91
92void tray_in(int fd, short mask, void *data) {
93 sd_bus *bus = data;
94 int ret;
95 while ((ret = sd_bus_process(bus, NULL)) > 0) {
96 // This space intentionally left blank
97 }
98 if (ret < 0) {
99 wlr_log(WLR_ERROR, "Failed to process bus: %s", strerror(-ret));
100 }
101}
102
103static int cmp_output(const void *item, const void *cmp_to) {
104 const struct swaybar_output *output = cmp_to;
105 if (output->identifier && strcmp(item, output->identifier) == 0) {
106 return 0;
107 }
108 return strcmp(item, output->name);
109}
110
111uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) {
112 struct swaybar_config *config = output->bar->config;
113 if (config->tray_outputs) {
114 if (list_seq_find(config->tray_outputs, cmp_output, output) == -1) {
115 return 0;
116 }
117 } // else display on all
118
119 if ((int) output->height*output->scale <= 2*config->tray_padding) {
120 return 2*config->tray_padding + 1;
121 }
122
123 uint32_t max_height = 0;
124 struct swaybar_tray *tray = output->bar->tray;
125 for (int i = 0; i < tray->items->length; ++i) {
126 uint32_t h = render_sni(cairo, output, x, tray->items->items[i]);
127 max_height = h > max_height ? h : max_height;
128 }
129
130 return max_height;
131}
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c
new file mode 100644
index 00000000..198c6c85
--- /dev/null
+++ b/swaybar/tray/watcher.c
@@ -0,0 +1,212 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h>
3#include <stddef.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include "list.h"
8#include "log.h"
9#include "swaybar/tray/watcher.h"
10
11static const char *obj_path = "/StatusNotifierWatcher";
12
13static bool using_standard_protocol(struct swaybar_watcher *watcher) {
14 return watcher->interface[strlen("org.")] == 'f'; // freedesktop
15}
16
17static int cmp_id(const void *item, const void *cmp_to) {
18 return strcmp(item, cmp_to);
19}
20
21static int cmp_service(const void *item, const void *cmp_to) {
22 return strncmp(item, cmp_to, strlen(cmp_to));
23}
24
25static int handle_lost_service(sd_bus_message *msg,
26 void *data, sd_bus_error *error) {
27 char *service, *old_owner, *new_owner;
28 int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner);
29 if (ret < 0) {
30 wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret));
31 return ret;
32 }
33
34 if (!*new_owner) {
35 struct swaybar_watcher *watcher = data;
36 int idx = list_seq_find(watcher->items,
37 using_standard_protocol(watcher) ? cmp_id : cmp_service, service);
38 if (idx != -1) {
39 char *id = watcher->items->items[idx];
40 wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id);
41 list_del(watcher->items, idx);
42 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
43 "StatusNotifierItemUnregistered", "s", id);
44 free(id);
45 }
46
47 idx = list_seq_find(watcher->hosts, cmp_id, service);
48 if (idx != -1) {
49 wlr_log(WLR_DEBUG, "Unregistering Status Notifier Host '%s'", service);
50 free(watcher->hosts->items[idx]);
51 list_del(watcher->hosts, idx);
52 }
53 }
54
55 return 0;
56}
57
58static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {
59 char *service_or_path, *id;
60 int ret = sd_bus_message_read(msg, "s", &service_or_path);
61 if (ret < 0) {
62 wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret));
63 return ret;
64 }
65
66 struct swaybar_watcher *watcher = data;
67 if (using_standard_protocol(watcher)) {
68 id = strdup(service_or_path);
69 } else {
70 const char *service, *path;
71 if (service_or_path[0] == '/') {
72 service = sd_bus_message_get_sender(msg);
73 path = service_or_path;
74 } else {
75 service = service_or_path;
76 path = "/StatusNotifierItem";
77 }
78 size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1;
79 id = malloc(id_len);
80 snprintf(id, id_len, "%s%s", service, path);
81 }
82
83 if (list_seq_find(watcher->items, cmp_id, id) == -1) {
84 wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id);
85 list_add(watcher->items, id);
86 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
87 "StatusNotifierItemRegistered", "s", id);
88 } else {
89 wlr_log(WLR_DEBUG, "Status Notifier Item '%s' already registered", id);
90 free(id);
91 }
92
93 return sd_bus_reply_method_return(msg, "");
94}
95
96static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {
97 char *service;
98 int ret = sd_bus_message_read(msg, "s", &service);
99 if (ret < 0) {
100 wlr_log(WLR_ERROR, "Failed to parse register host message: %s", strerror(-ret));
101 return ret;
102 }
103
104 struct swaybar_watcher *watcher = data;
105 if (list_seq_find(watcher->hosts, cmp_id, service) == -1) {
106 wlr_log(WLR_DEBUG, "Registering Status Notifier Host '%s'", service);
107 list_add(watcher->hosts, strdup(service));
108 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
109 "StatusNotifierHostRegistered", "s", service);
110 } else {
111 wlr_log(WLR_DEBUG, "Status Notifier Host '%s' already registered", service);
112 }
113
114 return sd_bus_reply_method_return(msg, "");
115}
116
117static int get_registered_snis(sd_bus *bus, const char *obj_path,
118 const char *interface, const char *property, sd_bus_message *reply,
119 void *data, sd_bus_error *error) {
120 struct swaybar_watcher *watcher = data;
121 list_add(watcher->items, NULL); // strv expects NULL-terminated string array
122 int ret = sd_bus_message_append_strv(reply, (char **)watcher->items->items);
123 list_del(watcher->items, watcher->items->length - 1);
124 return ret;
125}
126
127static int is_host_registered(sd_bus *bus, const char *obj_path,
128 const char *interface, const char *property, sd_bus_message *reply,
129 void *data, sd_bus_error *error) {
130 struct swaybar_watcher *watcher = data;
131 int val = watcher->hosts->length > 0; // dbus expects int rather than bool
132 return sd_bus_message_append_basic(reply, 'b', &val);
133}
134
135static const sd_bus_vtable watcher_vtable[] = {
136 SD_BUS_VTABLE_START(0),
137 SD_BUS_METHOD("RegisterStatusNotifierItem", "s", "", register_sni,
138 SD_BUS_VTABLE_UNPRIVILEGED),
139 SD_BUS_METHOD("RegisterStatusNotifierHost", "s", "", register_host,
140 SD_BUS_VTABLE_UNPRIVILEGED),
141 SD_BUS_PROPERTY("RegisteredStatusNotifierItems", "as", get_registered_snis,
142 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
143 SD_BUS_PROPERTY("IsStatusNotifierHostRegistered", "b", is_host_registered,
144 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
145 SD_BUS_PROPERTY("ProtocolVersion", "i", NULL,
146 offsetof(struct swaybar_watcher, version),
147 SD_BUS_VTABLE_PROPERTY_CONST),
148 SD_BUS_SIGNAL("StatusNotifierItemRegistered", "s", 0),
149 SD_BUS_SIGNAL("StatusNotifierItemUnregistered", "s", 0),
150 SD_BUS_SIGNAL("StatusNotifierHostRegistered", NULL, 0),
151 SD_BUS_VTABLE_END
152};
153
154struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) {
155 struct swaybar_watcher *watcher =
156 calloc(1, sizeof(struct swaybar_watcher));
157 if (!watcher) {
158 return NULL;
159 }
160
161 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1;
162 watcher->interface = malloc(len);
163 snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol);
164
165 sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL;
166 int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path,
167 watcher->interface, watcher_vtable, watcher);
168 if (ret < 0) {
169 wlr_log(WLR_ERROR, "Failed to add object vtable: %s", strerror(-ret));
170 goto error;
171 }
172
173 ret = sd_bus_match_signal(bus, &signal_slot, "org.freedesktop.DBus",
174 "/org/freedesktop/DBus", "org.freedesktop.DBus",
175 "NameOwnerChanged", handle_lost_service, watcher);
176 if (ret < 0) {
177 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
178 strerror(-ret));
179 goto error;
180 }
181
182 ret = sd_bus_request_name(bus, watcher->interface, 0);
183 if (ret < 0) {
184 wlr_log(WLR_ERROR, "Failed to acquire service name: %s", strerror(-ret));
185 goto error;
186 }
187
188 sd_bus_slot_set_floating(signal_slot, 0);
189 sd_bus_slot_set_floating(vtable_slot, 0);
190
191 watcher->bus = bus;
192 watcher->hosts = create_list();
193 watcher->items = create_list();
194 watcher->version = 0;
195 wlr_log(WLR_DEBUG, "Registered %s", watcher->interface);
196 return watcher;
197error:
198 sd_bus_slot_unref(signal_slot);
199 sd_bus_slot_unref(vtable_slot);
200 destroy_watcher(watcher);
201 return NULL;
202}
203
204void destroy_watcher(struct swaybar_watcher *watcher) {
205 if (!watcher) {
206 return;
207 }
208 list_free_items_and_destroy(watcher->hosts);
209 list_free_items_and_destroy(watcher->items);
210 free(watcher->interface);
211 free(watcher);
212}
diff --git a/swayidle/main.c b/swayidle/main.c
deleted file mode 100644
index 2b185949..00000000
--- a/swayidle/main.c
+++ /dev/null
@@ -1,442 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <errno.h>
4#include <getopt.h>
5#include <pthread.h>
6#include <signal.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/wait.h>
11#include <unistd.h>
12#include <wayland-client-protocol.h>
13#include <wayland-client.h>
14#include <wayland-server.h>
15#include <wayland-util.h>
16#include <wlr/config.h>
17#include <wlr/util/log.h>
18#include "config.h"
19#include "idle-client-protocol.h"
20#include "list.h"
21#if HAVE_SYSTEMD
22#include <systemd/sd-bus.h>
23#include <systemd/sd-login.h>
24#elif HAVE_ELOGIND
25#include <elogind/sd-bus.h>
26#include <elogind/sd-login.h>
27#endif
28
29static struct org_kde_kwin_idle *idle_manager = NULL;
30static struct wl_seat *seat = NULL;
31
32struct swayidle_state {
33 struct wl_display *display;
34 struct wl_event_loop *event_loop;
35 list_t *timeout_cmds; // struct swayidle_timeout_cmd *
36 char *lock_cmd;
37} state;
38
39struct swayidle_timeout_cmd {
40 int timeout, registered_timeout;
41 struct org_kde_kwin_idle_timeout *idle_timer;
42 char *idle_cmd;
43 char *resume_cmd;
44};
45
46static void cmd_exec(char *param) {
47 wlr_log(WLR_DEBUG, "Cmd exec %s", param);
48 pid_t pid = fork();
49 if (pid == 0) {
50 pid = fork();
51 if (pid == 0) {
52 char *const cmd[] = { "sh", "-c", param, NULL, };
53 execvp(cmd[0], cmd);
54 wlr_log_errno(WLR_ERROR, "execve failed!");
55 exit(1);
56 } else if (pid < 0) {
57 wlr_log_errno(WLR_ERROR, "fork failed");
58 exit(1);
59 }
60 exit(0);
61 } else if (pid < 0) {
62 wlr_log_errno(WLR_ERROR, "fork failed");
63 } else {
64 wlr_log(WLR_DEBUG, "Spawned process %s", param);
65 waitpid(pid, NULL, 0);
66 }
67}
68
69#if HAVE_SYSTEMD || HAVE_ELOGIND
70static int lock_fd = -1;
71static int ongoing_fd = -1;
72static struct sd_bus *bus = NULL;
73
74static int release_lock(void *data) {
75 wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);
76 if (ongoing_fd >= 0) {
77 close(ongoing_fd);
78 }
79 ongoing_fd = -1;
80 return 0;
81}
82
83static void acquire_sleep_lock(void) {
84 sd_bus_message *msg;
85 sd_bus_error error;
86 int ret = sd_bus_call_method(bus, "org.freedesktop.login1",
87 "/org/freedesktop/login1",
88 "org.freedesktop.login1.Manager", "Inhibit",
89 &error, &msg, "ssss", "sleep", "swayidle",
90 "Setup Up Lock Screen", "delay");
91 if (ret < 0) {
92 wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s",
93 strerror(-ret));
94 return;
95 }
96
97 ret = sd_bus_message_read(msg, "h", &lock_fd);
98 if (ret < 0) {
99 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
100 strerror(-ret));
101 return;
102 }
103
104 wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);
105}
106
107static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
108 sd_bus_error *ret_error) {
109 /* "b" apparently reads into an int, not a bool */
110 int going_down = 1;
111 int ret = sd_bus_message_read(msg, "b", &going_down);
112 if (ret < 0) {
113 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
114 strerror(-ret));
115 }
116 wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down);
117 if (!going_down) {
118 acquire_sleep_lock();
119 return 0;
120 }
121
122 ongoing_fd = lock_fd;
123
124 if (state.lock_cmd) {
125 cmd_exec(state.lock_cmd);
126 }
127
128 if (ongoing_fd >= 0) {
129 struct wl_event_source *source =
130 wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
131 wl_event_source_timer_update(source, 1000);
132 }
133
134 wlr_log(WLR_DEBUG, "Prepare for sleep done");
135 return 0;
136}
137
138static int dbus_event(int fd, uint32_t mask, void *data) {
139 sd_bus *bus = data;
140 while (sd_bus_process(bus, NULL) > 0) {
141 // Do nothing.
142 }
143 return 1;
144}
145
146static void setup_sleep_listener(void) {
147 int ret = sd_bus_default_system(&bus);
148 if (ret < 0) {
149 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
150 strerror(-ret));
151 return;
152 }
153
154 char str[256];
155 const char *fmt = "type='signal',"
156 "sender='org.freedesktop.login1',"
157 "interface='org.freedesktop.login1.%s',"
158 "member='%s'," "path='%s'";
159
160 snprintf(str, sizeof(str), fmt, "Manager", "PrepareForSleep",
161 "/org/freedesktop/login1");
162 ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);
163 if (ret < 0) {
164 wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
165 return;
166 }
167 acquire_sleep_lock();
168
169 wl_event_loop_add_fd(state.event_loop, sd_bus_get_fd(bus),
170 WL_EVENT_READABLE, dbus_event, bus);
171}
172#endif
173
174static void handle_global(void *data, struct wl_registry *registry,
175 uint32_t name, const char *interface, uint32_t version) {
176 if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0) {
177 idle_manager =
178 wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
179 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
180 seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
181 }
182}
183
184static void handle_global_remove(void *data, struct wl_registry *registry,
185 uint32_t name) {
186 // Who cares
187}
188
189static const struct wl_registry_listener registry_listener = {
190 .global = handle_global,
191 .global_remove = handle_global_remove,
192};
193
194static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener;
195
196static void register_timeout(struct swayidle_timeout_cmd *cmd,
197 int timeout) {
198 if (cmd->idle_timer != NULL) {
199 org_kde_kwin_idle_timeout_destroy(cmd->idle_timer);
200 cmd->idle_timer = NULL;
201 }
202 if (timeout < 0) {
203 wlr_log(WLR_DEBUG, "Not registering idle timeout");
204 return;
205 }
206 wlr_log(WLR_DEBUG, "Register with timeout: %d", timeout);
207 cmd->idle_timer =
208 org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, timeout);
209 org_kde_kwin_idle_timeout_add_listener(cmd->idle_timer,
210 &idle_timer_listener, cmd);
211 cmd->registered_timeout = timeout;
212}
213
214static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
215 struct swayidle_timeout_cmd *cmd = data;
216 wlr_log(WLR_DEBUG, "idle state");
217 if (cmd->idle_cmd) {
218 cmd_exec(cmd->idle_cmd);
219 }
220}
221
222static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
223 struct swayidle_timeout_cmd *cmd = data;
224 wlr_log(WLR_DEBUG, "active state");
225 if (cmd->registered_timeout != cmd->timeout) {
226 register_timeout(cmd, cmd->timeout);
227 }
228 if (cmd->resume_cmd) {
229 cmd_exec(cmd->resume_cmd);
230 }
231}
232
233static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
234 .idle = handle_idle,
235 .resumed = handle_resume,
236};
237
238static char *parse_command(int argc, char **argv) {
239 if (argc < 1) {
240 wlr_log(WLR_ERROR, "Missing command");
241 return NULL;
242 }
243
244 wlr_log(WLR_DEBUG, "Command: %s", argv[0]);
245 return strdup(argv[0]);
246}
247
248static int parse_timeout(int argc, char **argv) {
249 if (argc < 3) {
250 wlr_log(WLR_ERROR, "Too few parameters to timeout command. "
251 "Usage: timeout <seconds> <command>");
252 exit(-1);
253 }
254 errno = 0;
255 char *endptr;
256 int seconds = strtoul(argv[1], &endptr, 10);
257 if (errno != 0 || *endptr != '\0') {
258 wlr_log(WLR_ERROR, "Invalid timeout parameter '%s', it should be a "
259 "numeric value representing seconds", optarg);
260 exit(-1);
261 }
262
263 struct swayidle_timeout_cmd *cmd =
264 calloc(1, sizeof(struct swayidle_timeout_cmd));
265
266 if (seconds > 0) {
267 cmd->timeout = seconds * 1000;
268 } else {
269 cmd->timeout = -1;
270 }
271
272 wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
273 wlr_log(WLR_DEBUG, "Setup idle");
274 cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
275
276 int result = 3;
277 if (argc >= 5 && !strcmp("resume", argv[3])) {
278 wlr_log(WLR_DEBUG, "Setup resume");
279 cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
280 result = 5;
281 }
282 list_add(state.timeout_cmds, cmd);
283 return result;
284}
285
286static int parse_sleep(int argc, char **argv) {
287 if (argc < 2) {
288 wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "
289 "Usage: before-sleep <command>");
290 exit(-1);
291 }
292
293 state.lock_cmd = parse_command(argc - 1, &argv[1]);
294 if (state.lock_cmd) {
295 wlr_log(WLR_DEBUG, "Setup sleep lock: %s", state.lock_cmd);
296 }
297
298 return 2;
299}
300
301static int parse_args(int argc, char *argv[]) {
302 bool debug = false;
303
304 int c;
305 while ((c = getopt(argc, argv, "hd")) != -1) {
306 switch (c) {
307 case 'd':
308 debug = true;
309 break;
310 case 'h':
311 case '?':
312 printf("Usage: %s [OPTIONS]\n", argv[0]);
313 printf(" -d\tdebug\n");
314 printf(" -h\tthis help menu\n");
315 return 1;
316 default:
317 return 1;
318 }
319 }
320
321 wlr_log_init(debug ? WLR_DEBUG : WLR_INFO, NULL);
322
323 state.timeout_cmds = create_list();
324
325 int i = optind;
326 while (i < argc) {
327 if (!strcmp("timeout", argv[i])) {
328 wlr_log(WLR_DEBUG, "Got timeout");
329 i += parse_timeout(argc - i, &argv[i]);
330 } else if (!strcmp("before-sleep", argv[i])) {
331 wlr_log(WLR_DEBUG, "Got before-sleep");
332 i += parse_sleep(argc - i, &argv[i]);
333 } else {
334 wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);
335 return 1;
336 }
337 }
338
339 return 0;
340}
341
342void sway_terminate(int exit_code) {
343 wl_display_disconnect(state.display);
344 wl_event_loop_destroy(state.event_loop);
345 exit(exit_code);
346}
347
348static void register_zero_idle_timeout(void *item) {
349 struct swayidle_timeout_cmd *cmd = item;
350 register_timeout(cmd, 0);
351}
352
353static int handle_signal(int sig, void *data) {
354 switch (sig) {
355 case SIGINT:
356 case SIGTERM:
357 sway_terminate(0);
358 return 0;
359 case SIGUSR1:
360 wlr_log(WLR_DEBUG, "Got SIGUSR1");
361 list_foreach(state.timeout_cmds, register_zero_idle_timeout);
362 return 1;
363 }
364 assert(false); // not reached
365}
366
367static int display_event(int fd, uint32_t mask, void *data) {
368 if (mask & WL_EVENT_HANGUP) {
369 sway_terminate(0);
370 }
371 if (wl_display_dispatch(state.display) < 0) {
372 wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");
373 sway_terminate(0);
374 }
375 wl_display_flush(state.display);
376 return 0;
377}
378
379static void register_idle_timeout(void *item) {
380 struct swayidle_timeout_cmd *cmd = item;
381 register_timeout(cmd, cmd->timeout);
382}
383
384int main(int argc, char *argv[]) {
385 if (parse_args(argc, argv) != 0) {
386 return -1;
387 }
388
389 state.event_loop = wl_event_loop_create();
390
391 wl_event_loop_add_signal(state.event_loop, SIGINT, handle_signal, NULL);
392 wl_event_loop_add_signal(state.event_loop, SIGTERM, handle_signal, NULL);
393 wl_event_loop_add_signal(state.event_loop, SIGUSR1, handle_signal, NULL);
394
395 state.display = wl_display_connect(NULL);
396 if (state.display == NULL) {
397 wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
398 "If your compositor is running, check or set the "
399 "WAYLAND_DISPLAY environment variable.");
400 return -3;
401 }
402
403 struct wl_registry *registry = wl_display_get_registry(state.display);
404 wl_registry_add_listener(registry, &registry_listener, NULL);
405 wl_display_roundtrip(state.display);
406
407 if (idle_manager == NULL) {
408 wlr_log(WLR_ERROR, "Display doesn't support idle protocol");
409 return -4;
410 }
411 if (seat == NULL) {
412 wlr_log(WLR_ERROR, "Seat error");
413 return -5;
414 }
415
416 bool should_run = state.timeout_cmds->length > 0;
417#if HAVE_SYSTEMD || HAVE_ELOGIND
418 if (state.lock_cmd) {
419 should_run = true;
420 setup_sleep_listener();
421 }
422#endif
423 if (!should_run) {
424 wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");
425 sway_terminate(0);
426 }
427
428 list_foreach(state.timeout_cmds, register_idle_timeout);
429
430 wl_display_roundtrip(state.display);
431
432 struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
433 wl_display_get_fd(state.display), WL_EVENT_READABLE,
434 display_event, NULL);
435 wl_event_source_check(source);
436
437 while (wl_event_loop_dispatch(state.event_loop, -1) != 1) {
438 // This space intentionally left blank
439 }
440
441 sway_terminate(0);
442}
diff --git a/swayidle/meson.build b/swayidle/meson.build
deleted file mode 100644
index 79d2c5c4..00000000
--- a/swayidle/meson.build
+++ /dev/null
@@ -1,27 +0,0 @@
1threads = dependency('threads')
2
3swayidle_deps = [
4 client_protos,
5 pixman,
6 wayland_client,
7 wayland_server,
8 wlroots,
9]
10
11if systemd.found()
12 swayidle_deps += systemd
13endif
14if elogind.found()
15 swayidle_deps += elogind
16endif
17
18executable(
19 'swayidle', [
20 'main.c',
21 ],
22 include_directories: [sway_inc],
23 dependencies: swayidle_deps,
24 link_with: [lib_sway_common, lib_sway_client],
25 install_rpath : rpathdir,
26 install: true
27)
diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd
deleted file mode 100644
index 3083163f..00000000
--- a/swayidle/swayidle.1.scd
+++ /dev/null
@@ -1,63 +0,0 @@
1swayidle (1)
2
3# NAME
4
5swayidle - Idle manager for Wayland
6
7# SYNOPSIS
8
9*swayidle* [options] [events...]
10
11# OPTIONS
12
13*-h*
14 Show help message and quit.
15
16*-d*
17 Enable debug output.
18
19# DESCRIPTION
20
21swayidle listens for idle activity on your Wayland compositor and executes tasks
22on various idle-related events. You can specify any number of events at the
23command line.
24
25Sending SIGUSR1 to swayidle will immediately enter idle state.
26
27# EVENTS
28
29*timeout* <timeout> <timeout command> [resume <resume command>]
30 Execute _timeout command_ if there is no activity for <timeout> seconds.
31
32 If you specify "resume <resume command>", _resume command_ will be run when
33 there is activity again.
34
35*before-sleep* <command>
36 If built with systemd support, executes _command_ before systemd puts the
37 computer to sleep.
38
39All commands are executed in a shell.
40
41# EXAMPLE
42
43```
44swayidle \
45 timeout 300 'swaylock -c 000000' \
46 timeout 600 'swaymsg "output * dpms off"' \
47 resume 'swaymsg "output * dpms on"' \
48 before-sleep 'swaylock -c 000000'
49```
50
51This will lock your screen after 300 seconds of inactivity, then turn off your
52displays after another 600 seconds, and turn your screens back on when resumed.
53It will also lock your screen before your computer goes to sleep.
54
55# AUTHORS
56
57Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
58source contributors. For more information about sway development, see
59https://github.com/swaywm/sway.
60
61# SEE ALSO
62
63*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5)
diff --git a/swaylock/main.c b/swaylock/main.c
index 9b74b671..0b167da1 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -1,10 +1,10 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#define _POSIX_C_SOURCE 200112L
3#include <assert.h> 2#include <assert.h>
4#include <ctype.h> 3#include <ctype.h>
5#include <errno.h> 4#include <errno.h>
6#include <fcntl.h> 5#include <fcntl.h>
7#include <getopt.h> 6#include <getopt.h>
7#include <poll.h>
8#include <stdbool.h> 8#include <stdbool.h>
9#include <stdio.h> 9#include <stdio.h>
10#include <stdlib.h> 10#include <stdlib.h>
@@ -23,7 +23,6 @@
23#include "cairo.h" 23#include "cairo.h"
24#include "log.h" 24#include "log.h"
25#include "loop.h" 25#include "loop.h"
26#include "readline.h"
27#include "stringop.h" 26#include "stringop.h"
28#include "util.h" 27#include "util.h"
29#include "wlr-input-inhibitor-unstable-v1-client-protocol.h" 28#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
@@ -397,28 +396,34 @@ static void set_default_colors(struct swaylock_colors *colors) {
397 colors->background = 0xFFFFFFFF; 396 colors->background = 0xFFFFFFFF;
398 colors->bs_highlight = 0xDB3300FF; 397 colors->bs_highlight = 0xDB3300FF;
399 colors->key_highlight = 0x33DB00FF; 398 colors->key_highlight = 0x33DB00FF;
399 colors->caps_lock_bs_highlight = 0xDB3300FF;
400 colors->caps_lock_key_highlight = 0x33DB00FF;
400 colors->separator = 0x000000FF; 401 colors->separator = 0x000000FF;
401 colors->inside = (struct swaylock_colorset){ 402 colors->inside = (struct swaylock_colorset){
402 .input = 0x000000C0, 403 .input = 0x000000C0,
403 .cleared = 0xE5A445C0, 404 .cleared = 0xE5A445C0,
405 .caps_lock = 0x000000C0,
404 .verifying = 0x0072FFC0, 406 .verifying = 0x0072FFC0,
405 .wrong = 0xFA0000C0, 407 .wrong = 0xFA0000C0,
406 }; 408 };
407 colors->line = (struct swaylock_colorset){ 409 colors->line = (struct swaylock_colorset){
408 .input = 0x000000FF, 410 .input = 0x000000FF,
409 .cleared = 0x000000FF, 411 .cleared = 0x000000FF,
412 .caps_lock = 0x000000FF,
410 .verifying = 0x000000FF, 413 .verifying = 0x000000FF,
411 .wrong = 0x000000FF, 414 .wrong = 0x000000FF,
412 }; 415 };
413 colors->ring = (struct swaylock_colorset){ 416 colors->ring = (struct swaylock_colorset){
414 .input = 0x337D00FF, 417 .input = 0x337D00FF,
415 .cleared = 0xE5A445FF, 418 .cleared = 0xE5A445FF,
419 .caps_lock = 0xE5A445FF,
416 .verifying = 0x3300FFFF, 420 .verifying = 0x3300FFFF,
417 .wrong = 0x7D3300FF, 421 .wrong = 0x7D3300FF,
418 }; 422 };
419 colors->text = (struct swaylock_colorset){ 423 colors->text = (struct swaylock_colorset){
420 .input = 0xE5A445FF, 424 .input = 0xE5A445FF,
421 .cleared = 0x000000FF, 425 .cleared = 0x000000FF,
426 .caps_lock = 0xE5A445FF,
422 .verifying = 0x000000FF, 427 .verifying = 0x000000FF,
423 .wrong = 0x000000FF, 428 .wrong = 0x000000FF,
424 }; 429 };
@@ -434,25 +439,31 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
434 enum line_mode *line_mode, char **config_path) { 439 enum line_mode *line_mode, char **config_path) {
435 enum long_option_codes { 440 enum long_option_codes {
436 LO_BS_HL_COLOR = 256, 441 LO_BS_HL_COLOR = 256,
442 LO_CAPS_LOCK_BS_HL_COLOR,
443 LO_CAPS_LOCK_KEY_HL_COLOR,
437 LO_FONT, 444 LO_FONT,
438 LO_IND_RADIUS, 445 LO_IND_RADIUS,
439 LO_IND_THICKNESS, 446 LO_IND_THICKNESS,
440 LO_INSIDE_COLOR, 447 LO_INSIDE_COLOR,
441 LO_INSIDE_CLEAR_COLOR, 448 LO_INSIDE_CLEAR_COLOR,
449 LO_INSIDE_CAPS_LOCK_COLOR,
442 LO_INSIDE_VER_COLOR, 450 LO_INSIDE_VER_COLOR,
443 LO_INSIDE_WRONG_COLOR, 451 LO_INSIDE_WRONG_COLOR,
444 LO_KEY_HL_COLOR, 452 LO_KEY_HL_COLOR,
445 LO_LINE_COLOR, 453 LO_LINE_COLOR,
446 LO_LINE_CLEAR_COLOR, 454 LO_LINE_CLEAR_COLOR,
455 LO_LINE_CAPS_LOCK_COLOR,
447 LO_LINE_VER_COLOR, 456 LO_LINE_VER_COLOR,
448 LO_LINE_WRONG_COLOR, 457 LO_LINE_WRONG_COLOR,
449 LO_RING_COLOR, 458 LO_RING_COLOR,
450 LO_RING_CLEAR_COLOR, 459 LO_RING_CLEAR_COLOR,
460 LO_RING_CAPS_LOCK_COLOR,
451 LO_RING_VER_COLOR, 461 LO_RING_VER_COLOR,
452 LO_RING_WRONG_COLOR, 462 LO_RING_WRONG_COLOR,
453 LO_SEP_COLOR, 463 LO_SEP_COLOR,
454 LO_TEXT_COLOR, 464 LO_TEXT_COLOR,
455 LO_TEXT_CLEAR_COLOR, 465 LO_TEXT_CLEAR_COLOR,
466 LO_TEXT_CAPS_LOCK_COLOR,
456 LO_TEXT_VER_COLOR, 467 LO_TEXT_VER_COLOR,
457 LO_TEXT_WRONG_COLOR, 468 LO_TEXT_WRONG_COLOR,
458 }; 469 };
@@ -464,6 +475,8 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
464 {"daemonize", no_argument, NULL, 'f'}, 475 {"daemonize", no_argument, NULL, 'f'},
465 {"help", no_argument, NULL, 'h'}, 476 {"help", no_argument, NULL, 'h'},
466 {"image", required_argument, NULL, 'i'}, 477 {"image", required_argument, NULL, 'i'},
478 {"disable-caps-lock-text", no_argument, NULL, 'L'},
479 {"indicator-caps-lock", no_argument, NULL, 'l'},
467 {"line-uses-inside", no_argument, NULL, 'n'}, 480 {"line-uses-inside", no_argument, NULL, 'n'},
468 {"socket", required_argument, NULL, 'p'}, 481 {"socket", required_argument, NULL, 'p'},
469 {"line-uses-ring", no_argument, NULL, 'r'}, 482 {"line-uses-ring", no_argument, NULL, 'r'},
@@ -472,25 +485,31 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
472 {"no-unlock-indicator", no_argument, NULL, 'u'}, 485 {"no-unlock-indicator", no_argument, NULL, 'u'},
473 {"version", no_argument, NULL, 'v'}, 486 {"version", no_argument, NULL, 'v'},
474 {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR}, 487 {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR},
488 {"caps-lock-bs-hl-color", required_argument, NULL, LO_CAPS_LOCK_BS_HL_COLOR},
489 {"caps-lock-key-hl-color", required_argument, NULL, LO_CAPS_LOCK_KEY_HL_COLOR},
475 {"font", required_argument, NULL, LO_FONT}, 490 {"font", required_argument, NULL, LO_FONT},
476 {"indicator-radius", required_argument, NULL, LO_IND_RADIUS}, 491 {"indicator-radius", required_argument, NULL, LO_IND_RADIUS},
477 {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS}, 492 {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS},
478 {"inside-color", required_argument, NULL, LO_INSIDE_COLOR}, 493 {"inside-color", required_argument, NULL, LO_INSIDE_COLOR},
479 {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR}, 494 {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR},
495 {"inside-caps-lock-color", required_argument, NULL, LO_INSIDE_CAPS_LOCK_COLOR},
480 {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR}, 496 {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR},
481 {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR}, 497 {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR},
482 {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR}, 498 {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR},
483 {"line-color", required_argument, NULL, LO_LINE_COLOR}, 499 {"line-color", required_argument, NULL, LO_LINE_COLOR},
484 {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR}, 500 {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR},
501 {"line-caps-lock-color", required_argument, NULL, LO_LINE_CAPS_LOCK_COLOR},
485 {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR}, 502 {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR},
486 {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR}, 503 {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR},
487 {"ring-color", required_argument, NULL, LO_RING_COLOR}, 504 {"ring-color", required_argument, NULL, LO_RING_COLOR},
488 {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR}, 505 {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR},
506 {"ring-caps-lock-color", required_argument, NULL, LO_RING_CAPS_LOCK_COLOR},
489 {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR}, 507 {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR},
490 {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR}, 508 {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR},
491 {"separator-color", required_argument, NULL, LO_SEP_COLOR}, 509 {"separator-color", required_argument, NULL, LO_SEP_COLOR},
492 {"text-color", required_argument, NULL, LO_TEXT_COLOR}, 510 {"text-color", required_argument, NULL, LO_TEXT_COLOR},
493 {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR}, 511 {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR},
512 {"text-caps-lock-color", required_argument, NULL, LO_TEXT_CAPS_LOCK_COLOR},
494 {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR}, 513 {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR},
495 {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR}, 514 {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR},
496 {0, 0, 0, 0} 515 {0, 0, 0, 0}
@@ -499,76 +518,97 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
499 const char usage[] = 518 const char usage[] =
500 "Usage: swaylock [options...]\n" 519 "Usage: swaylock [options...]\n"
501 "\n" 520 "\n"
502 " -C, --config <config_file> " 521 " -C, --config <config_file> "
503 "Path to the config file.\n" 522 "Path to the config file.\n"
504 " -c, --color <color> " 523 " -c, --color <color> "
505 "Turn the screen into the given color instead of white.\n" 524 "Turn the screen into the given color instead of white.\n"
506 " -e, --ignore-empty-password " 525 " -e, --ignore-empty-password "
507 "When an empty password is provided, do not validate it.\n" 526 "When an empty password is provided, do not validate it.\n"
508 " -f, --daemonize " 527 " -f, --daemonize "
509 "Detach from the controlling terminal after locking.\n" 528 "Detach from the controlling terminal after locking.\n"
510 " -h, --help " 529 " -h, --help "
511 "Show help message and quit.\n" 530 "Show help message and quit.\n"
512 " -i, --image [<output>:]<path> " 531 " -i, --image [<output>:]<path> "
513 "Display the given image.\n" 532 "Display the given image.\n"
514 " -s, --scaling <mode> " 533 " -L, --disable-caps-lock-text "
534 "Disable the Caps Lock text.\n"
535 " -l, --indicator-caps-lock "
536 "Show the current Caps Lock state also on the indicator.\n"
537 " -s, --scaling <mode> "
515 "Scaling mode: stretch, fill, fit, center, tile.\n" 538 "Scaling mode: stretch, fill, fit, center, tile.\n"
516 " -t, --tiling " 539 " -t, --tiling "
517 "Same as --scaling=tile.\n" 540 "Same as --scaling=tile.\n"
518 " -u, --no-unlock-indicator " 541 " -u, --no-unlock-indicator "
519 "Disable the unlock indicator.\n" 542 "Disable the unlock indicator.\n"
520 " -v, --version " 543 " -v, --version "
521 "Show the version number and quit.\n" 544 "Show the version number and quit.\n"
522 " --bs-hl-color <color> " 545 " --bs-hl-color <color> "
523 "Sets the color of backspace highlight segments.\n" 546 "Sets the color of backspace highlight segments.\n"
524 " --font <font> " 547 " --caps-lock-bs-hl-color <color> "
548 "Sets the color of backspace highlight segments when Caps Lock "
549 "is active.\n"
550 " --caps-lock-key-hl-color <color> "
551 "Sets the color of the key press highlight segments when "
552 "Caps Lock is active.\n"
553 " --font <font> "
525 "Sets the font of the text.\n" 554 "Sets the font of the text.\n"
526 " --indicator-radius <radius> " 555 " --indicator-radius <radius> "
527 "Sets the indicator radius.\n" 556 "Sets the indicator radius.\n"
528 " --indicator-thickness <thick> " 557 " --indicator-thickness <thick> "
529 "Sets the indicator thickness.\n" 558 "Sets the indicator thickness.\n"
530 " --inside-color <color> " 559 " --inside-color <color> "
531 "Sets the color of the inside of the indicator.\n" 560 "Sets the color of the inside of the indicator.\n"
532 " --inside-clear-color <color> " 561 " --inside-clear-color <color> "
533 "Sets the color of the inside of the indicator when cleared.\n" 562 "Sets the color of the inside of the indicator when cleared.\n"
534 " --inside-ver-color <color> " 563 " --inside-caps-lock-color <color> "
564 "Sets the color of the inside of the indicator when Caps Lock "
565 "is active.\n"
566 " --inside-ver-color <color> "
535 "Sets the color of the inside of the indicator when verifying.\n" 567 "Sets the color of the inside of the indicator when verifying.\n"
536 " --inside-wrong-color <color> " 568 " --inside-wrong-color <color> "
537 "Sets the color of the inside of the indicator when invalid.\n" 569 "Sets the color of the inside of the indicator when invalid.\n"
538 " --key-hl-color <color> " 570 " --key-hl-color <color> "
539 "Sets the color of the key press highlight segments.\n" 571 "Sets the color of the key press highlight segments.\n"
540 " --line-color <color> " 572 " --line-color <color> "
541 "Sets the color of the line between the inside and ring.\n" 573 "Sets the color of the line between the inside and ring.\n"
542 " --line-clear-color <color> " 574 " --line-clear-color <color> "
543 "Sets the color of the line between the inside and ring when " 575 "Sets the color of the line between the inside and ring when "
544 "cleared.\n" 576 "cleared.\n"
545 " --line-ver-color <color> " 577 " --line-caps-lock-color <color> "
578 "Sets the color of the line between the inside and ring when "
579 "Caps Lock is active.\n"
580 " --line-ver-color <color> "
546 "Sets the color of the line between the inside and ring when " 581 "Sets the color of the line between the inside and ring when "
547 "verifying.\n" 582 "verifying.\n"
548 " --line-wrong-color <color> " 583 " --line-wrong-color <color> "
549 "Sets the color of the line between the inside and ring when " 584 "Sets the color of the line between the inside and ring when "
550 "invalid.\n" 585 "invalid.\n"
551 " -n, --line-uses-inside " 586 " -n, --line-uses-inside "
552 "Use the inside color for the line between the inside and ring.\n" 587 "Use the inside color for the line between the inside and ring.\n"
553 " -r, --line-uses-ring " 588 " -r, --line-uses-ring "
554 "Use the ring color for the line between the inside and ring.\n" 589 "Use the ring color for the line between the inside and ring.\n"
555 " --ring-color <color> " 590 " --ring-color <color> "
556 "Sets the color of the ring of the indicator.\n" 591 "Sets the color of the ring of the indicator.\n"
557 " --ring-clear-color <color> " 592 " --ring-clear-color <color> "
558 "Sets the color of the ring of the indicator when cleared.\n" 593 "Sets the color of the ring of the indicator when cleared.\n"
559 " --ring-ver-color <color> " 594 " --ring-caps-lock-color <color> "
595 "Sets the color of the ring of the indicator when Caps Lock "
596 "is active.\n"
597 " --ring-ver-color <color> "
560 "Sets the color of the ring of the indicator when verifying.\n" 598 "Sets the color of the ring of the indicator when verifying.\n"
561 " --ring-wrong-color <color> " 599 " --ring-wrong-color <color> "
562 "Sets the color of the ring of the indicator when invalid.\n" 600 "Sets the color of the ring of the indicator when invalid.\n"
563 " --separator-color <color> " 601 " --separator-color <color> "
564 "Sets the color of the lines that separate highlight segments.\n" 602 "Sets the color of the lines that separate highlight segments.\n"
565 " --text-color <color> " 603 " --text-color <color> "
566 "Sets the color of the text.\n" 604 "Sets the color of the text.\n"
567 " --text-clear-color <color> " 605 " --text-clear-color <color> "
568 "Sets the color of the text when cleared.\n" 606 "Sets the color of the text when cleared.\n"
569 " --text-ver-color <color> " 607 " --text-caps-lock-color <color> "
608 "Sets the color of the text when Caps Lock is active.\n"
609 " --text-ver-color <color> "
570 "Sets the color of the text when verifying.\n" 610 "Sets the color of the text when verifying.\n"
571 " --text-wrong-color <color> " 611 " --text-wrong-color <color> "
572 "Sets the color of the text when invalid.\n" 612 "Sets the color of the text when invalid.\n"
573 "\n" 613 "\n"
574 "All <color> options are of the form <rrggbb[aa]>.\n"; 614 "All <color> options are of the form <rrggbb[aa]>.\n";
@@ -577,7 +617,7 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
577 optind = 1; 617 optind = 1;
578 while (1) { 618 while (1) {
579 int opt_idx = 0; 619 int opt_idx = 0;
580 c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx); 620 c = getopt_long(argc, argv, "c:efhi:Llnrs:tuvC:", long_options, &opt_idx);
581 if (c == -1) { 621 if (c == -1) {
582 break; 622 break;
583 } 623 }
@@ -608,6 +648,16 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
608 load_image(optarg, state); 648 load_image(optarg, state);
609 } 649 }
610 break; 650 break;
651 case 'L':
652 if (state) {
653 state->args.show_caps_lock_text = false;
654 }
655 break;
656 case 'l':
657 if (state) {
658 state->args.show_caps_lock_indicator = true;
659 }
660 break;
611 case 'n': 661 case 'n':
612 if (line_mode) { 662 if (line_mode) {
613 *line_mode = LM_INSIDE; 663 *line_mode = LM_INSIDE;
@@ -645,6 +695,16 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
645 state->args.colors.bs_highlight = parse_color(optarg); 695 state->args.colors.bs_highlight = parse_color(optarg);
646 } 696 }
647 break; 697 break;
698 case LO_CAPS_LOCK_BS_HL_COLOR:
699 if (state) {
700 state->args.colors.caps_lock_bs_highlight = parse_color(optarg);
701 }
702 break;
703 case LO_CAPS_LOCK_KEY_HL_COLOR:
704 if (state) {
705 state->args.colors.caps_lock_key_highlight = parse_color(optarg);
706 }
707 break;
648 case LO_FONT: 708 case LO_FONT:
649 if (state) { 709 if (state) {
650 free(state->args.font); 710 free(state->args.font);
@@ -671,6 +731,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
671 state->args.colors.inside.cleared = parse_color(optarg); 731 state->args.colors.inside.cleared = parse_color(optarg);
672 } 732 }
673 break; 733 break;
734 case LO_INSIDE_CAPS_LOCK_COLOR:
735 if (state) {
736 state->args.colors.inside.caps_lock = parse_color(optarg);
737 }
738 break;
674 case LO_INSIDE_VER_COLOR: 739 case LO_INSIDE_VER_COLOR:
675 if (state) { 740 if (state) {
676 state->args.colors.inside.verifying = parse_color(optarg); 741 state->args.colors.inside.verifying = parse_color(optarg);
@@ -696,6 +761,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
696 state->args.colors.line.cleared = parse_color(optarg); 761 state->args.colors.line.cleared = parse_color(optarg);
697 } 762 }
698 break; 763 break;
764 case LO_LINE_CAPS_LOCK_COLOR:
765 if (state) {
766 state->args.colors.line.caps_lock = parse_color(optarg);
767 }
768 break;
699 case LO_LINE_VER_COLOR: 769 case LO_LINE_VER_COLOR:
700 if (state) { 770 if (state) {
701 state->args.colors.line.verifying = parse_color(optarg); 771 state->args.colors.line.verifying = parse_color(optarg);
@@ -716,6 +786,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
716 state->args.colors.ring.cleared = parse_color(optarg); 786 state->args.colors.ring.cleared = parse_color(optarg);
717 } 787 }
718 break; 788 break;
789 case LO_RING_CAPS_LOCK_COLOR:
790 if (state) {
791 state->args.colors.ring.caps_lock = parse_color(optarg);
792 }
793 break;
719 case LO_RING_VER_COLOR: 794 case LO_RING_VER_COLOR:
720 if (state) { 795 if (state) {
721 state->args.colors.ring.verifying = parse_color(optarg); 796 state->args.colors.ring.verifying = parse_color(optarg);
@@ -741,6 +816,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
741 state->args.colors.text.cleared = parse_color(optarg); 816 state->args.colors.text.cleared = parse_color(optarg);
742 } 817 }
743 break; 818 break;
819 case LO_TEXT_CAPS_LOCK_COLOR:
820 if (state) {
821 state->args.colors.text.caps_lock = parse_color(optarg);
822 }
823 break;
744 case LO_TEXT_VER_COLOR: 824 case LO_TEXT_VER_COLOR:
745 if (state) { 825 if (state) {
746 state->args.colors.text.verifying = parse_color(optarg); 826 state->args.colors.text.verifying = parse_color(optarg);
@@ -808,36 +888,32 @@ static int load_config(char *path, struct swaylock_state *state,
808 wlr_log(WLR_ERROR, "Failed to read config. Running without it."); 888 wlr_log(WLR_ERROR, "Failed to read config. Running without it.");
809 return 0; 889 return 0;
810 } 890 }
811 char *line; 891 char *line = NULL;
892 size_t line_size = 0;
893 ssize_t nread;
812 int line_number = 0; 894 int line_number = 0;
813 while (!feof(config)) { 895 int result = 0;
814 line = read_line(config); 896 while ((nread = getline(&line, &line_size, config)) != -1) {
815 if (!line) {
816 continue;
817 }
818
819 line_number++; 897 line_number++;
820 if (line[0] == '#') { 898
821 free(line); 899 if (line[nread - 1] == '\n') {
822 continue; 900 line[--nread] = '\0';
823 } 901 }
824 if (strlen(line) == 0) { 902
825 free(line); 903 if (!*line || line[0] == '#') {
826 continue; 904 continue;
827 } 905 }
828 906
829 wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); 907 wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line);
830 char flag[strlen(line) + 3]; 908 char flag[nread + 3];
831 sprintf(flag, "--%s", line); 909 sprintf(flag, "--%s", line);
832 char *argv[] = {"swaylock", flag}; 910 char *argv[] = {"swaylock", flag};
833 int result = parse_options(2, argv, state, line_mode, NULL); 911 result = parse_options(2, argv, state, line_mode, NULL);
834 if (result != 0) { 912 if (result != 0) {
835 free(line); 913 break;
836 fclose(config);
837 return result;
838 } 914 }
839 free(line);
840 } 915 }
916 free(line);
841 fclose(config); 917 fclose(config);
842 return 0; 918 return 0;
843} 919}
@@ -862,6 +938,8 @@ int main(int argc, char **argv) {
862 .thickness = 10, 938 .thickness = 10,
863 .ignore_empty = false, 939 .ignore_empty = false,
864 .show_indicator = true, 940 .show_indicator = true,
941 .show_caps_lock_indicator = false,
942 .show_caps_lock_text = true
865 }; 943 };
866 wl_list_init(&state.images); 944 wl_list_init(&state.images);
867 set_default_colors(&state.args.colors); 945 set_default_colors(&state.args.colors);
@@ -962,7 +1040,7 @@ int main(int argc, char **argv) {
962 } 1040 }
963 1041
964 state.eventloop = loop_create(); 1042 state.eventloop = loop_create();
965 loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLL_IN, 1043 loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLLIN,
966 display_in, NULL); 1044 display_in, NULL);
967 1045
968 state.run_display = true; 1046 state.run_display = true;
diff --git a/swaylock/pam.c b/swaylock/pam.c
index cac95a85..b90d9e87 100644
--- a/swaylock/pam.c
+++ b/swaylock/pam.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <pwd.h> 2#include <pwd.h>
3#include <security/pam_appl.h> 3#include <security/pam_appl.h>
4#include <stdbool.h> 4#include <stdbool.h>
diff --git a/swaylock/password.c b/swaylock/password.c
index 6138e1fe..3bd113ad 100644
--- a/swaylock/password.c
+++ b/swaylock/password.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <assert.h> 1#include <assert.h>
3#include <errno.h> 2#include <errno.h>
4#include <pwd.h> 3#include <pwd.h>
@@ -78,52 +77,56 @@ static void handle_preverify_timeout(void *data) {
78 state->verify_password_timer = NULL; 77 state->verify_password_timer = NULL;
79} 78}
80 79
81void swaylock_handle_key(struct swaylock_state *state, 80static void submit_password(struct swaylock_state *state) {
82 xkb_keysym_t keysym, uint32_t codepoint) { 81 if (state->args.ignore_empty && state->password.len == 0) {
83 switch (keysym) { 82 return;
84 case XKB_KEY_KP_Enter: /* fallthrough */ 83 }
85 case XKB_KEY_Return:
86 if (state->args.ignore_empty && state->password.len == 0) {
87 break;
88 }
89 84
90 state->auth_state = AUTH_STATE_VALIDATING; 85 state->auth_state = AUTH_STATE_VALIDATING;
91 damage_state(state); 86 damage_state(state);
92 87
93 // We generally want to wait until all surfaces are showing the 88 // We generally want to wait until all surfaces are showing the
94 // "verifying" state before we go and verify the password, because 89 // "verifying" state before we go and verify the password, because
95 // verifying it is a blocking operation. However, if the surface is on 90 // verifying it is a blocking operation. However, if the surface is on
96 // an output with DPMS off then it won't update, so we set a timer. 91 // an output with DPMS off then it won't update, so we set a timer.
97 state->verify_password_timer = loop_add_timer( 92 state->verify_password_timer = loop_add_timer(
98 state->eventloop, 50, handle_preverify_timeout, state); 93 state->eventloop, 50, handle_preverify_timeout, state);
99 94
100 while (state->run_display && state->verify_password_timer) { 95 while (state->run_display && state->verify_password_timer) {
101 errno = 0; 96 errno = 0;
102 if (wl_display_flush(state->display) == -1 && errno != EAGAIN) { 97 if (wl_display_flush(state->display) == -1 && errno != EAGAIN) {
103 break; 98 break;
104 } 99 }
105 loop_poll(state->eventloop); 100 loop_poll(state->eventloop);
106 101
107 bool ok = 1; 102 bool ok = 1;
108 struct swaylock_surface *surface; 103 struct swaylock_surface *surface;
109 wl_list_for_each(surface, &state->surfaces, link) { 104 wl_list_for_each(surface, &state->surfaces, link) {
110 if (surface->dirty) { 105 if (surface->dirty) {
111 ok = 0; 106 ok = 0;
112 }
113 }
114 if (ok) {
115 break;
116 } 107 }
117 } 108 }
118 wl_display_flush(state->display); 109 if (ok) {
119
120 if (attempt_password(&state->password)) {
121 state->run_display = false;
122 break; 110 break;
123 } 111 }
124 state->auth_state = AUTH_STATE_INVALID; 112 }
125 damage_state(state); 113 wl_display_flush(state->display);
126 schedule_indicator_clear(state); 114
115 if (attempt_password(&state->password)) {
116 state->run_display = false;
117 return;
118 }
119 state->auth_state = AUTH_STATE_INVALID;
120 damage_state(state);
121 schedule_indicator_clear(state);
122}
123
124void swaylock_handle_key(struct swaylock_state *state,
125 xkb_keysym_t keysym, uint32_t codepoint) {
126 switch (keysym) {
127 case XKB_KEY_KP_Enter: /* fallthrough */
128 case XKB_KEY_Return:
129 submit_password(state);
127 break; 130 break;
128 case XKB_KEY_Delete: 131 case XKB_KEY_Delete:
129 case XKB_KEY_BackSpace: 132 case XKB_KEY_BackSpace:
@@ -143,14 +146,6 @@ void swaylock_handle_key(struct swaylock_state *state,
143 schedule_indicator_clear(state); 146 schedule_indicator_clear(state);
144 break; 147 break;
145 case XKB_KEY_Caps_Lock: 148 case XKB_KEY_Caps_Lock:
146 /* The state is getting active after this
147 * so we need to manually toggle it */
148 state->xkb.caps_lock = !state->xkb.caps_lock;
149 state->auth_state = AUTH_STATE_INPUT_NOP;
150 damage_state(state);
151 schedule_indicator_clear(state);
152 schedule_password_clear(state);
153 break;
154 case XKB_KEY_Shift_L: 149 case XKB_KEY_Shift_L:
155 case XKB_KEY_Shift_R: 150 case XKB_KEY_Shift_R:
156 case XKB_KEY_Control_L: 151 case XKB_KEY_Control_L:
@@ -166,6 +161,13 @@ void swaylock_handle_key(struct swaylock_state *state,
166 schedule_indicator_clear(state); 161 schedule_indicator_clear(state);
167 schedule_password_clear(state); 162 schedule_password_clear(state);
168 break; 163 break;
164 case XKB_KEY_d:
165 if (state->xkb.control) {
166 submit_password(state);
167 break;
168 }
169 // fallthrough
170 case XKB_KEY_c: /* fallthrough */
169 case XKB_KEY_u: 171 case XKB_KEY_u:
170 if (state->xkb.control) { 172 if (state->xkb.control) {
171 clear_password_buffer(&state->password); 173 clear_password_buffer(&state->password);
diff --git a/swaylock/render.c b/swaylock/render.c
index fa8832bd..5aedaad5 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199506L
2#include <math.h> 1#include <math.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <wayland-client.h> 3#include <wayland-client.h>
@@ -19,7 +18,17 @@ static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state,
19 } else if (state->auth_state == AUTH_STATE_CLEAR) { 18 } else if (state->auth_state == AUTH_STATE_CLEAR) {
20 cairo_set_source_u32(cairo, colorset->cleared); 19 cairo_set_source_u32(cairo, colorset->cleared);
21 } else { 20 } else {
22 cairo_set_source_u32(cairo, colorset->input); 21 if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
22 cairo_set_source_u32(cairo, colorset->caps_lock);
23 } else if (state->xkb.caps_lock && !state->args.show_caps_lock_indicator &&
24 state->args.show_caps_lock_text) {
25 uint32_t inputtextcolor = state->args.colors.text.input;
26 state->args.colors.text.input = state->args.colors.text.caps_lock;
27 cairo_set_source_u32(cairo, colorset->input);
28 state->args.colors.text.input = inputtextcolor;
29 } else {
30 cairo_set_source_u32(cairo, colorset->input);
31 }
23 } 32 }
24} 33}
25 34
@@ -93,7 +102,8 @@ void render_frame(struct swaylock_surface *surface) {
93 break; 102 break;
94 case AUTH_STATE_INPUT: 103 case AUTH_STATE_INPUT:
95 case AUTH_STATE_INPUT_NOP: 104 case AUTH_STATE_INPUT_NOP:
96 if (state->xkb.caps_lock) { 105 case AUTH_STATE_BACKSPACE:
106 if (state->xkb.caps_lock && state->args.show_caps_lock_text) {
97 text = "Caps Lock"; 107 text = "Caps Lock";
98 } 108 }
99 break; 109 break;
@@ -126,9 +136,17 @@ void render_frame(struct swaylock_surface *surface) {
126 arc_radius, highlight_start, 136 arc_radius, highlight_start,
127 highlight_start + TYPE_INDICATOR_RANGE); 137 highlight_start + TYPE_INDICATOR_RANGE);
128 if (state->auth_state == AUTH_STATE_INPUT) { 138 if (state->auth_state == AUTH_STATE_INPUT) {
129 cairo_set_source_u32(cairo, state->args.colors.key_highlight); 139 if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
140 cairo_set_source_u32(cairo, state->args.colors.caps_lock_key_highlight);
141 } else {
142 cairo_set_source_u32(cairo, state->args.colors.key_highlight);
143 }
130 } else { 144 } else {
131 cairo_set_source_u32(cairo, state->args.colors.bs_highlight); 145 if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
146 cairo_set_source_u32(cairo, state->args.colors.caps_lock_bs_highlight);
147 } else {
148 cairo_set_source_u32(cairo, state->args.colors.bs_highlight);
149 }
132 } 150 }
133 cairo_stroke(cairo); 151 cairo_stroke(cairo);
134 152
diff --git a/swaylock/seat.c b/swaylock/seat.c
index 7b72114f..f0b1385e 100644
--- a/swaylock/seat.c
+++ b/swaylock/seat.c
@@ -63,8 +63,12 @@ static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
63 struct swaylock_state *state = data; 63 struct swaylock_state *state = data;
64 xkb_state_update_mask(state->xkb.state, 64 xkb_state_update_mask(state->xkb.state,
65 mods_depressed, mods_latched, mods_locked, 0, 0, group); 65 mods_depressed, mods_latched, mods_locked, 0, 0, group);
66 state->xkb.caps_lock = xkb_state_mod_name_is_active(state->xkb.state, 66 int caps_lock = xkb_state_mod_name_is_active(state->xkb.state,
67 XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED); 67 XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED);
68 if (caps_lock != state->xkb.caps_lock) {
69 state->xkb.caps_lock = caps_lock;
70 damage_state(state);
71 }
68 state->xkb.control = xkb_state_mod_name_is_active(state->xkb.state, 72 state->xkb.control = xkb_state_mod_name_is_active(state->xkb.state,
69 XKB_MOD_NAME_CTRL, 73 XKB_MOD_NAME_CTRL,
70 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); 74 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
diff --git a/swaylock/shadow.c b/swaylock/shadow.c
index f928eaa3..b7b10a67 100644
--- a/swaylock/shadow.c
+++ b/swaylock/shadow.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 1#define _XOPEN_SOURCE // for crypt
2#include <pwd.h> 2#include <pwd.h>
3#include <shadow.h> 3#include <shadow.h>
4#include <stdbool.h> 4#include <stdbool.h>
diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd
index 8ddc7d3a..2c7979be 100644
--- a/swaylock/swaylock.1.scd
+++ b/swaylock/swaylock.1.scd
@@ -27,7 +27,7 @@ Locks your Wayland session.
27*-f, --daemonize* 27*-f, --daemonize*
28 Detach from the controlling terminal after locking. 28 Detach from the controlling terminal after locking.
29 29
30 Note: this is the default bahavior of i3lock. 30 Note: this is the default behavior of i3lock.
31 31
32*-h, --help* 32*-h, --help*
33 Show help message and quit. 33 Show help message and quit.
@@ -44,6 +44,12 @@ Locks your Wayland session.
44 Display the given image, optionally only on the given output. Use -c to set 44 Display the given image, optionally only on the given output. Use -c to set
45 a background color. 45 a background color.
46 46
47*-L, --disable-caps-lock-text*
48 Disable the Caps Lock Text.
49
50*-l, --indicator-caps-lock*
51 Show the current Caps Lock state also on the indicator.
52
47*-s, --scaling* 53*-s, --scaling*
48 Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. 54 Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_.
49 55
@@ -58,6 +64,12 @@ Locks your Wayland session.
58*--bs-hl-color* <rrggbb[aa]> 64*--bs-hl-color* <rrggbb[aa]>
59 Sets the color of backspace highlight segments. 65 Sets the color of backspace highlight segments.
60 66
67*--caps-lock-bs-hl-color* <rrggbb[aa]>
68 Sets the color of backspace highlight segments when Caps Lock is active.
69
70*--caps-lock-bs-hl-color* <rrggbb[aa]>
71 Sets the color of the key press highlight segments when Caps Lock is active.
72
61*--font* <font> 73*--font* <font>
62 Sets the font of the text inside the indicator. 74 Sets the font of the text inside the indicator.
63 75
@@ -75,6 +87,9 @@ Locks your Wayland session.
75*--inside-clear-color* <rrggbb[aa]> 87*--inside-clear-color* <rrggbb[aa]>
76 Sets the color of the inside of the indicator when cleared. 88 Sets the color of the inside of the indicator when cleared.
77 89
90*--inside-caps-lock-color* <rrggbb[aa]>
91 Sets the color of the inside of the indicator when Caps Lock is active.
92
78*--inside-ver-color* <rrggbb[aa]> 93*--inside-ver-color* <rrggbb[aa]>
79 Sets the color of the inside of the indicator when verifying. 94 Sets the color of the inside of the indicator when verifying.
80 95
@@ -92,6 +107,10 @@ Locks your Wayland session.
92 Sets the color of the lines that separate the inside and outside of the 107 Sets the color of the lines that separate the inside and outside of the
93 indicator when cleared. 108 indicator when cleared.
94 109
110*--line-caps-lock-color* <rrggbb[aa]>
111 Sets the color of the line between the inside and ring when Caps Lock
112 is active.
113
95*--line-ver-color* <rrggbb[aa]> 114*--line-ver-color* <rrggbb[aa]>
96 Sets the color of the lines that separate the inside and outside of the 115 Sets the color of the lines that separate the inside and outside of the
97 indicator when verifying. 116 indicator when verifying.
@@ -114,6 +133,9 @@ Locks your Wayland session.
114*--ring-clear-color* <rrggbb[aa]> 133*--ring-clear-color* <rrggbb[aa]>
115 Sets the color of the outside of the indicator when cleared. 134 Sets the color of the outside of the indicator when cleared.
116 135
136*--ring-caps-lock-color* <rrggbb[aa]>
137 Sets the color of the ring of the indicator when Caps Lock is active.
138
117*--ring-ver-color* <rrggbb[aa]> 139*--ring-ver-color* <rrggbb[aa]>
118 Sets the color of the outside of the indicator when verifying. 140 Sets the color of the outside of the indicator when verifying.
119 141
@@ -129,6 +151,9 @@ Locks your Wayland session.
129*--text-clear-color* <rrggbb[aa]> 151*--text-clear-color* <rrggbb[aa]>
130 Sets the color of the text inside the indicator when cleared. 152 Sets the color of the text inside the indicator when cleared.
131 153
154*--text-caps-lock-color* <rrggbb[aa]>
155 Sets the color of the text when Caps Lock is active.
156
132*--text-ver-color* <rrggbb[aa]> 157*--text-ver-color* <rrggbb[aa]>
133 Sets the color of the text inside the indicator when verifying. 158 Sets the color of the text inside the indicator when verifying.
134 159
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 243b5fdc..e5eee631 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <stdlib.h> 3#include <stdlib.h>
4#include <string.h> 4#include <string.h>
@@ -12,7 +12,6 @@
12#include <json-c/json.h> 12#include <json-c/json.h>
13#include "stringop.h" 13#include "stringop.h"
14#include "ipc-client.h" 14#include "ipc-client.h"
15#include "readline.h"
16#include "log.h" 15#include "log.h"
17 16
18void sway_terminate(int exit_code) { 17void sway_terminate(int exit_code) {
@@ -32,6 +31,9 @@ static bool success_object(json_object *result) {
32// Iterate results array and return false if any of them failed 31// Iterate results array and return false if any of them failed
33static bool success(json_object *r, bool fallback) { 32static bool success(json_object *r, bool fallback) {
34 if (!json_object_is_type(r, json_type_array)) { 33 if (!json_object_is_type(r, json_type_array)) {
34 if (json_object_is_type(r, json_type_object)) {
35 return success_object(r);
36 }
35 return fallback; 37 return fallback;
36 } 38 }
37 39
@@ -111,7 +113,7 @@ static const char *pretty_type_name(const char *name) {
111} 113}
112 114
113static void pretty_print_input(json_object *i) { 115static void pretty_print_input(json_object *i) {
114 json_object *id, *name, *type, *product, *vendor, *kbdlayout; 116 json_object *id, *name, *type, *product, *vendor, *kbdlayout, *events;
115 json_object_object_get_ex(i, "identifier", &id); 117 json_object_object_get_ex(i, "identifier", &id);
116 json_object_object_get_ex(i, "name", &name); 118 json_object_object_get_ex(i, "name", &name);
117 json_object_object_get_ex(i, "type", &type); 119 json_object_object_get_ex(i, "type", &type);
@@ -137,6 +139,10 @@ static void pretty_print_input(json_object *i) {
137 json_object_get_string(kbdlayout)); 139 json_object_get_string(kbdlayout));
138 } 140 }
139 141
142 if (json_object_object_get_ex(i, "libinput_send_events", &events)) {
143 printf(" Libinput Send Events: %s\n", json_object_get_string(events));
144 }
145
140 printf("\n"); 146 printf("\n");
141} 147}
142 148
@@ -305,8 +311,9 @@ static void pretty_print(int type, json_object *resp) {
305} 311}
306 312
307int main(int argc, char **argv) { 313int main(int argc, char **argv) {
308 static int quiet = 0; 314 static bool quiet = false;
309 static int raw = 0; 315 static bool raw = false;
316 static bool monitor = false;
310 char *socket_path = NULL; 317 char *socket_path = NULL;
311 char *cmdtype = NULL; 318 char *cmdtype = NULL;
312 319
@@ -314,6 +321,7 @@ int main(int argc, char **argv) {
314 321
315 static struct option long_options[] = { 322 static struct option long_options[] = {
316 {"help", no_argument, NULL, 'h'}, 323 {"help", no_argument, NULL, 'h'},
324 {"monitor", no_argument, NULL, 'm'},
317 {"quiet", no_argument, NULL, 'q'}, 325 {"quiet", no_argument, NULL, 'q'},
318 {"raw", no_argument, NULL, 'r'}, 326 {"raw", no_argument, NULL, 'r'},
319 {"socket", required_argument, NULL, 's'}, 327 {"socket", required_argument, NULL, 's'},
@@ -326,6 +334,7 @@ int main(int argc, char **argv) {
326 "Usage: swaymsg [options] [message]\n" 334 "Usage: swaymsg [options] [message]\n"
327 "\n" 335 "\n"
328 " -h, --help Show help message and quit.\n" 336 " -h, --help Show help message and quit.\n"
337 " -m, --monitor Monitor until killed (-t SUBSCRIBE only)\n"
329 " -q, --quiet Be quiet.\n" 338 " -q, --quiet Be quiet.\n"
330 " -r, --raw Use raw output even if using a tty\n" 339 " -r, --raw Use raw output even if using a tty\n"
331 " -s, --socket <socket> Use the specified socket.\n" 340 " -s, --socket <socket> Use the specified socket.\n"
@@ -337,16 +346,19 @@ int main(int argc, char **argv) {
337 int c; 346 int c;
338 while (1) { 347 while (1) {
339 int option_index = 0; 348 int option_index = 0;
340 c = getopt_long(argc, argv, "hqrs:t:v", long_options, &option_index); 349 c = getopt_long(argc, argv, "hmqrs:t:v", long_options, &option_index);
341 if (c == -1) { 350 if (c == -1) {
342 break; 351 break;
343 } 352 }
344 switch (c) { 353 switch (c) {
354 case 'm': // Monitor
355 monitor = true;
356 break;
345 case 'q': // Quiet 357 case 'q': // Quiet
346 quiet = 1; 358 quiet = true;
347 break; 359 break;
348 case 'r': // Raw 360 case 'r': // Raw
349 raw = 1; 361 raw = true;
350 break; 362 break;
351 case 's': // Socket 363 case 's': // Socket
352 socket_path = strdup(optarg); 364 socket_path = strdup(optarg);
@@ -400,12 +412,20 @@ int main(int argc, char **argv) {
400 type = IPC_GET_CONFIG; 412 type = IPC_GET_CONFIG;
401 } else if (strcasecmp(cmdtype, "send_tick") == 0) { 413 } else if (strcasecmp(cmdtype, "send_tick") == 0) {
402 type = IPC_SEND_TICK; 414 type = IPC_SEND_TICK;
415 } else if (strcasecmp(cmdtype, "subscribe") == 0) {
416 type = IPC_SUBSCRIBE;
403 } else { 417 } else {
404 sway_abort("Unknown message type %s", cmdtype); 418 sway_abort("Unknown message type %s", cmdtype);
405 } 419 }
406 420
407 free(cmdtype); 421 free(cmdtype);
408 422
423 if (monitor && type != IPC_SUBSCRIBE) {
424 wlr_log(WLR_ERROR, "Monitor can only be used with -t SUBSCRIBE");
425 free(socket_path);
426 return 1;
427 }
428
409 char *command = NULL; 429 char *command = NULL;
410 if (optind < argc) { 430 if (optind < argc) {
411 command = join_args(argv + optind, argc - optind); 431 command = join_args(argv + optind, argc - optind);
@@ -422,26 +442,56 @@ int main(int argc, char **argv) {
422 json_object *obj = json_tokener_parse(resp); 442 json_object *obj = json_tokener_parse(resp);
423 443
424 if (obj == NULL) { 444 if (obj == NULL) {
425 fprintf(stderr, "ERROR: Could not parse json response from ipc. This is a bug in sway."); 445 fprintf(stderr, "ERROR: Could not parse json response from ipc. "
446 "This is a bug in sway.");
426 printf("%s\n", resp); 447 printf("%s\n", resp);
427 ret = 1; 448 ret = 1;
428 } else { 449 } else {
429 if (!success(obj, true)) { 450 if (!success(obj, true)) {
430 ret = 1; 451 ret = 1;
431 } 452 }
432 if (raw) { 453 if (type != IPC_SUBSCRIBE || ret != 0) {
433 printf("%s\n", json_object_to_json_string_ext(obj, 454 if (raw) {
434 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); 455 printf("%s\n", json_object_to_json_string_ext(obj,
435 } else { 456 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
436 pretty_print(type, obj); 457 } else {
458 pretty_print(type, obj);
459 }
437 } 460 }
438 json_object_put(obj); 461 json_object_put(obj);
439 } 462 }
440 } 463 }
441 close(socketfd);
442
443 free(command); 464 free(command);
444 free(resp); 465 free(resp);
466
467 if (type == IPC_SUBSCRIBE && ret == 0) {
468 do {
469 struct ipc_response *reply = ipc_recv_response(socketfd);
470 if (!reply) {
471 break;
472 }
473
474 json_object *obj = json_tokener_parse(reply->payload);
475 if (obj == NULL) {
476 fprintf(stderr, "ERROR: Could not parse json response from ipc"
477 ". This is a bug in sway.");
478 ret = 1;
479 break;
480 } else {
481 if (raw) {
482 printf("%s\n", json_object_to_json_string(obj));
483 } else {
484 printf("%s\n", json_object_to_json_string_ext(obj,
485 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
486 }
487 json_object_put(obj);
488 }
489
490 free_ipc_response(reply);
491 } while (monitor);
492 }
493
494 close(socketfd);
445 free(socket_path); 495 free(socket_path);
446 return ret; 496 return ret;
447} 497}
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index eaac8105..f55f86a9 100644
--- a/swaymsg/swaymsg.1.scd
+++ b/swaymsg/swaymsg.1.scd
@@ -13,6 +13,12 @@ _swaymsg_ [options...] [message]
13*-h, --help* 13*-h, --help*
14 Show help message and quit. 14 Show help message and quit.
15 15
16*-m, --monitor*
17 Monitor for responses until killed instead of exiting after the first
18 response. This can only be used with the IPC message type _subscribe_. If
19 there is a malformed response or an invalid event type was requested,
20 swaymsg will stop monitoring and exit.
21
16*-q, --quiet* 22*-q, --quiet*
17 Sends the IPC message but does not print the response from sway. 23 Sends the IPC message but does not print the response from sway.
18 24
@@ -71,3 +77,8 @@ _swaymsg_ [options...] [message]
71 77
72*send\_tick* 78*send\_tick*
73 Sends a tick event to all subscribed clients. 79 Sends a tick event to all subscribed clients.
80
81*subscribe*
82 Subscribe to a list of event types. The argument for this type should be
83 provided in the form of a valid JSON array. If any of the types are invalid
84 or if an valid JSON array is not provided, this will result in an failure.
diff --git a/swaynag/config.c b/swaynag/config.c
index cd34dcc2..85aa380a 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -1,11 +1,10 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#define _POSIX_C_SOURCE 200112L
3#include <getopt.h> 2#include <getopt.h>
3#include <stdio.h>
4#include <stdlib.h> 4#include <stdlib.h>
5#include <wordexp.h> 5#include <wordexp.h>
6#include "log.h" 6#include "log.h"
7#include "list.h" 7#include "list.h"
8#include "readline.h"
9#include "swaynag/swaynag.h" 8#include "swaynag/swaynag.h"
10#include "swaynag/types.h" 9#include "swaynag/types.h"
11#include "util.h" 10#include "util.h"
@@ -13,21 +12,19 @@
13 12
14static char *read_from_stdin(void) { 13static char *read_from_stdin(void) {
15 char *buffer = NULL; 14 char *buffer = NULL;
16 while (!feof(stdin)) { 15 size_t buffer_len = 0;
17 char *line = read_line(stdin); 16 char *line = NULL;
18 if (!line) { 17 size_t line_size = 0;
19 continue; 18 ssize_t nread;
20 } 19 while ((nread = getline(&line, &line_size, stdin)) != -1) {
21 20 buffer = realloc(buffer, buffer_len + nread);
22 size_t curlen = buffer ? strlen(buffer) : 0; 21 snprintf(&buffer[buffer_len], nread + 1, "%s", line);
23 buffer = realloc(buffer, curlen + strlen(line) + 2); 22 buffer_len += nread;
24 snprintf(buffer + curlen, strlen(line) + 2, "%s\n", line);
25
26 free(line);
27 } 23 }
24 free(line);
28 25
29 while (buffer && buffer[strlen(buffer) - 1] == '\n') { 26 while (buffer && buffer[buffer_len - 1] == '\n') {
30 buffer[strlen(buffer) - 1] = '\0'; 27 buffer[--buffer_len] = '\0';
31 } 28 }
32 29
33 return buffer; 30 return buffer;
@@ -53,6 +50,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
53 50
54 static struct option opts[] = { 51 static struct option opts[] = {
55 {"button", required_argument, NULL, 'b'}, 52 {"button", required_argument, NULL, 'b'},
53 {"button-no-terminal", required_argument, NULL, 'B'},
56 {"config", required_argument, NULL, 'c'}, 54 {"config", required_argument, NULL, 'c'},
57 {"debug", no_argument, NULL, 'd'}, 55 {"debug", no_argument, NULL, 'd'},
58 {"edge", required_argument, NULL, 'e'}, 56 {"edge", required_argument, NULL, 'e'},
@@ -87,7 +85,10 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
87 "Usage: swaynag [options...]\n" 85 "Usage: swaynag [options...]\n"
88 "\n" 86 "\n"
89 " -b, --button <text> <action> Create a button with text that " 87 " -b, --button <text> <action> Create a button with text that "
90 "executes action when pressed. Multiple buttons can be defined.\n" 88 "executes action in a terminal when pressed. Multiple buttons can "
89 "be defined.\n"
90 " -B, --button-no-terminal <text> <action> Like --button, but does"
91 "not run the action in a terminal.\n"
91 " -c, --config <path> Path to config file.\n" 92 " -c, --config <path> Path to config file.\n"
92 " -d, --debug Enable debugging.\n" 93 " -d, --debug Enable debugging.\n"
93 " -e, --edge top|bottom Set the edge to use.\n" 94 " -e, --edge top|bottom Set the edge to use.\n"
@@ -118,12 +119,13 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
118 119
119 optind = 1; 120 optind = 1;
120 while (1) { 121 while (1) {
121 int c = getopt_long(argc, argv, "b:c:de:f:hlL:m:o:s:t:v", opts, NULL); 122 int c = getopt_long(argc, argv, "b:B:c:de:f:hlL:m:o:s:t:v", opts, NULL);
122 if (c == -1) { 123 if (c == -1) {
123 break; 124 break;
124 } 125 }
125 switch (c) { 126 switch (c) {
126 case 'b': // Button 127 case 'b': // Button
128 case 'B': // Button (No Terminal)
127 if (swaynag) { 129 if (swaynag) {
128 if (optind >= argc) { 130 if (optind >= argc) {
129 fprintf(stderr, "Missing action for button %s\n", optarg); 131 fprintf(stderr, "Missing action for button %s\n", optarg);
@@ -134,6 +136,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
134 button->text = strdup(optarg); 136 button->text = strdup(optarg);
135 button->type = SWAYNAG_ACTION_COMMAND; 137 button->type = SWAYNAG_ACTION_COMMAND;
136 button->action = strdup(argv[optind]); 138 button->action = strdup(argv[optind]);
139 button->terminal = c == 'b';
137 list_add(swaynag->buttons, button); 140 list_add(swaynag->buttons, button);
138 } 141 }
139 optind++; 142 optind++;
@@ -343,32 +346,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {
343 type->name = strdup("<config>"); 346 type->name = strdup("<config>");
344 list_add(types, type); 347 list_add(types, type);
345 348
346 char *line; 349 char *line = NULL;
350 size_t line_size = 0;
351 ssize_t nread;
347 int line_number = 0; 352 int line_number = 0;
348 while (!feof(config)) { 353 int result = 0;
349 line = read_line(config); 354 while ((nread = getline(&line, &line_size, config)) != -1) {
350 if (!line) {
351 continue;
352 }
353
354 line_number++; 355 line_number++;
355 if (line[0] == '#') { 356 if (!*line || line[0] == '\n' || line[0] == '#') {
356 free(line);
357 continue;
358 }
359 if (strlen(line) == 0) {
360 free(line);
361 continue; 357 continue;
362 } 358 }
363 359
364 if (line[0] == '[') { 360 if (line[0] == '[') {
365 char *close = strchr(line, ']'); 361 char *close = strchr(line, ']');
366 if (!close) { 362 if (!close) {
367 free(line);
368 fclose(config);
369 fprintf(stderr, "Closing bracket not found on line %d\n", 363 fprintf(stderr, "Closing bracket not found on line %d\n",
370 line_number); 364 line_number);
371 return 1; 365 result = 1;
366 break;
372 } 367 }
373 char *name = calloc(1, close - line); 368 char *name = calloc(1, close - line);
374 strncat(name, line + 1, close - line - 1); 369 strncat(name, line + 1, close - line - 1);
@@ -380,22 +375,17 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {
380 } 375 }
381 free(name); 376 free(name);
382 } else { 377 } else {
383 char flag[strlen(line) + 3]; 378 char flag[nread + 3];
384 sprintf(flag, "--%s", line); 379 sprintf(flag, "--%s", line);
385 char *argv[] = {"swaynag", flag}; 380 char *argv[] = {"swaynag", flag};
386 int result;
387 result = swaynag_parse_options(2, argv, swaynag, types, type, 381 result = swaynag_parse_options(2, argv, swaynag, types, type,
388 NULL, NULL); 382 NULL, NULL);
389 if (result != 0) { 383 if (result != 0) {
390 free(line); 384 break;
391 fclose(config);
392 return result;
393 } 385 }
394 } 386 }
395
396 free(line);
397 } 387 }
388 free(line);
398 fclose(config); 389 fclose(config);
399 return 0; 390 return result;
400} 391}
401
diff --git a/swaynag/main.c b/swaynag/main.c
index bae3c0e2..9f00ac7e 100644
--- a/swaynag/main.c
+++ b/swaynag/main.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <signal.h> 3#include <signal.h>
4#include "log.h" 4#include "log.h"
@@ -130,4 +130,3 @@ cleanup:
130 swaynag_destroy(&swaynag); 130 swaynag_destroy(&swaynag);
131 return exit_code; 131 return exit_code;
132} 132}
133
diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd
index bb69e47d..b25568a0 100644
--- a/swaynag/swaynag.1.scd
+++ b/swaynag/swaynag.1.scd
@@ -12,7 +12,14 @@ _swaynag_ [options...]
12 12
13*-b, --button* <text> <action> 13*-b, --button* <text> <action>
14 Create a button with the text _text_ that executes _action_ when pressed. 14 Create a button with the text _text_ that executes _action_ when pressed.
15 Multiple buttons can be defined by providing the flag multiple times. 15 If the environment variable `TERMINAL` is set, _action_ will be run inside
16 the terminal. Otherwise, it will fallback to running directly. Multiple
17 buttons can be defined by providing the flag multiple times.
18
19*-B, --button-no-terminal* <text> <action>
20 Create a button with the text _text_ that executes _action_ when pressed.
21 _action_ will be run directly instead of in a terminal. Multiple buttons
22 can be defined by providing the flag multiple times.
16 23
17*-c, --config* <path> 24*-c, --config* <path>
18 The config file to use. By default, the following paths are checked: 25 The config file to use. By default, the following paths are checked:
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index 06185f20..674c24b5 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <assert.h> 3#include <assert.h>
4#include <sys/stat.h> 4#include <sys/stat.h>
@@ -49,14 +49,17 @@ static void swaynag_button_execute(struct swaynag *swaynag,
49 if (fork() == 0) { 49 if (fork() == 0) {
50 // Child of the child. Will be reparented to the init process 50 // Child of the child. Will be reparented to the init process
51 char *terminal = getenv("TERMINAL"); 51 char *terminal = getenv("TERMINAL");
52 if (terminal && strlen(terminal)) { 52 if (button->terminal && terminal && strlen(terminal)) {
53 wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal); 53 wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal);
54 if (!terminal_execute(terminal, button->action)) { 54 if (!terminal_execute(terminal, button->action)) {
55 swaynag_destroy(swaynag); 55 swaynag_destroy(swaynag);
56 exit(EXIT_FAILURE); 56 exit(EXIT_FAILURE);
57 } 57 }
58 } else { 58 } else {
59 wlr_log(WLR_DEBUG, "$TERMINAL not found. Running directly"); 59 if (button->terminal) {
60 wlr_log(WLR_DEBUG,
61 "$TERMINAL not found. Running directly");
62 }
60 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); 63 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL);
61 } 64 }
62 } 65 }
@@ -126,6 +129,8 @@ static void update_cursor(struct swaynag *swaynag) {
126 pointer->cursor_surface, 129 pointer->cursor_surface,
127 pointer->cursor_image->hotspot_x / swaynag->scale, 130 pointer->cursor_image->hotspot_x / swaynag->scale,
128 pointer->cursor_image->hotspot_y / swaynag->scale); 131 pointer->cursor_image->hotspot_y / swaynag->scale);
132 wl_surface_damage_buffer(pointer->cursor_surface, 0, 0,
133 INT32_MAX, INT32_MAX);
129 wl_surface_commit(pointer->cursor_surface); 134 wl_surface_commit(pointer->cursor_surface);
130} 135}
131 136
@@ -290,7 +295,7 @@ static void handle_global(void *data, struct wl_registry *registry,
290 struct swaynag *swaynag = data; 295 struct swaynag *swaynag = data;
291 if (strcmp(interface, wl_compositor_interface.name) == 0) { 296 if (strcmp(interface, wl_compositor_interface.name) == 0) {
292 swaynag->compositor = wl_registry_bind(registry, name, 297 swaynag->compositor = wl_registry_bind(registry, name,
293 &wl_compositor_interface, 3); 298 &wl_compositor_interface, 4);
294 } else if (strcmp(interface, wl_seat_interface.name) == 0) { 299 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
295 swaynag->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); 300 swaynag->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
296 wl_seat_add_listener(swaynag->seat, &seat_listener, swaynag); 301 wl_seat_add_listener(swaynag->seat, &seat_listener, swaynag);
@@ -404,15 +409,13 @@ void swaynag_destroy(struct swaynag *swaynag) {
404 swaynag->run_display = false; 409 swaynag->run_display = false;
405 410
406 free(swaynag->message); 411 free(swaynag->message);
407 while (swaynag->buttons->length) { 412 for (int i = 0; i < swaynag->buttons->length; ++i) {
408 struct swaynag_button *button = swaynag->buttons->items[0]; 413 struct swaynag_button *button = swaynag->buttons->items[i];
409 list_del(swaynag->buttons, 0);
410 free(button->text); 414 free(button->text);
411 free(button->action); 415 free(button->action);
412 free(button); 416 free(button);
413 } 417 }
414 list_free(swaynag->buttons); 418 list_free(swaynag->buttons);
415 free(swaynag->details.button_details);
416 free(swaynag->details.message); 419 free(swaynag->details.message);
417 free(swaynag->details.button_up.text); 420 free(swaynag->details.button_up.text);
418 free(swaynag->details.button_down.text); 421 free(swaynag->details.button_down.text);
diff --git a/swaynag/types.c b/swaynag/types.c
index 1e0a138b..bc17bd33 100644
--- a/swaynag/types.c
+++ b/swaynag/types.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 2#include <getopt.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <stdlib.h> 4#include <stdlib.h>
@@ -147,10 +147,8 @@ void swaynag_type_free(struct swaynag_type *type) {
147} 147}
148 148
149void swaynag_types_free(list_t *types) { 149void swaynag_types_free(list_t *types) {
150 while (types->length) { 150 for (int i = 0; i < types->length; ++i) {
151 struct swaynag_type *type = types->items[0]; 151 swaynag_type_free(types->items[i]);
152 swaynag_type_free(type);
153 list_del(types, 0);
154 } 152 }
155 list_free(types); 153 list_free(types);
156} 154}