summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.bg.md10
-rw-r--r--README.de.md8
-rw-r--r--README.el.md8
-rw-r--r--README.fr.md8
-rw-r--r--README.it.md8
-rw-r--r--README.ja.md11
-rw-r--r--README.md13
-rw-r--r--README.pt.md8
-rw-r--r--README.ru.md10
-rw-r--r--README.uk.md8
-rw-r--r--common/loop.c180
-rw-r--r--common/meson.build1
-rw-r--r--common/pango.c15
-rw-r--r--include/ipc.h3
-rw-r--r--include/loop.h54
-rw-r--r--include/sway/config.h4
-rw-r--r--include/sway/input/seat.h11
-rw-r--r--include/sway/ipc-server.h1
-rw-r--r--include/swaybar/bar.h40
-rw-r--r--include/swaybar/config.h5
-rw-r--r--include/swaybar/event_loop.h26
-rw-r--r--include/swaybar/i3bar.h2
-rw-r--r--include/swaybar/ipc.h4
-rw-r--r--include/swaybar/status_line.h5
-rw-r--r--include/swaylock/swaylock.h4
-rw-r--r--meson.build5
-rw-r--r--sway/commands/bar.c115
-rw-r--r--sway/commands/bar/hidden_state.c34
-rw-r--r--sway/commands/bar/id.c2
-rw-r--r--sway/commands/bar/mode.c34
-rw-r--r--sway/commands/bar/status_command.c2
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/gaps.c50
-rw-r--r--sway/commands/move.c8
-rw-r--r--sway/commands/sticky.c5
-rw-r--r--sway/commands/swap.c19
-rw-r--r--sway/commands/workspace.c22
-rw-r--r--sway/config.c1
-rw-r--r--sway/config/bar.c16
-rw-r--r--sway/desktop/output.c12
-rw-r--r--sway/desktop/render.c165
-rw-r--r--sway/input/cursor.c6
-rw-r--r--sway/input/keyboard.c28
-rw-r--r--sway/input/seat.c51
-rw-r--r--sway/ipc-server.c18
-rw-r--r--sway/main.c1
-rw-r--r--sway/sway-bar.5.scd16
-rw-r--r--sway/sway-input.5.scd2
-rw-r--r--sway/sway-output.5.scd66
-rw-r--r--sway/sway.1.scd2
-rw-r--r--sway/sway.5.scd86
-rw-r--r--sway/tree/arrange.c4
-rw-r--r--sway/tree/container.c4
-rw-r--r--sway/tree/view.c11
-rw-r--r--sway/tree/workspace.c7
-rw-r--r--swaybar/bar.c125
-rw-r--r--swaybar/config.c4
-rw-r--r--swaybar/event_loop.c156
-rw-r--r--swaybar/ipc.c128
-rw-r--r--swaybar/main.c8
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/render.c32
-rw-r--r--swaybar/status_line.c18
-rw-r--r--swaybg/main.c7
-rw-r--r--swayidle/main.c4
-rw-r--r--swaylock/main.c30
-rw-r--r--swaylock/password.c64
-rw-r--r--swaynag/swaynag.c6
68 files changed, 1086 insertions, 738 deletions
diff --git a/README.bg.md b/README.bg.md
index e2753656..9d91dfa4 100644
--- a/README.bg.md
+++ b/README.bg.md
@@ -5,8 +5,6 @@
5[IRC канала](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 5[IRC канала](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на
6irc.freenode.net). 6irc.freenode.net).
7 7
8**Внимание**: На този етап в Sway няма да бъдат добавяни нови функции, докато не приключим с интеграцията на Sway и wlroots. Все още се оправят бъгове.
9
10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 8[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
11 9
12Ако желаете, може да дарите на [Patreon страницата на автора](https://patreon.com/sircmpwn), което ще помогне за цялостното здраве и развитие на проекта. 10Ако желаете, може да дарите на [Patreon страницата на автора](https://patreon.com/sircmpwn), което ще помогне за цялостното здраве и развитие на проекта.
@@ -21,14 +19,6 @@ irc.freenode.net).
21Версии подписани с ключ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 19Версии подписани с ключ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
22и публикувани в [GitHub](https://github.com/swaywm/sway/releases). 20и публикувани в [GitHub](https://github.com/swaywm/sway/releases).
23 21
24## Статус
25
26- [i3 поддръжка](https://github.com/swaywm/sway/issues/2)
27- [i3-bar поддръжка](https://github.com/swaywm/sway/issues/343)
28- [i3-gaps поддръжка](https://github.com/swaywm/sway/issues/307)
29- [IPC поддръжка](https://github.com/swaywm/sway/issues/98)
30- [Сигурност](https://github.com/swaywm/sway/issues/984)
31
32## Инсталация 22## Инсталация
33 23
34### От пакети 24### От пакети
diff --git a/README.de.md b/README.de.md
index 9d8a41ce..9e591022 100644
--- a/README.de.md
+++ b/README.de.md
@@ -28,14 +28,6 @@ Neue Versionen werden mit
28[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 28[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
29signiert und [auf Github](https://github.com/swaywm/sway/releases) veröffentlicht. 29signiert und [auf Github](https://github.com/swaywm/sway/releases) veröffentlicht.
30 30
31## Status
32
33- [i3-Features](https://github.com/swaywm/sway/issues/2)
34- [IPC-Features](https://github.com/swaywm/sway/issues/98)
35- [i3bar-Features](https://github.com/swaywm/sway/issues/343)
36- [i3-gaps-Features](https://github.com/swaywm/sway/issues/307)
37- [Sicherheitsfeatures](https://github.com/swaywm/sway/issues/984)
38
39## Installation 31## Installation
40 32
41### Als Paket 33### Als Paket
diff --git a/README.el.md b/README.el.md
index 53fdd2d9..d56728a0 100644
--- a/README.el.md
+++ b/README.el.md
@@ -23,14 +23,6 @@ To username μου στο Freenode είναι kon14 και θα με βρείτ
23 23
24Οι εκδόσεις υπογράφονται ως [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) και δημοσιεύονται στο [GitHub](https://github.com/swaywm/sway/releases). 24Οι εκδόσεις υπογράφονται ως [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) και δημοσιεύονται στο [GitHub](https://github.com/swaywm/sway/releases).
25 25
26## Κατάσταση
27
28- [Υποστήριξη δυνατοτήτων του i3](https://github.com/swaywm/sway/issues/2)
29- [Υποστήριξη δυνατοτήτων IPC](https://github.com/swaywm/sway/issues/98)
30- [Υποστήριξη δυνατοτήτων i3bar](https://github.com/swaywm/sway/issues/343)
31- [Υποστήριξη δυνατοτήτων i3-gaps](https://github.com/swaywm/sway/issues/307)
32- [Δυνατότητες Ασφαλείας](https://github.com/swaywm/sway/issues/984)
33
34## Εγκατάσταση 26## Εγκατάσταση
35 27
36### Από Πακέτα 28### Από Πακέτα
diff --git a/README.fr.md b/README.fr.md
index 0a9697b8..629aef95 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -24,14 +24,6 @@ maintenance de Sway.
24Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 24Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
25et publiées [sur GitHub](https://github.com/swaywm/sway/releases). 25et publiées [sur GitHub](https://github.com/swaywm/sway/releases).
26 26
27## Statut
28
29- [support des fonctionnalités d'i3](https://github.com/swaywm/sway/issues/2)
30- [support des fonctionnalités d'IPC](https://github.com/swaywm/sway/issues/98)
31- [support des fonctionnalités d'i3bar](https://github.com/swaywm/sway/issues/343)
32- [support des fonctionnalités d'i3-gaps](https://github.com/swaywm/sway/issues/307)
33- [fonctionnalités de sécurité](https://github.com/swaywm/sway/issues/984)
34
35## Installation 27## Installation
36 28
37### À partir de paquets 29### À partir de paquets
diff --git a/README.it.md b/README.it.md
index 653e6aea..a7e175c1 100644
--- a/README.it.md
+++ b/README.it.md
@@ -25,14 +25,6 @@ Questa traduzione non è ancora completa. [Clicca qui per maggiori informazioni]
25Le release sono firmate con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 25Le release sono firmate con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
26e pubblicate [su GitHub](https://github.com/swaywm/sway/releases). 26e pubblicate [su GitHub](https://github.com/swaywm/sway/releases).
27 27
28## Status
29
30- [supporto funzionalità i3](https://github.com/swaywm/sway/issues/2)
31- [supporto funzionalità IPC](https://github.com/swaywm/sway/issues/98)
32- [supporto funzionalità i3bar](https://github.com/swaywm/sway/issues/343)
33- [supporto funzionalità i3-gaps](https://github.com/swaywm/sway/issues/307)
34- [sicurezza](https://github.com/swaywm/sway/issues/984)
35
36## Installazione 28## Installazione
37 29
38### Dai pacchetti 30### Dai pacchetti
diff --git a/README.ja.md b/README.ja.md
index b0488c53..396e0a72 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -5,27 +5,18 @@ i3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。
5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。
6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。
7 7
8**注意**: Swayは現在*凍結中*であり、wlcからwlrootsへの移植が完了するまで新たな機能は追加されません。2018年9月以降に発見されるバグは0.15では対応されません。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。
9
10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 8[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
11 9
12Swayの開発を支援したい場合は、[SirCmpwnのPatreon](https://patreon.com/sircmpwn)や、特定の機能に対する[報奨金のページ](https://github.com/swaywm/sway/issues/986)から寄付ができます。誰でも報奨金を請求できますし、自分の欲しい機能に報奨金を懸ける事も出来ます。またSwayのメンテナンスを支援するには、Patreonがより有用です。 10Swayの開発を支援したい場合は、[SirCmpwnのPatreon](https://patreon.com/sircmpwn)や、特定の機能に対する[報奨金のページ](https://github.com/swaywm/sway/issues/986)から寄付ができます。誰でも報奨金を請求できますし、自分の欲しい機能に報奨金を懸ける事も出来ます。またSwayのメンテナンスを支援するには、Patreonがより有用です。
13 11
14## 日本語サポート 12## 日本語サポート
13
15SirCmpwnは、日本語でのサポートをIRCとGitHubで行います。タイムゾーンはUTC-4です。 14SirCmpwnは、日本語でのサポートをIRCとGitHubで行います。タイムゾーンはUTC-4です。
16 15
17## リリースの署名 16## リリースの署名
18 17
19Swayのリリースは[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)で署名され、[GitHub](https://github.com/swaywm/sway/releases)で公開されています。 18Swayのリリースは[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)で署名され、[GitHub](https://github.com/swaywm/sway/releases)で公開されています。
20 19
21## 開発状況
22
23- [i3の機能のサポート](https://github.com/swaywm/sway/issues/2)
24- [IPCの機能のサポート](https://github.com/swaywm/sway/issues/98)
25- [i3barの機能のサポート](https://github.com/swaywm/sway/issues/343)
26- [i3-gapsの機能のサポート](https://github.com/swaywm/sway/issues/307)
27- [セキュリティ機能](https://github.com/swaywm/sway/issues/984)
28
29## インストール 20## インストール
30 21
31### パッケージから 22### パッケージから
diff --git a/README.md b/README.md
index 57bc4b79..7809a440 100644
--- a/README.md
+++ b/README.md
@@ -9,11 +9,6 @@ Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the
9[IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 9[IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
10irc.freenode.net). 10irc.freenode.net).
11 11
12**Notice**: work is well underway to port sway to
13[wlroots](https://github.com/swaywm/wlroots). This is **unstable** and
14**unsupported** - we accept patches, but are not fond of bug reports. We are no
15longer accepting bugs for 0.15.
16
17If 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
18Patreon page](https://patreon.com/sircmpwn). 13Patreon page](https://patreon.com/sircmpwn).
19 14
@@ -22,14 +17,6 @@ Patreon page](https://patreon.com/sircmpwn).
22Releases are signed with [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 17Releases are signed with [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
23and published [on GitHub](https://github.com/swaywm/sway/releases). 18and published [on GitHub](https://github.com/swaywm/sway/releases).
24 19
25## Status
26
27- [i3 feature support](https://github.com/swaywm/sway/issues/2)
28- [IPC feature support](https://github.com/swaywm/sway/issues/98)
29- [i3bar feature support](https://github.com/swaywm/sway/issues/343)
30- [i3-gaps feature support](https://github.com/swaywm/sway/issues/307)
31- [security features](https://github.com/swaywm/sway/issues/984)
32
33## Installation 20## Installation
34 21
35### From Packages 22### From Packages
diff --git a/README.pt.md b/README.pt.md
index 9089c8c6..57220fae 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -30,14 +30,6 @@ exite em enviar quaisquer correções necessárias.
30[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 30[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
31e publicadas [no GitHub](https://github.com/swaywm/sway/releases). 31e publicadas [no GitHub](https://github.com/swaywm/sway/releases).
32 32
33## Status
34
35- [Suporte aos recursos do i3](https://github.com/swaywm/sway/issues/2)
36- [Suporte aos recursos IPC](https://github.com/swaywm/sway/issues/98)
37- [Suporte aos recursos do i3bar](https://github.com/swaywm/sway/issues/343)
38- [Suporte aos recursos do i3-gaps](https://github.com/swaywm/sway/issues/307)
39- [Recursos de segurança](https://github.com/swaywm/sway/issues/984)
40
41## Instalação 33## Instalação
42 34
43### A partir de pacotes 35### A partir de pacotes
diff --git a/README.ru.md b/README.ru.md
index 68675db3..25b80a23 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -6,8 +6,6 @@ i3-совместимый [Wayland](http://wayland.freedesktop.org/) компо
6[IRC каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на 6[IRC каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на
7irc.freenode.net). 7irc.freenode.net).
8 8
9**Внимание**: на данный момент ведется активная интеграция wlroots, в связи с чем разработка sway приостановлена, однако патчи продолжают приниматься.
10
11[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 9[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
12 10
13При желании поддержать разработку Sway вы можете пожертвовать [автору 11При желании поддержать разработку Sway вы можете пожертвовать [автору
@@ -26,14 +24,6 @@ DarkReef оказывает поддержку на русском языке в
26Версии подписаны ключом [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 24Версии подписаны ключом [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
27и опубликованы [на GitHub'е](https://github.com/swaywm/sway/releases). 25и опубликованы [на GitHub'е](https://github.com/swaywm/sway/releases).
28 26
29## Статус
30
31- [Поддержка i3](https://github.com/swaywm/sway/issues/2)
32- [Поддержка i3-bar](https://github.com/swaywm/sway/issues/343)
33- [Поддержка i3-gaps](https://github.com/swaywm/sway/issues/307)
34- [Поддержка IPC](https://github.com/swaywm/sway/issues/98)
35- [Безопасность](https://github.com/swaywm/sway/issues/984)
36
37## Установка 27## Установка
38 28
39### Из пакета 29### Из пакета
diff --git a/README.uk.md b/README.uk.md
index c31a3ea9..8c8b2eed 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -29,14 +29,6 @@ Hummer12007 у IRC-спільноті. Будьте терплячі, вам о
29Випуски підписані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 29Випуски підписані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)
30та публікуються на сторінці [GitHub](https://github.com/swaywm/sway/releases). 30та публікуються на сторінці [GitHub](https://github.com/swaywm/sway/releases).
31 31
32## Стан розробки
33
34- [Підтримка функцій i3](https://github.com/swaywm/sway/issues/2)
35- [Реалізація IPC-протоколу i3](https://github.com/swaywm/sway/issues/98)
36- [Підтримка функцій i3bar](https://github.com/swaywm/sway/issues/343)
37- [Підтримка функцій i3-gaps](https://github.com/swaywm/sway/issues/307)
38- [Функції безпеки](https://github.com/swaywm/sway/issues/984)
39
40## Встановлення 32## Встановлення
41 33
42### З пакунків 34### З пакунків
diff --git a/common/loop.c b/common/loop.c
new file mode 100644
index 00000000..750bee75
--- /dev/null
+++ b/common/loop.c
@@ -0,0 +1,180 @@
1#define _POSIX_C_SOURCE 199309L
2#include <limits.h>
3#include <string.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <poll.h>
8#include <time.h>
9#include <unistd.h>
10#include "list.h"
11#include "log.h"
12#include "loop.h"
13
14struct loop_fd_event {
15 void (*callback)(int fd, short mask, void *data);
16 void *data;
17};
18
19struct loop_timer {
20 void (*callback)(void *data);
21 void *data;
22 struct timespec expiry;
23};
24
25struct loop {
26 struct pollfd *fds;
27 int fd_length;
28 int fd_capacity;
29
30 list_t *fd_events; // struct loop_fd_event
31 list_t *timers; // struct loop_timer
32};
33
34struct loop *loop_create(void) {
35 struct loop *loop = calloc(1, sizeof(struct loop));
36 if (!loop) {
37 wlr_log(WLR_ERROR, "Unable to allocate memory for loop");
38 return NULL;
39 }
40 loop->fd_capacity = 10;
41 loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity);
42 loop->fd_events = create_list();
43 loop->timers = create_list();
44 return loop;
45}
46
47void loop_destroy(struct loop *loop) {
48 list_foreach(loop->fd_events, free);
49 list_foreach(loop->timers, free);
50 list_free(loop->fd_events);
51 list_free(loop->timers);
52 free(loop->fds);
53 free(loop);
54}
55
56void loop_poll(struct loop *loop) {
57 // Calculate next timer in ms
58 int ms = INT_MAX;
59 if (loop->timers->length) {
60 struct timespec now;
61 clock_gettime(CLOCK_MONOTONIC, &now);
62 for (int i = 0; i < loop->timers->length; ++i) {
63 struct loop_timer *timer = loop->timers->items[i];
64 int timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000;
65 timer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000;
66 if (timer_ms < ms) {
67 ms = timer_ms;
68 }
69 }
70 }
71 if (ms < 0) {
72 ms = 0;
73 }
74
75 poll(loop->fds, loop->fd_length, ms);
76
77 // Dispatch fds
78 for (int i = 0; i < loop->fd_length; ++i) {
79 struct pollfd pfd = loop->fds[i];
80 struct loop_fd_event *event = loop->fd_events->items[i];
81
82 // Always send these events
83 unsigned events = pfd.events | POLLHUP | POLLERR;
84
85 if (pfd.revents & events) {
86 event->callback(pfd.fd, pfd.revents, event->data);
87 }
88 }
89
90 // Dispatch timers
91 if (loop->timers->length) {
92 struct timespec now;
93 clock_gettime(CLOCK_MONOTONIC, &now);
94 for (int i = 0; i < loop->timers->length; ++i) {
95 struct loop_timer *timer = loop->timers->items[i];
96 bool expired = timer->expiry.tv_sec < now.tv_sec ||
97 (timer->expiry.tv_sec == now.tv_sec &&
98 timer->expiry.tv_nsec < now.tv_nsec);
99 if (expired) {
100 timer->callback(timer->data);
101 loop_remove_timer(loop, timer);
102 --i;
103 }
104 }
105 }
106}
107
108void loop_add_fd(struct loop *loop, int fd, short mask,
109 void (*callback)(int fd, short mask, void *data), void *data) {
110 struct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event));
111 if (!event) {
112 wlr_log(WLR_ERROR, "Unable to allocate memory for event");
113 return;
114 }
115 event->callback = callback;
116 event->data = data;
117 list_add(loop->fd_events, event);
118
119 struct pollfd pfd = {fd, mask, 0};
120
121 if (loop->fd_length == loop->fd_capacity) {
122 loop->fd_capacity += 10;
123 loop->fds = realloc(loop->fds,
124 sizeof(struct pollfd) * loop->fd_capacity);
125 }
126
127 loop->fds[loop->fd_length++] = pfd;
128}
129
130struct loop_timer *loop_add_timer(struct loop *loop, int ms,
131 void (*callback)(void *data), void *data) {
132 struct loop_timer *timer = calloc(1, sizeof(struct loop_timer));
133 if (!timer) {
134 wlr_log(WLR_ERROR, "Unable to allocate memory for timer");
135 return NULL;
136 }
137 timer->callback = callback;
138 timer->data = data;
139
140 clock_gettime(CLOCK_MONOTONIC, &timer->expiry);
141 timer->expiry.tv_sec += ms / 1000;
142
143 long int nsec = (ms % 1000) * 1000000;
144 if (timer->expiry.tv_nsec + nsec >= 1000000000) {
145 timer->expiry.tv_sec++;
146 nsec -= 1000000000;
147 }
148 timer->expiry.tv_nsec += nsec;
149
150 list_add(loop->timers, timer);
151
152 return timer;
153}
154
155bool loop_remove_fd(struct loop *loop, int fd) {
156 for (int i = 0; i < loop->fd_length; ++i) {
157 if (loop->fds[i].fd == fd) {
158 free(loop->fd_events->items[i]);
159 list_del(loop->fd_events, i);
160
161 loop->fd_length--;
162 memmove(&loop->fds[i], &loop->fds[i + 1],
163 sizeof(struct pollfd) * (loop->fd_length - i));
164
165 return true;
166 }
167 }
168 return false;
169}
170
171bool loop_remove_timer(struct loop *loop, struct loop_timer *timer) {
172 for (int i = 0; i < loop->timers->length; ++i) {
173 if (loop->timers->items[i] == timer) {
174 list_del(loop->timers, i);
175 free(timer);
176 return true;
177 }
178 }
179 return false;
180}
diff --git a/common/meson.build b/common/meson.build
index 44a29508..224a9c3f 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -5,6 +5,7 @@ lib_sway_common = static_library(
5 'cairo.c', 5 'cairo.c',
6 'ipc-client.c', 6 'ipc-client.c',
7 'log.c', 7 'log.c',
8 'loop.c',
8 'list.c', 9 'list.c',
9 'pango.c', 10 'pango.c',
10 'readline.c', 11 'readline.c',
diff --git a/common/pango.c b/common/pango.c
index ba74692e..3bc97808 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -10,6 +10,9 @@
10#include "log.h" 10#include "log.h"
11#include "stringop.h" 11#include "stringop.h"
12 12
13static const char overflow[] = "[buffer overflow]";
14static const int max_chars = 16384;
15
13size_t escape_markup_text(const char *src, char *dest) { 16size_t escape_markup_text(const char *src, char *dest) {
14 size_t length = 0; 17 size_t length = 0;
15 if (dest) { 18 if (dest) {
@@ -84,12 +87,12 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
84 87
85void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 88void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
86 int *baseline, double scale, bool markup, const char *fmt, ...) { 89 int *baseline, double scale, bool markup, const char *fmt, ...) {
87 static char buf[2048]; 90 char buf[max_chars];
88 91
89 va_list args; 92 va_list args;
90 va_start(args, fmt); 93 va_start(args, fmt);
91 if (vsnprintf(buf, 2048, fmt, args) >= 2048) { 94 if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) {
92 strcpy(buf, "[buffer overflow]"); 95 strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow);
93 } 96 }
94 va_end(args); 97 va_end(args);
95 98
@@ -104,12 +107,12 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
104 107
105void pango_printf(cairo_t *cairo, const char *font, 108void pango_printf(cairo_t *cairo, const char *font,
106 double scale, bool markup, const char *fmt, ...) { 109 double scale, bool markup, const char *fmt, ...) {
107 static char buf[2048]; 110 char buf[max_chars];
108 111
109 va_list args; 112 va_list args;
110 va_start(args, fmt); 113 va_start(args, fmt);
111 if (vsnprintf(buf, 2048, fmt, args) >= 2048) { 114 if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) {
112 strcpy(buf, "[buffer overflow]"); 115 strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow);
113 } 116 }
114 va_end(args); 117 va_end(args);
115 118
diff --git a/include/ipc.h b/include/ipc.h
index a3f60e19..9063b933 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -30,6 +30,9 @@ enum ipc_command_type {
30 IPC_EVENT_BINDING = ((1<<31) | 5), 30 IPC_EVENT_BINDING = ((1<<31) | 5),
31 IPC_EVENT_SHUTDOWN = ((1<<31) | 6), 31 IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
32 IPC_EVENT_TICK = ((1<<31) | 7), 32 IPC_EVENT_TICK = ((1<<31) | 7),
33
34 // sway-specific event types
35 IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20),
33}; 36};
34 37
35#endif 38#endif
diff --git a/include/loop.h b/include/loop.h
new file mode 100644
index 00000000..2f608eda
--- /dev/null
+++ b/include/loop.h
@@ -0,0 +1,54 @@
1#ifndef _SWAY_LOOP_H
2#define _SWAY_LOOP_H
3#include <stdbool.h>
4
5/**
6 * This is an event loop system designed for sway clients, not sway itself.
7 *
8 * The loop consists of file descriptors and timers. Typically the Wayland
9 * display's file descriptor will be one of the fds in the loop.
10 */
11
12struct loop;
13struct loop_timer;
14
15/**
16 * Create an event loop.
17 */
18struct loop *loop_create(void);
19
20/**
21 * Destroy the event loop (eg. on program termination).
22 */
23void loop_destroy(struct loop *loop);
24
25/**
26 * Poll the event loop. This will block until one of the fds has data.
27 */
28void loop_poll(struct loop *loop);
29
30/**
31 * Add a file descriptor to the loop.
32 */
33void loop_add_fd(struct loop *loop, int fd, short mask,
34 void (*func)(int fd, short mask, void *data), void *data);
35
36/**
37 * Add a timer to the loop.
38 *
39 * When the timer expires, the timer will be removed from the loop and freed.
40 */
41struct loop_timer *loop_add_timer(struct loop *loop, int ms,
42 void (*callback)(void *data), void *data);
43
44/**
45 * Remove a file descriptor from the loop.
46 */
47bool loop_remove_fd(struct loop *loop, int fd);
48
49/**
50 * Remove a timer from the loop.
51 */
52bool loop_remove_timer(struct loop *loop, struct loop_timer *timer);
53
54#endif
diff --git a/include/sway/config.h b/include/sway/config.h
index bc02c0fd..be5a00b5 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -191,6 +191,7 @@ struct bar_config {
191 * In "show" mode, it will always be shown on top of the active workspace. 191 * In "show" mode, it will always be shown on top of the active workspace.
192 */ 192 */
193 char *hidden_state; 193 char *hidden_state;
194 bool visible_by_modifier; // only relevant in "hide" mode
194 /** 195 /**
195 * Id name used to identify the bar through IPC. 196 * Id name used to identify the bar through IPC.
196 * 197 *
@@ -389,7 +390,6 @@ struct sway_config {
389 bool show_marks; 390 bool show_marks;
390 bool tiling_drag; 391 bool tiling_drag;
391 392
392 bool edge_gaps;
393 bool smart_gaps; 393 bool smart_gaps;
394 int gaps_inner; 394 int gaps_inner;
395 int gaps_outer; 395 int gaps_outer;
@@ -531,6 +531,8 @@ void free_sway_binding(struct sway_binding *sb);
531 531
532void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); 532void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
533 533
534void load_swaybar(struct bar_config *bar);
535
534void load_swaybars(void); 536void load_swaybars(void);
535 537
536void terminate_swaybg(pid_t pid); 538void terminate_swaybg(pid_t pid);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index ebb0cd43..be95567e 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -51,6 +51,7 @@ struct sway_seat {
51 51
52 bool has_focus; 52 bool has_focus;
53 struct wl_list focus_stack; // list of containers in focus order 53 struct wl_list focus_stack; // list of containers in focus order
54 struct sway_workspace *workspace;
54 55
55 // If the focused layer is set, views cannot receive keyboard focus 56 // If the focused layer is set, views cannot receive keyboard focus
56 struct wlr_layer_surface_v1 *focused_layer; 57 struct wlr_layer_surface_v1 *focused_layer;
@@ -112,8 +113,16 @@ void seat_set_focus_container(struct sway_seat *seat,
112void seat_set_focus_workspace(struct sway_seat *seat, 113void seat_set_focus_workspace(struct sway_seat *seat,
113 struct sway_workspace *ws); 114 struct sway_workspace *ws);
114 115
116/**
117 * Manipulate the focus stack without triggering any other behaviour.
118 *
119 * This can be used to set focus_inactive by calling the function a second time
120 * with the real focus.
121 */
122void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node);
123
115void seat_set_focus_warp(struct sway_seat *seat, 124void seat_set_focus_warp(struct sway_seat *seat,
116 struct sway_node *node, bool warp, bool notify); 125 struct sway_node *node, bool warp);
117 126
118void seat_set_focus_surface(struct sway_seat *seat, 127void seat_set_focus_surface(struct sway_seat *seat,
119 struct wlr_surface *surface, bool unfocus); 128 struct wlr_surface *surface, bool unfocus);
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index 80180ec4..3c43f74d 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -15,6 +15,7 @@ void ipc_event_workspace(struct sway_workspace *old,
15 struct sway_workspace *new, const char *change); 15 struct sway_workspace *new, const char *change);
16void ipc_event_window(struct sway_container *window, const char *change); 16void ipc_event_window(struct sway_container *window, const char *change);
17void ipc_event_barconfig_update(struct bar_config *bar); 17void ipc_event_barconfig_update(struct bar_config *bar);
18void ipc_event_bar_state_update(struct bar_config *bar);
18void ipc_event_mode(const char *mode, bool pango); 19void ipc_event_mode(const char *mode, bool pango);
19void ipc_event_shutdown(const char *reason); 20void ipc_event_shutdown(const char *reason);
20void ipc_event_binding(struct sway_binding *binding); 21void ipc_event_binding(struct sway_binding *binding);
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index de234111..58e2dee6 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -8,6 +8,7 @@
8struct swaybar_config; 8struct swaybar_config;
9struct swaybar_output; 9struct swaybar_output;
10struct swaybar_workspace; 10struct swaybar_workspace;
11struct loop;
11 12
12struct swaybar_pointer { 13struct swaybar_pointer {
13 struct wl_pointer *pointer; 14 struct wl_pointer *pointer;
@@ -37,7 +38,7 @@ enum hotspot_event_handling {
37}; 38};
38 39
39struct swaybar_hotspot { 40struct swaybar_hotspot {
40 struct wl_list link; 41 struct wl_list link; // swaybar_output::hotspots
41 int x, y, width, height; 42 int x, y, width, height;
42 enum hotspot_event_handling (*callback)(struct swaybar_output *output, 43 enum hotspot_event_handling (*callback)(struct swaybar_output *output,
43 int x, int y, enum x11_button button, void *data); 44 int x, int y, enum x11_button button, void *data);
@@ -46,6 +47,15 @@ struct swaybar_hotspot {
46}; 47};
47 48
48struct swaybar { 49struct swaybar {
50 char *id;
51 char *mode;
52 bool mode_pango_markup;
53
54 // only relevant when bar is in "hide" mode
55 bool visible_by_modifier;
56 bool visible_by_urgency;
57 bool visible;
58
49 struct wl_display *display; 59 struct wl_display *display;
50 struct wl_compositor *compositor; 60 struct wl_compositor *compositor;
51 struct zwlr_layer_shell_v1 *layer_shell; 61 struct zwlr_layer_shell_v1 *layer_shell;
@@ -57,14 +67,16 @@ struct swaybar {
57 struct swaybar_pointer pointer; 67 struct swaybar_pointer pointer;
58 struct status_line *status; 68 struct status_line *status;
59 69
70 struct loop *eventloop;
71
60 int ipc_event_socketfd; 72 int ipc_event_socketfd;
61 int ipc_socketfd; 73 int ipc_socketfd;
62 74
63 struct wl_list outputs; 75 struct wl_list outputs; // swaybar_output::link
64}; 76};
65 77
66struct swaybar_output { 78struct swaybar_output {
67 struct wl_list link; 79 struct wl_list link; // swaybar::outputs
68 struct swaybar *bar; 80 struct swaybar *bar;
69 struct wl_output *output; 81 struct wl_output *output;
70 struct zxdg_output_v1 *xdg_output; 82 struct zxdg_output_v1 *xdg_output;
@@ -72,8 +84,8 @@ struct swaybar_output {
72 struct zwlr_layer_surface_v1 *layer_surface; 84 struct zwlr_layer_surface_v1 *layer_surface;
73 uint32_t wl_name; 85 uint32_t wl_name;
74 86
75 struct wl_list workspaces; 87 struct wl_list workspaces; // swaybar_workspace::link
76 struct wl_list hotspots; 88 struct wl_list hotspots; // swaybar_hotspot::link
77 89
78 char *name; 90 char *name;
79 bool focused; 91 bool focused;
@@ -88,7 +100,7 @@ struct swaybar_output {
88}; 100};
89 101
90struct swaybar_workspace { 102struct swaybar_workspace {
91 struct wl_list link; 103 struct wl_list link; // swaybar_output::workspaces
92 int num; 104 int num;
93 char *name; 105 char *name;
94 bool focused; 106 bool focused;
@@ -96,10 +108,24 @@ struct swaybar_workspace {
96 bool urgent; 108 bool urgent;
97}; 109};
98 110
99bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id); 111bool bar_setup(struct swaybar *bar, const char *socket_path);
100void bar_run(struct swaybar *bar); 112void bar_run(struct swaybar *bar);
101void bar_teardown(struct swaybar *bar); 113void bar_teardown(struct swaybar *bar);
102 114
115/*
116 * Determines whether the bar should be visible and changes it to be so.
117 * If the current visibility of the bar is the different to what it should be,
118 * then it adds or destroys the layer surface as required,
119 * as well as sending the cont or stop signal to the status command.
120 * If the current visibility of the bar is already what it should be,
121 * then this function is a no-op, unless moving_layer is true, which occurs
122 * when the bar changes from "hide" to "dock" mode or vice versa, and the bar
123 * needs to be destroyed and re-added in order to change its layer.
124 *
125 * Returns true if the bar is now visible, otherwise false.
126 */
127bool determine_bar_visibility(struct swaybar *bar, bool moving_layer);
128void free_hotspots(struct wl_list *list);
103void free_workspaces(struct wl_list *list); 129void free_workspaces(struct wl_list *list);
104 130
105#endif 131#endif
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index d0336c27..5d40790a 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -13,7 +13,7 @@ struct box_colors {
13}; 13};
14 14
15struct config_output { 15struct config_output {
16 struct wl_list link; 16 struct wl_list link; // swaybar_config::outputs
17 char *name; 17 char *name;
18 size_t index; 18 size_t index;
19}; 19};
@@ -31,7 +31,8 @@ struct swaybar_config {
31 char *font; 31 char *font;
32 char *sep_symbol; 32 char *sep_symbol;
33 char *mode; 33 char *mode;
34 bool mode_pango_markup; 34 char *hidden_state;
35 char *modifier;
35 bool strip_workspace_numbers; 36 bool strip_workspace_numbers;
36 bool binding_mode_indicator; 37 bool binding_mode_indicator;
37 bool wrap_scroll; 38 bool wrap_scroll;
diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h
deleted file mode 100644
index 47be5b79..00000000
--- a/include/swaybar/event_loop.h
+++ /dev/null
@@ -1,26 +0,0 @@
1#ifndef _SWAYBAR_EVENT_LOOP_H
2#define _SWAYBAR_EVENT_LOOP_H
3#include <stdbool.h>
4#include <time.h>
5
6void add_event(int fd, short mask,
7 void(*cb)(int fd, short mask, void *data),
8 void *data);
9
10// Not guaranteed to notify cb immediately
11void add_timer(timer_t timer,
12 void(*cb)(timer_t timer, void *data),
13 void *data);
14
15// Returns false if nothing exists, true otherwise
16bool remove_event(int fd);
17
18// Returns false if nothing exists, true otherwise
19bool remove_timer(timer_t timer);
20
21// Blocks and returns after sending callbacks
22void event_loop_poll(void);
23
24void init_event_loop(void);
25
26#endif
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h
index 12d9b317..d4a48e07 100644
--- a/include/swaybar/i3bar.h
+++ b/include/swaybar/i3bar.h
@@ -5,7 +5,7 @@
5#include "status_line.h" 5#include "status_line.h"
6 6
7struct i3bar_block { 7struct i3bar_block {
8 struct wl_list link; 8 struct wl_list link; // status_link::blocks
9 int ref_count; 9 int ref_count;
10 char *full_text, *short_text, *align; 10 char *full_text, *short_text, *align;
11 bool urgent; 11 bool urgent;
diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h
index 8731dac2..d8cd0c76 100644
--- a/include/swaybar/ipc.h
+++ b/include/swaybar/ipc.h
@@ -3,9 +3,9 @@
3#include <stdbool.h> 3#include <stdbool.h>
4#include "swaybar/bar.h" 4#include "swaybar/bar.h"
5 5
6bool ipc_initialize(struct swaybar *bar, const char *bar_id); 6bool ipc_initialize(struct swaybar *bar);
7bool handle_ipc_readable(struct swaybar *bar); 7bool handle_ipc_readable(struct swaybar *bar);
8void ipc_get_workspaces(struct swaybar *bar); 8bool ipc_get_workspaces(struct swaybar *bar);
9void ipc_send_workspace_command(struct swaybar *bar, const char *ws); 9void ipc_send_workspace_command(struct swaybar *bar, const char *ws);
10void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind); 10void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind);
11 11
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index ca88b0c5..957a808e 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -14,6 +14,8 @@ enum status_protocol {
14}; 14};
15 15
16struct status_line { 16struct status_line {
17 struct swaybar *bar;
18
17 pid_t pid; 19 pid_t pid;
18 int read_fd, write_fd; 20 int read_fd, write_fd;
19 FILE *read, *write; 21 FILE *read, *write;
@@ -22,6 +24,9 @@ struct status_line {
22 const char *text; 24 const char *text;
23 struct wl_list blocks; // i3bar_block::link 25 struct wl_list blocks; // i3bar_block::link
24 26
27 int stop_signal;
28 int cont_signal;
29
25 bool click_events; 30 bool click_events;
26 bool clicked; 31 bool clicked;
27 char *buffer; 32 char *buffer;
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index fbdd42a8..25b41a71 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -54,6 +54,10 @@ struct swaylock_password {
54}; 54};
55 55
56struct swaylock_state { 56struct swaylock_state {
57 struct loop *eventloop;
58 struct loop_timer *clear_indicator_timer; // clears the indicator
59 struct loop_timer *clear_password_timer; // clears the password buffer
60 struct loop_timer *verify_password_timer;
57 struct wl_display *display; 61 struct wl_display *display;
58 struct wl_compositor *compositor; 62 struct wl_compositor *compositor;
59 struct zwlr_layer_shell_v1 *layer_shell; 63 struct zwlr_layer_shell_v1 *layer_shell;
diff --git a/meson.build b/meson.build
index 42386fbc..3fb1e81e 100644
--- a/meson.build
+++ b/meson.build
@@ -89,6 +89,7 @@ if scdoc.found()
89 'sway/sway.5.scd', 89 'sway/sway.5.scd',
90 'sway/sway-bar.5.scd', 90 'sway/sway-bar.5.scd',
91 'sway/sway-input.5.scd', 91 'sway/sway-input.5.scd',
92 'sway/sway-output.5.scd',
92 'swaylock/swaylock.1.scd', 93 'swaylock/swaylock.1.scd',
93 'swaymsg/swaymsg.1.scd', 94 'swaymsg/swaymsg.1.scd',
94 'swayidle/swayidle.1.scd', 95 'swayidle/swayidle.1.scd',
@@ -113,7 +114,7 @@ if scdoc.found()
113 endforeach 114 endforeach
114endif 115endif
115 116
116add_project_arguments('-DSYSCONFDIR="/@0@/@1@"'.format(prefix, sysconfdir), language : 'c') 117add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c')
117 118
118version = get_option('sway-version') 119version = get_option('sway-version')
119if version != '' 120if version != ''
@@ -156,7 +157,7 @@ subdir('swaynag')
156subdir('swaylock') 157subdir('swaylock')
157 158
158config = configuration_data() 159config = configuration_data()
159config.set('sysconfdir', join_paths(prefix, sysconfdir)) 160config.set('sysconfdir', sysconfdir)
160config.set('datadir', join_paths(prefix, datadir)) 161config.set('datadir', join_paths(prefix, datadir))
161config.set('prefix', prefix) 162config.set('prefix', prefix)
162 163
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 03f4c557..c808aef2 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -17,7 +17,6 @@ static struct cmd_handler bar_handlers[] = {
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 },
20 { "id", bar_cmd_id },
21 { "mode", bar_cmd_mode }, 20 { "mode", bar_cmd_mode },
22 { "modifier", bar_cmd_modifier }, 21 { "modifier", bar_cmd_modifier },
23 { "output", bar_cmd_output }, 22 { "output", bar_cmd_output },
@@ -27,7 +26,6 @@ static struct cmd_handler bar_handlers[] = {
27 { "separator_symbol", bar_cmd_separator_symbol }, 26 { "separator_symbol", bar_cmd_separator_symbol },
28 { "status_command", bar_cmd_status_command }, 27 { "status_command", bar_cmd_status_command },
29 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, 28 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
30 { "swaybar_command", bar_cmd_swaybar_command },
31 { "tray_output", bar_cmd_tray_output }, 29 { "tray_output", bar_cmd_tray_output },
32 { "tray_padding", bar_cmd_tray_padding }, 30 { "tray_padding", bar_cmd_tray_padding },
33 { "workspace_buttons", bar_cmd_workspace_buttons }, 31 { "workspace_buttons", bar_cmd_workspace_buttons },
@@ -36,54 +34,49 @@ static struct cmd_handler bar_handlers[] = {
36 34
37// Must be in alphabetical order for bsearch 35// Must be in alphabetical order for bsearch
38static struct cmd_handler bar_config_handlers[] = { 36static struct cmd_handler bar_config_handlers[] = {
39 { "hidden_state", bar_cmd_hidden_state }, 37 { "id", bar_cmd_id },
40 { "mode", bar_cmd_mode } 38 { "swaybar_command", bar_cmd_swaybar_command },
41}; 39};
42 40
41// Determines whether the subcommand is valid in any bar handler struct
42static bool is_subcommand(char *name) {
43 return find_handler(name, bar_handlers, sizeof(bar_handlers)) ||
44 find_handler(name, bar_config_handlers, sizeof(bar_config_handlers));
45}
46
43struct cmd_results *cmd_bar(int argc, char **argv) { 47struct cmd_results *cmd_bar(int argc, char **argv) {
44 struct cmd_results *error = NULL; 48 struct cmd_results *error = NULL;
45 if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) { 49 if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 2))) {
46 return error; 50 return error;
47 } 51 }
48 52
49 if (find_handler(argv[0], bar_config_handlers, 53 bool spawn = false;
50 sizeof(bar_config_handlers))) { 54 struct bar_config *bar = NULL;
51 if (config->reading) { 55 if (strcmp(argv[0], "id") != 0 && is_subcommand(argv[1])) {
52 return config_subcommand(argv, argc, bar_config_handlers, 56 for (int i = 0; i < config->bars->length; ++i) {
53 sizeof(bar_config_handlers)); 57 struct bar_config *item = config->bars->items[i];
54 } 58 if (strcmp(item->id, argv[0]) == 0) {
55 return cmd_results_new(CMD_FAILURE, "bar", 59 wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]);
56 "Can only be used in config file."); 60 bar = item;
57 } 61 break;
58
59 if (argc > 1) {
60 struct bar_config *bar = NULL;
61 if (!find_handler(argv[0], bar_handlers, sizeof(bar_handlers))
62 && find_handler(argv[1], bar_handlers, sizeof(bar_handlers))) {
63 for (int i = 0; i < config->bars->length; ++i) {
64 struct bar_config *item = config->bars->items[i];
65 if (strcmp(item->id, argv[0]) == 0) {
66 wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]);
67 bar = item;
68 break;
69 }
70 } 62 }
63 }
64 if (!bar) {
65 spawn = !config->reading;
66 wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]);
67 bar = default_bar_config();
71 if (!bar) { 68 if (!bar) {
72 wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]); 69 return cmd_results_new(CMD_FAILURE, "bar",
73 bar = default_bar_config(); 70 "Unable to allocate bar state");
74 if (!bar) {
75 return cmd_results_new(CMD_FAILURE, "bar",
76 "Unable to allocate bar state");
77 }
78
79 bar->id = strdup(argv[0]);
80 } 71 }
81 config->current_bar = bar; 72
82 ++argv; --argc; 73 bar->id = strdup(argv[0]);
83 } 74 }
75 config->current_bar = bar;
76 ++argv; --argc;
84 } 77 }
85 78
86 if (!config->current_bar) { 79 if (!config->current_bar && config->reading) {
87 // Create new bar with default values 80 // Create new bar with default values
88 struct bar_config *bar = default_bar_config(); 81 struct bar_config *bar = default_bar_config();
89 if (!bar) { 82 if (!bar) {
@@ -92,18 +85,13 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
92 } 85 }
93 86
94 // set bar id 87 // set bar id
95 for (int i = 0; i < config->bars->length; ++i) { 88 const int len = 5 + numlen(config->bars->length - 1); // "bar-"+i+\0
96 if (bar == config->bars->items[i]) { 89 bar->id = malloc(len * sizeof(char));
97 const int len = 5 + numlen(i); // "bar-" + i + \0 90 if (bar->id) {
98 bar->id = malloc(len * sizeof(char)); 91 snprintf(bar->id, len, "bar-%d", config->bars->length - 1);
99 if (bar->id) { 92 } else {
100 snprintf(bar->id, len, "bar-%d", i); 93 return cmd_results_new(CMD_FAILURE,
101 } else { 94 "bar", "Unable to allocate bar ID");
102 return cmd_results_new(CMD_FAILURE,
103 "bar", "Unable to allocate bar ID");
104 }
105 break;
106 }
107 } 95 }
108 96
109 // Set current bar 97 // Set current bar
@@ -111,5 +99,32 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
111 wlr_log(WLR_DEBUG, "Creating bar %s", bar->id); 99 wlr_log(WLR_DEBUG, "Creating bar %s", bar->id);
112 } 100 }
113 101
114 return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); 102 if (find_handler(argv[0], bar_config_handlers,
103 sizeof(bar_config_handlers))) {
104 if (config->reading) {
105 return config_subcommand(argv, argc, bar_config_handlers,
106 sizeof(bar_config_handlers));
107 } else if (spawn) {
108 for (int i = config->bars->length - 1; i >= 0; i--) {
109 struct bar_config *bar = config->bars->items[i];
110 if (bar == config->current_bar) {
111 list_del(config->bars, i);
112 free_bar_config(bar);
113 break;
114 }
115 }
116 }
117 return cmd_results_new(CMD_INVALID, "bar",
118 "Can only be used in the config file.");
119 }
120
121 struct cmd_results *res =
122 config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers));
123 if (!config->reading) {
124 if (spawn) {
125 load_swaybar(config->current_bar);
126 }
127 config->current_bar = NULL;
128 }
129 return res;
115} 130}
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 502ce2c4..28adf6c7 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -32,7 +32,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
32 } 32 }
33 // free old mode 33 // free old mode
34 free(old_state); 34 free(old_state);
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 35 return NULL;
36} 36}
37 37
38struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { 38struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
@@ -50,24 +50,20 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
50 50
51 const char *state = argv[0]; 51 const char *state = argv[0];
52 if (config->reading) { 52 if (config->reading) {
53 return bar_set_hidden_state(config->current_bar, state); 53 error = bar_set_hidden_state(config->current_bar, state);
54 } 54 } else {
55 55 const char *id = argc == 2 ? argv[1] : NULL;
56 const char *id = NULL; 56 for (int i = 0; i < config->bars->length; ++i) {
57 if (argc == 2) { 57 struct bar_config *bar = config->bars->items[i];
58 id = argv[1]; 58 if (id) {
59 } 59 if (strcmp(id, bar->id) == 0) {
60 struct bar_config *bar; 60 error = bar_set_hidden_state(bar, state);
61 for (int i = 0; i < config->bars->length; ++i) { 61 break;
62 bar = config->bars->items[i]; 62 }
63 if (id && strcmp(id, bar->id) == 0) { 63 } else if ((error = bar_set_hidden_state(bar, state))) {
64 return bar_set_hidden_state(bar, state); 64 break;
65 } 65 }
66
67 error = bar_set_hidden_state(bar, state);
68 if (error) {
69 return error;
70 } 66 }
71 } 67 }
72 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 68 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
73} 69}
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index 65fa69fd..7690a852 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -13,6 +13,8 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
13 const char *oldname = config->current_bar->id; 13 const char *oldname = config->current_bar->id;
14 if (strcmp(name, oldname) == 0) { 14 if (strcmp(name, oldname) == 0) {
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); // NOP 15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); // NOP
16 } else if (strcmp(name, "id") == 0) {
17 return cmd_results_new(CMD_INVALID, "id", "id cannot be 'id'");
16 } 18 }
17 // check if id is used by a previously defined bar 19 // check if id is used by a previously defined bar
18 for (int i = 0; i < config->bars->length; ++i) { 20 for (int i = 0; i < config->bars->length; ++i) {
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 28e2d77b..dbdd3897 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -33,7 +33,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode
33 33
34 // free old mode 34 // free old mode
35 free(old_mode); 35 free(old_mode);
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return NULL;
37} 37}
38 38
39struct cmd_results *bar_cmd_mode(int argc, char **argv) { 39struct cmd_results *bar_cmd_mode(int argc, char **argv) {
@@ -51,24 +51,20 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
51 51
52 const char *mode = argv[0]; 52 const char *mode = argv[0];
53 if (config->reading) { 53 if (config->reading) {
54 return bar_set_mode(config->current_bar, mode); 54 error = bar_set_mode(config->current_bar, mode);
55 } 55 } else {
56 56 const char *id = argc == 2 ? argv[1] : NULL;
57 const char *id = NULL; 57 for (int i = 0; i < config->bars->length; ++i) {
58 if (argc == 2) { 58 struct bar_config *bar = config->bars->items[i];
59 id = argv[1]; 59 if (id) {
60 } 60 if (strcmp(id, bar->id) == 0) {
61 61 error = bar_set_mode(bar, mode);
62 struct bar_config *bar; 62 break;
63 for (int i = 0; i < config->bars->length; ++i) { 63 }
64 bar = config->bars->items[i]; 64 } else if ((error = bar_set_mode(bar, mode))) {
65 if (id && strcmp(id, bar->id) == 0) { 65 break;
66 return bar_set_mode(bar, mode); 66 }
67 }
68 error = bar_set_mode(bar, mode);
69 if (error) {
70 return error;
71 } 67 }
72 } 68 }
73 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 69 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
74} 70}
diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c
index 5b4fdc87..490393f1 100644
--- a/sway/commands/bar/status_command.c
+++ b/sway/commands/bar/status_command.c
@@ -25,7 +25,7 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
25 } 25 }
26 26
27 if (config->active && !config->validating) { 27 if (config->active && !config->validating) {
28 load_swaybars(); 28 load_swaybar(config->current_bar);
29 } 29 }
30 30
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/border.c b/sway/commands/border.c
index bfd3b9ed..cc0d635a 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -81,7 +81,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
81 border_toggle(view); 81 border_toggle(view);
82 } else { 82 } else {
83 return cmd_results_new(CMD_INVALID, "border", 83 return cmd_results_new(CMD_INVALID, "border",
84 "Expected 'border <none|normal|pixel|toggle>' " 84 "Expected 'border <none|normal|pixel|csd|toggle>' "
85 "or 'border pixel <px>'"); 85 "or 'border pixel <px>'");
86 } 86 }
87 if (argc == 2) { 87 if (argc == 2) {
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 2e0876a9..042b415f 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -20,31 +20,6 @@ struct gaps_data {
20 int amount; 20 int amount;
21}; 21};
22 22
23// gaps edge_gaps on|off|toggle
24static struct cmd_results *gaps_edge_gaps(int argc, char **argv) {
25 struct cmd_results *error;
26 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) {
27 return error;
28 }
29
30 if (strcmp(argv[1], "on") == 0) {
31 config->edge_gaps = true;
32 } else if (strcmp(argv[1], "off") == 0) {
33 config->edge_gaps = false;
34 } else if (strcmp(argv[1], "toggle") == 0) {
35 if (!config->active) {
36 return cmd_results_new(CMD_INVALID, "gaps",
37 "Cannot toggle gaps while not running.");
38 }
39 config->edge_gaps = !config->edge_gaps;
40 } else {
41 return cmd_results_new(CMD_INVALID, "gaps",
42 "gaps edge_gaps on|off|toggle");
43 }
44 arrange_root();
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46}
47
48// gaps inner|outer <px> 23// gaps inner|outer <px>
49static struct cmd_results *gaps_set_defaults(int argc, char **argv) { 24static struct cmd_results *gaps_set_defaults(int argc, char **argv) {
50 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); 25 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
@@ -68,15 +43,17 @@ static struct cmd_results *gaps_set_defaults(int argc, char **argv) {
68 return cmd_results_new(CMD_INVALID, "gaps", 43 return cmd_results_new(CMD_INVALID, "gaps",
69 "Expected 'gaps inner|outer <px>'"); 44 "Expected 'gaps inner|outer <px>'");
70 } 45 }
71 if (amount < 0) {
72 amount = 0;
73 }
74
75 if (inner) { 46 if (inner) {
76 config->gaps_inner = amount; 47 config->gaps_inner = (amount >= 0) ? amount : 0;
77 } else { 48 } else {
78 config->gaps_outer = amount; 49 config->gaps_outer = amount;
79 } 50 }
51
52 // Prevent negative outer gaps from moving windows out of the workspace.
53 if (config->gaps_outer < -config->gaps_inner) {
54 config->gaps_outer = -config->gaps_inner;
55 }
56
80 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 57 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
81} 58}
82 59
@@ -95,8 +72,12 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) {
95 *prop -= data->amount; 72 *prop -= data->amount;
96 break; 73 break;
97 } 74 }
98 if (*prop < 0) { 75 // Prevent invalid gaps configurations.
99 *prop = 0; 76 if (ws->gaps_inner < 0) {
77 ws->gaps_inner = 0;
78 }
79 if (ws->gaps_outer < -ws->gaps_inner) {
80 ws->gaps_outer = -ws->gaps_inner;
100 } 81 }
101 arrange_workspace(ws); 82 arrange_workspace(ws);
102} 83}
@@ -156,7 +137,6 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
156 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 137 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
157} 138}
158 139
159// gaps edge_gaps on|off|toggle
160// gaps inner|outer <px> - sets defaults for workspaces 140// gaps inner|outer <px> - sets defaults for workspaces
161// gaps inner|outer current|all set|plus|minus <px> - runtime only 141// gaps inner|outer current|all set|plus|minus <px> - runtime only
162struct cmd_results *cmd_gaps(int argc, char **argv) { 142struct cmd_results *cmd_gaps(int argc, char **argv) {
@@ -165,10 +145,6 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
165 return error; 145 return error;
166 } 146 }
167 147
168 if (strcmp(argv[0], "edge_gaps") == 0) {
169 return gaps_edge_gaps(argc, argv);
170 }
171
172 if (argc == 2) { 148 if (argc == 2) {
173 return gaps_set_defaults(argc, argv); 149 return gaps_set_defaults(argc, argv);
174 } 150 }
diff --git a/sway/commands/move.c b/sway/commands/move.c
index fc2f1cc1..24036f36 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -543,7 +543,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
543 if (new_output_last_ws && new_output_last_ws != new_workspace) { 543 if (new_output_last_ws && new_output_last_ws != new_workspace) {
544 struct sway_node *new_output_last_focus = 544 struct sway_node *new_output_last_focus =
545 seat_get_focus_inactive(seat, &new_output_last_ws->node); 545 seat_get_focus_inactive(seat, &new_output_last_ws->node);
546 seat_set_focus_warp(seat, new_output_last_focus, false, false); 546 seat_set_raw_focus(seat, new_output_last_focus);
547 } 547 }
548 548
549 // restore focus 549 // restore focus
@@ -556,7 +556,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
556 focus = seat_get_focus_inactive(seat, &old_ws->node); 556 focus = seat_get_focus_inactive(seat, &old_ws->node);
557 } 557 }
558 } 558 }
559 seat_set_focus_warp(seat, focus, true, false); 559 seat_set_focus(seat, focus);
560 560
561 // clean-up, destroying parents if the container was the last child 561 // clean-up, destroying parents if the container was the last child
562 if (old_parent) { 562 if (old_parent) {
@@ -593,7 +593,7 @@ static void workspace_move_to_output(struct sway_workspace *workspace,
593 char *ws_name = workspace_next_name(old_output->wlr_output->name); 593 char *ws_name = workspace_next_name(old_output->wlr_output->name);
594 struct sway_workspace *ws = workspace_create(old_output, ws_name); 594 struct sway_workspace *ws = workspace_create(old_output, ws_name);
595 free(ws_name); 595 free(ws_name);
596 seat_set_focus_workspace(seat, ws); 596 seat_set_raw_focus(seat, &ws->node);
597 } 597 }
598 598
599 workspace_consider_destroy(new_output_old_ws); 599 workspace_consider_destroy(new_output_old_ws);
@@ -704,7 +704,7 @@ static struct cmd_results *cmd_move_in_direction(
704 } 704 }
705 705
706 // Hack to re-focus container 706 // Hack to re-focus container
707 seat_set_focus_workspace(config->handler_context.seat, new_ws); 707 seat_set_raw_focus(config->handler_context.seat, &new_ws->node);
708 seat_set_focus_container(config->handler_context.seat, container); 708 seat_set_focus_container(config->handler_context.seat, container);
709 709
710 if (old_ws != new_ws) { 710 if (old_ws != new_ws) {
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index 7995cdd6..f18322b7 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -16,6 +16,11 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = config->handler_context.container; 18 struct sway_container *container = config->handler_context.container;
19
20 if (container == NULL) {
21 return cmd_results_new(CMD_FAILURE, "sticky", "No current container");
22 };
23
19 if (!container_is_floating(container)) { 24 if (!container_is_floating(container)) {
20 return cmd_results_new(CMD_FAILURE, "sticky", 25 return cmd_results_new(CMD_FAILURE, "sticky",
21 "Can't set sticky on a tiled container"); 26 "Can't set sticky on a tiled container");
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index e7f9cbea..9cc0d5c2 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -22,6 +22,7 @@ static void swap_places(struct sway_container *con1,
22 temp->width = con1->width; 22 temp->width = con1->width;
23 temp->height = con1->height; 23 temp->height = con1->height;
24 temp->parent = con1->parent; 24 temp->parent = con1->parent;
25 temp->workspace = con1->workspace;
25 26
26 con1->x = con2->x; 27 con1->x = con2->x;
27 con1->y = con2->y; 28 con1->y = con2->y;
@@ -34,8 +35,18 @@ static void swap_places(struct sway_container *con1,
34 con2->height = temp->height; 35 con2->height = temp->height;
35 36
36 int temp_index = container_sibling_index(con1); 37 int temp_index = container_sibling_index(con1);
37 container_insert_child(con2->parent, con1, container_sibling_index(con2)); 38 if (con2->parent) {
38 container_insert_child(temp->parent, con2, temp_index); 39 container_insert_child(con2->parent, con1,
40 container_sibling_index(con2));
41 } else {
42 workspace_insert_tiling(con2->workspace, con1,
43 container_sibling_index(con2));
44 }
45 if (temp->parent) {
46 container_insert_child(temp->parent, con2, temp_index);
47 } else {
48 workspace_insert_tiling(temp->workspace, con2, temp_index);
49 }
39 50
40 free(temp); 51 free(temp);
41} 52}
@@ -50,13 +61,13 @@ static void swap_focus(struct sway_container *con1,
50 enum sway_container_layout layout2 = container_parent_layout(con2); 61 enum sway_container_layout layout2 = container_parent_layout(con2);
51 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { 62 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
52 if (workspace_is_visible(ws2)) { 63 if (workspace_is_visible(ws2)) {
53 seat_set_focus_warp(seat, &con2->node, false, true); 64 seat_set_focus_warp(seat, &con2->node, false);
54 } 65 }
55 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); 66 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
56 } else if (focus == con2 && (layout1 == L_TABBED 67 } else if (focus == con2 && (layout1 == L_TABBED
57 || layout1 == L_STACKED)) { 68 || layout1 == L_STACKED)) {
58 if (workspace_is_visible(ws1)) { 69 if (workspace_is_visible(ws1)) {
59 seat_set_focus_warp(seat, &con1->node, false, true); 70 seat_set_focus_warp(seat, &con1->node, false);
60 } 71 }
61 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); 72 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
62 } else if (ws1 != ws2) { 73 } else if (ws1 != ws2) {
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 63f29641..58c2201d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,5 +1,6 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <ctype.h> 2#include <ctype.h>
3#include <limits.h>
3#include <string.h> 4#include <string.h>
4#include <strings.h> 5#include <strings.h>
5#include "sway/commands.h" 6#include "sway/commands.h"
@@ -20,8 +21,8 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
20 return NULL; 21 return NULL;
21 } 22 }
22 wsc->workspace = strdup(ws_name); 23 wsc->workspace = strdup(ws_name);
23 wsc->gaps_inner = -1; 24 wsc->gaps_inner = INT_MIN;
24 wsc->gaps_outer = -1; 25 wsc->gaps_outer = INT_MIN;
25 list_add(config->workspace_configs, wsc); 26 list_add(config->workspace_configs, wsc);
26 return wsc; 27 return wsc;
27} 28}
@@ -94,7 +95,16 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
94 return cmd_results_new(CMD_FAILURE, "workspace gaps", 95 return cmd_results_new(CMD_FAILURE, "workspace gaps",
95 "Expected 'workspace <ws> gaps inner|outer <px>'"); 96 "Expected 'workspace <ws> gaps inner|outer <px>'");
96 } 97 }
97 *prop = val >= 0 ? val : 0; 98 *prop = val;
99
100 // Prevent invalid gaps configurations.
101 if (wsc->gaps_inner < 0) {
102 wsc->gaps_inner = 0;
103 }
104 if (wsc->gaps_outer < -wsc->gaps_inner) {
105 wsc->gaps_outer = -wsc->gaps_inner;
106 }
107
98 } else { 108 } else {
99 if (config->reading || !config->active) { 109 if (config->reading || !config->active) {
100 return cmd_results_new(CMD_DEFER, "workspace", NULL); 110 return cmd_results_new(CMD_DEFER, "workspace", NULL);
@@ -132,7 +142,11 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
132 strcasecmp(argv[0], "current") == 0) { 142 strcasecmp(argv[0], "current") == 0) {
133 ws = workspace_by_name(argv[0]); 143 ws = workspace_by_name(argv[0]);
134 } else if (strcasecmp(argv[0], "back_and_forth") == 0) { 144 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
135 if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) { 145 if (!prev_workspace_name) {
146 return cmd_results_new(CMD_INVALID, "workspace",
147 "There is no previous workspace");
148 }
149 if (!(ws = workspace_by_name(argv[0]))) {
136 ws = workspace_create(NULL, prev_workspace_name); 150 ws = workspace_create(NULL, prev_workspace_name);
137 } 151 }
138 } else { 152 } else {
diff --git a/sway/config.c b/sway/config.c
index f239ba1d..89b89464 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -234,7 +234,6 @@ static void config_defaults(struct sway_config *config) {
234 config->show_marks = true; 234 config->show_marks = true;
235 config->tiling_drag = true; 235 config->tiling_drag = true;
236 236
237 config->edge_gaps = true;
238 config->smart_gaps = false; 237 config->smart_gaps = false;
239 config->gaps_inner = 0; 238 config->gaps_inner = 0;
240 config->gaps_outer = 0; 239 config->gaps_outer = 0;
diff --git a/sway/config/bar.c b/sway/config/bar.c
index c6899f57..8b88642e 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -16,6 +16,7 @@
16#include "stringop.h" 16#include "stringop.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
19#include "util.h"
19 20
20static void terminate_swaybar(pid_t pid) { 21static void terminate_swaybar(pid_t pid) {
21 wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid); 22 wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid);
@@ -101,6 +102,7 @@ struct bar_config *default_bar_config(void) {
101 bar->binding_mode_indicator = true; 102 bar->binding_mode_indicator = true;
102 bar->verbose = false; 103 bar->verbose = false;
103 bar->pid = 0; 104 bar->pid = 0;
105 bar->modifier = get_modifier_mask_by_name("Mod4");
104 if (!(bar->mode = strdup("dock"))) { 106 if (!(bar->mode = strdup("dock"))) {
105 goto cleanup; 107 goto cleanup;
106 } 108 }
@@ -226,13 +228,17 @@ static void invoke_swaybar(struct bar_config *bar) {
226 close(filedes[1]); 228 close(filedes[1]);
227} 229}
228 230
231void load_swaybar(struct bar_config *bar) {
232 if (bar->pid != 0) {
233 terminate_swaybar(bar->pid);
234 }
235 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
236 invoke_swaybar(bar);
237}
238
229void load_swaybars(void) { 239void load_swaybars(void) {
230 for (int i = 0; i < config->bars->length; ++i) { 240 for (int i = 0; i < config->bars->length; ++i) {
231 struct bar_config *bar = config->bars->items[i]; 241 struct bar_config *bar = config->bars->items[i];
232 if (bar->pid != 0) { 242 load_swaybar(bar);
233 terminate_swaybar(bar->pid);
234 }
235 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
236 invoke_swaybar(bar);
237 } 243 }
238} 244}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index adc1ee10..fc52dd28 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -223,11 +223,15 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
223 } 223 }
224} 224}
225 225
226static int scale_length(int length, int offset, float scale) {
227 return round((offset + length) * scale) - round(offset * scale);
228}
229
226static void scale_box(struct wlr_box *box, float scale) { 230static void scale_box(struct wlr_box *box, float scale) {
227 box->x *= scale; 231 box->width = scale_length(box->width, box->x, scale);
228 box->y *= scale; 232 box->height = scale_length(box->height, box->y, scale);
229 box->width *= scale; 233 box->x = round(box->x * scale);
230 box->height *= scale; 234 box->y = round(box->y * scale);
231} 235}
232 236
233struct sway_workspace *output_get_active_workspace(struct sway_output *output) { 237struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 3617da87..9b26c560 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -33,11 +33,27 @@ struct render_data {
33 float alpha; 33 float alpha;
34}; 34};
35 35
36/**
37 * Apply scale to a width or height.
38 *
39 * One does not simply multiply the width by the scale. We allow fractional
40 * scaling, which means the resulting scaled width might be a decimal.
41 * So we round it.
42 *
43 * But even this can produce undesirable results depending on the X or Y offset
44 * of the box. For example, with a scale of 1.5, a box with width=1 should not
45 * scale to 2px if its X coordinate is 1, because the X coordinate would have
46 * scaled to 2px.
47 */
48static int scale_length(int length, int offset, float scale) {
49 return round((offset + length) * scale) - round(offset * scale);
50}
51
36static void scale_box(struct wlr_box *box, float scale) { 52static void scale_box(struct wlr_box *box, float scale) {
37 box->x *= scale; 53 box->width = scale_length(box->width, box->x, scale);
38 box->y *= scale; 54 box->height = scale_length(box->height, box->y, scale);
39 box->width *= scale; 55 box->x = round(box->x * scale);
40 box->height *= scale; 56 box->y = round(box->y * scale);
41} 57}
42 58
43static void scissor_output(struct wlr_output *wlr_output, 59static void scissor_output(struct wlr_output *wlr_output,
@@ -392,14 +408,23 @@ static void render_titlebar(struct sway_output *output,
392 render_rect(output->wlr_output, output_damage, &box, color); 408 render_rect(output->wlr_output, output_damage, &box, color);
393 409
394 // Single pixel right edge 410 // Single pixel right edge
395 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; 411 box.x = x + width - TITLEBAR_BORDER_THICKNESS;
412 box.y = y + TITLEBAR_BORDER_THICKNESS;
413 box.width = TITLEBAR_BORDER_THICKNESS;
414 box.height =
415 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
416 scale_box(&box, output_scale);
396 render_rect(output->wlr_output, output_damage, &box, color); 417 render_rect(output->wlr_output, output_damage, &box, color);
397 } 418 }
398 419
399 size_t inner_width = width - TITLEBAR_H_PADDING * 2; 420 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
421 int bg_y = y + TITLEBAR_BORDER_THICKNESS;
422 int ob_bg_height = scale_length(
423 (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 +
424 config->font_height, bg_y, output_scale);
400 425
401 // Marks 426 // Marks
402 size_t marks_ob_width = 0; // output-buffer-local 427 int marks_ob_width = 0; // output-buffer-local
403 if (config->show_marks && marks_texture) { 428 if (config->show_marks && marks_texture) {
404 struct wlr_box texture_box; 429 struct wlr_box texture_box;
405 wlr_texture_get_size(marks_texture, 430 wlr_texture_get_size(marks_texture,
@@ -408,15 +433,14 @@ static void render_titlebar(struct sway_output *output,
408 433
409 // The marks texture might be shorter than the config->font_height, in 434 // The marks texture might be shorter than the config->font_height, in
410 // which case we need to pad it as evenly as possible above and below. 435 // which case we need to pad it as evenly as possible above and below.
411 int ob_padding_total = config->font_height * output_scale - 436 int ob_padding_total = ob_bg_height - texture_box.height;
412 texture_box.height; 437 int ob_padding_above = floor(ob_padding_total / 2.0);
413 int ob_padding_above = floor(ob_padding_total / 2); 438 int ob_padding_below = ceil(ob_padding_total / 2.0);
414 int ob_padding_below = ceil(ob_padding_total / 2);
415 439
416 // Render texture 440 // Render texture
417 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) 441 texture_box.x = round((x - output_x + width - TITLEBAR_H_PADDING)
418 * output_scale - texture_box.width; 442 * output_scale) - texture_box.width;
419 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale + 443 texture_box.y = round((bg_y - output_y) * output_scale) +
420 ob_padding_above; 444 ob_padding_above;
421 445
422 float matrix[9]; 446 float matrix[9];
@@ -431,29 +455,18 @@ static void render_titlebar(struct sway_output *output,
431 &texture_box, matrix, con->alpha); 455 &texture_box, matrix, con->alpha);
432 456
433 // Padding above 457 // Padding above
434 if (ob_padding_above > 0) { 458 memcpy(&color, colors->background, sizeof(float) * 4);
435 memcpy(&color, colors->background, sizeof(float) * 4); 459 premultiply_alpha(color, con->alpha);
436 premultiply_alpha(color, con->alpha); 460 box.x = texture_box.x + round(output_x * output_scale);
437 box.x = (x + width - TITLEBAR_H_PADDING) * output_scale - 461 box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale);
438 texture_box.width; 462 box.width = texture_box.width;
439 box.y = (y + TITLEBAR_V_PADDING) * output_scale; 463 box.height = ob_padding_above;
440 box.width = texture_box.width; 464 render_rect(output->wlr_output, output_damage, &box, color);
441 box.height = ob_padding_above;
442 render_rect(output->wlr_output, output_damage, &box, color);
443 }
444 465
445 // Padding below 466 // Padding below
446 if (ob_padding_below > 0) { 467 box.y += ob_padding_above + texture_box.height;
447 memcpy(&color, colors->background, sizeof(float) * 4); 468 box.height = ob_padding_below;
448 premultiply_alpha(color, con->alpha); 469 render_rect(output->wlr_output, output_damage, &box, color);
449 box.x = (x + width - TITLEBAR_H_PADDING) * output_scale -
450 texture_box.width;
451 box.y = (y + TITLEBAR_V_PADDING) * output_scale +
452 ob_padding_above + texture_box.height;
453 box.width = texture_box.width;
454 box.height = ob_padding_below;
455 render_rect(output->wlr_output, output_damage, &box, color);
456 }
457 } 470 }
458 471
459 // Title text 472 // Title text
@@ -466,89 +479,73 @@ static void render_titlebar(struct sway_output *output,
466 479
467 // The title texture might be shorter than the config->font_height, 480 // The title texture might be shorter than the config->font_height,
468 // in which case we need to pad it above and below. 481 // in which case we need to pad it above and below.
469 int ob_padding_above = (config->font_baseline - con->title_baseline) 482 int ob_padding_above = round((config->font_baseline -
470 * output_scale; 483 con->title_baseline + TITLEBAR_V_PADDING -
471 int ob_padding_below = (config->font_height - con->title_height) 484 TITLEBAR_BORDER_THICKNESS) * output_scale);
472 * output_scale - ob_padding_above; 485 int ob_padding_below = ob_bg_height - ob_padding_above -
486 texture_box.height;
473 487
474 // Render texture 488 // Render texture
475 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; 489 texture_box.x =
476 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale + 490 round((x - output_x + TITLEBAR_H_PADDING) * output_scale);
477 ob_padding_above; 491 texture_box.y =
492 round((bg_y - output_y) * output_scale) + ob_padding_above;
478 493
479 float matrix[9]; 494 float matrix[9];
480 wlr_matrix_project_box(matrix, &texture_box, 495 wlr_matrix_project_box(matrix, &texture_box,
481 WL_OUTPUT_TRANSFORM_NORMAL, 496 WL_OUTPUT_TRANSFORM_NORMAL,
482 0.0, output->wlr_output->transform_matrix); 497 0.0, output->wlr_output->transform_matrix);
483 498
484 if (inner_width * output_scale - marks_ob_width < texture_box.width) { 499 int inner_x = x - output_x + TITLEBAR_H_PADDING;
485 texture_box.width = inner_width * output_scale - marks_ob_width; 500 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
501 if (ob_inner_width - marks_ob_width < texture_box.width) {
502 texture_box.width = ob_inner_width - marks_ob_width;
486 } 503 }
487 render_texture(output->wlr_output, output_damage, title_texture, 504 render_texture(output->wlr_output, output_damage, title_texture,
488 &texture_box, matrix, con->alpha); 505 &texture_box, matrix, con->alpha);
489 506
490 // Padding above 507 // Padding above
491 if (ob_padding_above > 0) { 508 memcpy(&color, colors->background, sizeof(float) * 4);
492 memcpy(&color, colors->background, sizeof(float) * 4); 509 premultiply_alpha(color, con->alpha);
493 premultiply_alpha(color, con->alpha); 510 box.x = texture_box.x + round(output_x * output_scale);
494 box.x = (x + TITLEBAR_H_PADDING) * output_scale; 511 box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale);
495 box.y = (y + TITLEBAR_V_PADDING) * output_scale; 512 box.width = texture_box.width;
496 box.width = texture_box.width; 513 box.height = ob_padding_above;
497 box.height = ob_padding_above; 514 render_rect(output->wlr_output, output_damage, &box, color);
498 render_rect(output->wlr_output, output_damage, &box, color);
499 }
500 515
501 // Padding below 516 // Padding below
502 if (ob_padding_below > 0) { 517 box.y += ob_padding_above + texture_box.height;
503 memcpy(&color, colors->background, sizeof(float) * 4); 518 box.height = ob_padding_below;
504 premultiply_alpha(color, con->alpha); 519 render_rect(output->wlr_output, output_damage, &box, color);
505 box.x = (x + TITLEBAR_H_PADDING) * output_scale;
506 box.y = (y + TITLEBAR_V_PADDING) * output_scale +
507 ob_padding_above + texture_box.height;
508 box.width = texture_box.width;
509 box.height = ob_padding_below;
510 render_rect(output->wlr_output, output_damage, &box, color);
511 }
512 } 520 }
513 521
514 // Padding above title
515 memcpy(&color, colors->background, sizeof(float) * 4);
516 premultiply_alpha(color, con->alpha);
517 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
518 box.y = y + TITLEBAR_BORDER_THICKNESS;
519 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
520 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
521 scale_box(&box, output_scale);
522 render_rect(output->wlr_output, output_damage, &box, color);
523
524 // Padding below title
525 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
526 render_rect(output->wlr_output, output_damage, &box, color);
527
528 // Filler between title and marks 522 // Filler between title and marks
529 box.width = inner_width * output_scale - title_ob_width - marks_ob_width; 523 box.width =
524 round(inner_width * output_scale) - title_ob_width - marks_ob_width;
530 if (box.width > 0) { 525 if (box.width > 0) {
531 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; 526 box.x = round((x + TITLEBAR_H_PADDING) * output_scale) + title_ob_width;
532 box.y = (y + TITLEBAR_V_PADDING) * output_scale; 527 box.y = round(bg_y * output_scale);
533 box.height = config->font_height * output_scale; 528 box.height = ob_bg_height;
534 render_rect(output->wlr_output, output_damage, &box, color); 529 render_rect(output->wlr_output, output_damage, &box, color);
535 } 530 }
536 531
537 // Padding left of title 532 // Padding left of title
538 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; 533 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
539 box.x = x + left_offset; 534 box.x = x + left_offset;
540 box.y = y + TITLEBAR_V_PADDING; 535 box.y = y + TITLEBAR_BORDER_THICKNESS;
541 box.width = TITLEBAR_H_PADDING - left_offset; 536 box.width = TITLEBAR_H_PADDING - left_offset;
542 box.height = config->font_height; 537 box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 +
538 config->font_height;
543 scale_box(&box, output_scale); 539 scale_box(&box, output_scale);
544 render_rect(output->wlr_output, output_damage, &box, color); 540 render_rect(output->wlr_output, output_damage, &box, color);
545 541
546 // Padding right of marks 542 // Padding right of marks
547 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; 543 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
548 box.x = x + width - TITLEBAR_H_PADDING; 544 box.x = x + width - TITLEBAR_H_PADDING;
549 box.y = y + TITLEBAR_V_PADDING; 545 box.y = y + TITLEBAR_BORDER_THICKNESS;
550 box.width = TITLEBAR_H_PADDING - right_offset; 546 box.width = TITLEBAR_H_PADDING - right_offset;
551 box.height = config->font_height; 547 box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 +
548 config->font_height;
552 scale_box(&box, output_scale); 549 scale_box(&box, output_scale);
553 render_rect(output->wlr_output, output_damage, &box, color); 550 render_rect(output->wlr_output, output_damage, &box, color);
554 551
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 21e104ec..925190d6 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -597,7 +597,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
597 struct sway_output *focused_output = node_get_output(focus); 597 struct sway_output *focused_output = node_get_output(focus);
598 struct sway_output *output = node_get_output(node); 598 struct sway_output *output = node_get_output(node);
599 if (output != focused_output) { 599 if (output != focused_output) {
600 seat_set_focus_warp(seat, node, false, true); 600 seat_set_focus_warp(seat, node, false);
601 } 601 }
602 } else if (node->type == N_CONTAINER && node->sway_container->view) { 602 } else if (node->type == N_CONTAINER && node->sway_container->view) {
603 // Focus node if the following are true: 603 // Focus node if the following are true:
@@ -607,14 +607,14 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
607 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && 607 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
608 node != prev_node && 608 node != prev_node &&
609 view_is_visible(node->sway_container->view)) { 609 view_is_visible(node->sway_container->view)) {
610 seat_set_focus_warp(seat, node, false, true); 610 seat_set_focus_warp(seat, node, false);
611 } else { 611 } else {
612 struct sway_node *next_focus = 612 struct sway_node *next_focus =
613 seat_get_focus_inactive(seat, &root->node); 613 seat_get_focus_inactive(seat, &root->node);
614 if (next_focus && next_focus->type == N_CONTAINER && 614 if (next_focus && next_focus->type == N_CONTAINER &&
615 next_focus->sway_container->view && 615 next_focus->sway_container->view &&
616 view_is_visible(next_focus->sway_container->view)) { 616 view_is_visible(next_focus->sway_container->view)) {
617 seat_set_focus_warp(seat, next_focus, false, true); 617 seat_set_focus_warp(seat, next_focus, false);
618 } 618 }
619 } 619 }
620 } 620 }
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index fb1fe7b5..2c8b41cd 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -9,6 +9,7 @@
9#include "sway/input/input-manager.h" 9#include "sway/input/input-manager.h"
10#include "sway/input/keyboard.h" 10#include "sway/input/keyboard.h"
11#include "sway/input/seat.h" 11#include "sway/input/seat.h"
12#include "sway/ipc-server.h"
12#include "log.h" 13#include "log.h"
13 14
14/** 15/**
@@ -66,10 +67,10 @@ static void update_shortcut_state(struct sway_shortcut_state *state,
66 bool last_key_was_a_modifier = raw_modifiers != state->last_raw_modifiers; 67 bool last_key_was_a_modifier = raw_modifiers != state->last_raw_modifiers;
67 state->last_raw_modifiers = raw_modifiers; 68 state->last_raw_modifiers = raw_modifiers;
68 69
69 if (last_key_was_a_modifier && state->last_keycode) { 70 if (last_key_was_a_modifier && state->last_keycode) {
70 // Last pressed key before this one was a modifier 71 // Last pressed key before this one was a modifier
71 state_erase_key(state, state->last_keycode); 72 state_erase_key(state, state->last_keycode);
72 } 73 }
73 74
74 if (event->state == WLR_KEY_PRESSED) { 75 if (event->state == WLR_KEY_PRESSED) {
75 // Add current key to set; there may be duplicates 76 // Add current key to set; there may be duplicates
@@ -235,7 +236,6 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
235 code_modifiers); 236 code_modifiers);
236 } 237 }
237 238
238
239 bool handled = false; 239 bool handled = false;
240 240
241 // Identify active release binding 241 // Identify active release binding
@@ -337,6 +337,19 @@ static int handle_keyboard_repeat(void *data) {
337 return 0; 337 return 0;
338} 338}
339 339
340static void determine_bar_visibility(uint32_t modifiers) {
341 for (int i = 0; i < config->bars->length; ++i) {
342 struct bar_config *bar = config->bars->items[i];
343 if (strcmp(bar->mode, bar->hidden_state) == 0) { // both are "hide"
344 bool should_be_visible = (~modifiers & bar->modifier) == 0;
345 if (bar->visible_by_modifier != should_be_visible) {
346 bar->visible_by_modifier = should_be_visible;
347 ipc_event_bar_state_update(bar);
348 }
349 }
350 }
351}
352
340static void handle_keyboard_modifiers(struct wl_listener *listener, 353static void handle_keyboard_modifiers(struct wl_listener *listener,
341 void *data) { 354 void *data) {
342 struct sway_keyboard *keyboard = 355 struct sway_keyboard *keyboard =
@@ -346,6 +359,9 @@ static void handle_keyboard_modifiers(struct wl_listener *listener,
346 keyboard->seat_device->input_device->wlr_device; 359 keyboard->seat_device->input_device->wlr_device;
347 wlr_seat_set_keyboard(wlr_seat, wlr_device); 360 wlr_seat_set_keyboard(wlr_seat, wlr_device);
348 wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers); 361 wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers);
362
363 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
364 determine_bar_visibility(modifiers);
349} 365}
350 366
351struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, 367struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
@@ -464,7 +480,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
464 keyboard->keyboard_key.notify = handle_keyboard_key; 480 keyboard->keyboard_key.notify = handle_keyboard_key;
465 481
466 wl_list_remove(&keyboard->keyboard_modifiers.link); 482 wl_list_remove(&keyboard->keyboard_modifiers.link);
467 wl_signal_add( &wlr_device->keyboard->events.modifiers, 483 wl_signal_add(&wlr_device->keyboard->events.modifiers,
468 &keyboard->keyboard_modifiers); 484 &keyboard->keyboard_modifiers);
469 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; 485 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
470} 486}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 23f582ca..d8d2f3a4 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -184,8 +184,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
184 seat_set_focus(seat, next_focus); 184 seat_set_focus(seat, next_focus);
185 } else { 185 } else {
186 // Setting focus_inactive 186 // Setting focus_inactive
187 seat_set_focus_warp(seat, next_focus, false, false); 187 seat_set_raw_focus(seat, next_focus);
188 seat_set_focus_warp(seat, focus, false, false); 188 seat_set_raw_focus(seat, focus);
189 } 189 }
190} 190}
191 191
@@ -623,8 +623,25 @@ static void container_raise_floating(struct sway_container *con) {
623 } 623 }
624} 624}
625 625
626static void set_workspace(struct sway_seat *seat,
627 struct sway_workspace *new_ws) {
628 if (seat->workspace == new_ws) {
629 return;
630 }
631 ipc_event_workspace(seat->workspace, new_ws, "focus");
632 seat->workspace = new_ws;
633}
634
635void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
636 struct sway_seat_node *seat_node = seat_node_from_node(seat, node);
637 wl_list_remove(&seat_node->link);
638 wl_list_insert(&seat->focus_stack, &seat_node->link);
639 node_set_dirty(node);
640 node_set_dirty(node_get_parent(node));
641}
642
626void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, 643void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
627 bool warp, bool notify) { 644 bool warp) {
628 if (seat->focused_layer) { 645 if (seat->focused_layer) {
629 return; 646 return;
630 } 647 }
@@ -688,34 +705,20 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
688 if (container) { 705 if (container) {
689 struct sway_container *parent = container->parent; 706 struct sway_container *parent = container->parent;
690 while (parent) { 707 while (parent) {
691 struct sway_seat_node *seat_node = 708 seat_set_raw_focus(seat, &parent->node);
692 seat_node_from_node(seat, &parent->node);
693 wl_list_remove(&seat_node->link);
694 wl_list_insert(&seat->focus_stack, &seat_node->link);
695 node_set_dirty(&parent->node);
696 parent = parent->parent; 709 parent = parent->parent;
697 } 710 }
698 } 711 }
699 if (new_workspace) { 712 if (new_workspace) {
700 struct sway_seat_node *seat_node = 713 seat_set_raw_focus(seat, &new_workspace->node);
701 seat_node_from_node(seat, &new_workspace->node);
702 wl_list_remove(&seat_node->link);
703 wl_list_insert(&seat->focus_stack, &seat_node->link);
704 node_set_dirty(&new_workspace->node);
705 } 714 }
706 if (container) { 715 if (container) {
707 struct sway_seat_node *seat_node = 716 seat_set_raw_focus(seat, &container->node);
708 seat_node_from_node(seat, &container->node);
709 wl_list_remove(&seat_node->link);
710 wl_list_insert(&seat->focus_stack, &seat_node->link);
711 node_set_dirty(&container->node);
712 seat_send_focus(&container->node, seat); 717 seat_send_focus(&container->node, seat);
713 } 718 }
714 719
715 // emit ipc events 720 // emit ipc events
716 if (notify && new_workspace && last_workspace != new_workspace) { 721 set_workspace(seat, new_workspace);
717 ipc_event_workspace(last_workspace, new_workspace, "focus");
718 }
719 if (container && container->view) { 722 if (container && container->view) {
720 ipc_event_window(container, "focus"); 723 ipc_event_window(container, "focus");
721 } 724 }
@@ -798,17 +801,17 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
798} 801}
799 802
800void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { 803void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
801 seat_set_focus_warp(seat, node, true, true); 804 seat_set_focus_warp(seat, node, true);
802} 805}
803 806
804void seat_set_focus_container(struct sway_seat *seat, 807void seat_set_focus_container(struct sway_seat *seat,
805 struct sway_container *con) { 808 struct sway_container *con) {
806 seat_set_focus_warp(seat, con ? &con->node : NULL, true, true); 809 seat_set_focus_warp(seat, con ? &con->node : NULL, true);
807} 810}
808 811
809void seat_set_focus_workspace(struct sway_seat *seat, 812void seat_set_focus_workspace(struct sway_seat *seat,
810 struct sway_workspace *ws) { 813 struct sway_workspace *ws) {
811 seat_set_focus_warp(seat, ws ? &ws->node : NULL, true, true); 814 seat_set_focus_warp(seat, ws ? &ws->node : NULL, true);
812} 815}
813 816
814void seat_set_focus_surface(struct sway_seat *seat, 817void seat_set_focus_surface(struct sway_seat *seat,
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 2d915502..63c95503 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -349,6 +349,22 @@ void ipc_event_barconfig_update(struct bar_config *bar) {
349 json_object_put(json); 349 json_object_put(json);
350} 350}
351 351
352void ipc_event_bar_state_update(struct bar_config *bar) {
353 if (!ipc_has_event_listeners(IPC_EVENT_BAR_STATE_UPDATE)) {
354 return;
355 }
356 wlr_log(WLR_DEBUG, "Sending bar_state_update event");
357
358 json_object *json = json_object_new_object();
359 json_object_object_add(json, "id", json_object_new_string(bar->id));
360 json_object_object_add(json, "visible_by_modifier",
361 json_object_new_boolean(bar->visible_by_modifier));
362
363 const char *json_string = json_object_to_json_string(json);
364 ipc_send_event(json_string, IPC_EVENT_BAR_STATE_UPDATE);
365 json_object_put(json);
366}
367
352void ipc_event_mode(const char *mode, bool pango) { 368void ipc_event_mode(const char *mode, bool pango) {
353 if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { 369 if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {
354 return; 370 return;
@@ -651,6 +667,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
651 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); 667 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
652 } else if (strcmp(event_type, "barconfig_update") == 0) { 668 } else if (strcmp(event_type, "barconfig_update") == 0) {
653 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); 669 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
670 } else if (strcmp(event_type, "bar_state_update") == 0) {
671 client->subscribed_events |= event_mask(IPC_EVENT_BAR_STATE_UPDATE);
654 } else if (strcmp(event_type, "mode") == 0) { 672 } else if (strcmp(event_type, "mode") == 0) {
655 client->subscribed_events |= event_mask(IPC_EVENT_MODE); 673 client->subscribed_events |= event_mask(IPC_EVENT_MODE);
656 } else if (strcmp(event_type, "shutdown") == 0) { 674 } else if (strcmp(event_type, "shutdown") == 0) {
diff --git a/sway/main.c b/sway/main.c
index dea4a31c..8d39d5f1 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -287,7 +287,6 @@ int main(int argc, char **argv) {
287 } 287 }
288 } 288 }
289 289
290 // TODO: switch logging over to wlroots?
291 if (debug) { 290 if (debug) {
292 wlr_log_init(WLR_DEBUG, NULL); 291 wlr_log_init(WLR_DEBUG, NULL);
293 } else if (verbose || validate) { 292 } else if (verbose || validate) {
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 6729c9ac..873741c0 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -65,6 +65,22 @@ Sway allows configuring swaybar in the sway configuration file.
65 is given, when mouse button _n_ has been released). To disable the default 65 is given, when mouse button _n_ has been released). To disable the default
66 behavior for a button, use the command _nop_. 66 behavior for a button, use the command _nop_.
67 67
68*mode* dock|hide|invisible
69 Specifies the visibility of the bar. In _dock_ mode, it is permanently
70 visible at one edge of the screen. In _hide_ mode, it is hidden unless the
71 modifier key is pressed, though this behaviour depends on the hidden state.
72 In _invisible_ mode, it is permanently hidden. Default is _dock_.
73
74*hidden\_state* hide|show
75 Specifies the behaviour of the bar when it is in _hide_ mode. When the
76 hidden state is _hide_, then it is normally hidden, and only unhidden by
77 pressing the modifier key or in case of urgency hints. When the hidden
78 state is _show_, then it is permanently visible, drawn on top of the
79 currently visible workspace. Default is _hide_.
80
81*modifier* <Modifier>|none
82 Specifies the modifier key that shows a hidden bar. Default is _Mod4_.
83
68## TRAY 84## TRAY
69 85
70Swaybar provides a system tray where third-party applications may place icons. 86Swaybar provides a system tray where third-party applications may place icons.
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index e5673452..82273ef3 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -143,4 +143,4 @@ in their own "seat").
143 143
144# SEE ALSO 144# SEE ALSO
145 145
146*sway*(5) 146*sway*(5) *sway-output*(5)
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
new file mode 100644
index 00000000..fe5b33bc
--- /dev/null
+++ b/sway/sway-output.5.scd
@@ -0,0 +1,66 @@
1sway-output(5)
2
3# NAME
4
5sway-output - output configuration commands for sway
6
7# DESCRIPTION
8
9You may combine output commands into one, like so:
10
11 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
12
13You can get a list of output names with *swaymsg -t get\_outputs*. You may also
14match any output by using the output name "\*".
15
16# COMMANDS
17
18*output* <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]]
19 Configures the specified output to use the given mode. Modes are a
20 combination of width and height (in pixels) and a refresh rate that your
21 display can be configured to use. For a list of available modes for each
22 output, use *swaymsg -t get\_outputs*.
23
24 Examples:
25
26 output HDMI-A-1 mode 1920x1080
27
28 output HDMI-A-1 mode 1920x1080@60Hz
29
30*output* <name> position|pos <X> <Y>
31 Places the specified output at the specific position in the global
32 coordinate space.
33
34*output* <name> scale <factor>
35 Scales the specified output by the specified scale _factor_. An integer is
36 recommended, but fractional values are also supported. If a fractional
37 value are specified, be warned that it is not possible to faithfully
38 represent the contents of your windows - they will be rendered at the next
39 highest integral scale factor and downscaled. You may be better served by
40 setting an integral scale factor and adjusting the font size of your
41 applications to taste.
42
43*output* <name> background|bg <file> <mode> [<fallback\_color>]
44 Sets the wallpaper for the given output to the specified file, using the
45 given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If
46 the specified file cannot be accessed or if the image does fill the entire
47 output, a fallback color may be provided to cover the rest of the output.
48 __fallback\_color__ should be specified as _#RRGGBB_. Alpha is not
49 supported.
50
51*output* <name> background|bg <color> solid\_color
52 Sets the background of the given output to the specified color. _color_
53 should be specified as _#RRGGBB_. Alpha is not supported.
54
55*output* <name> transform <transform>
56 Sets the background transform to the given value. Can be one of "90", "180",
57 "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270"
58 to apply a rotation and flip, or "normal" to apply no transform.
59
60*output* <name> disable|enable
61 Enables or disables the specified output (all outputs are enabled by
62 default).
63
64# SEE ALSO
65
66*sway*(5) *sway-input*(5)
diff --git a/sway/sway.1.scd b/sway/sway.1.scd
index 0c2ee974..f66c4cdb 100644
--- a/sway/sway.1.scd
+++ b/sway/sway.1.scd
@@ -92,4 +92,4 @@ source contributors. For more information about sway development, see
92 92
93# SEE ALSO 93# SEE ALSO
94 94
95*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) 95*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 240e0731..f6f0e859 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -87,14 +87,15 @@ The following commands may only be used in the configuration file.
87The following commands cannot be used directly in the configuration file. 87The following commands cannot be used directly in the configuration file.
88They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). 88They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
89 89
90*border* normal|pixel [<n>] 90*border* none|normal|csd|pixel [<n>]
91 Set border style for focused window. _normal_ includes a border of 91 Set border style for focused window. _normal_ includes a border of
92 thickness _n_ and a title bar. _pixel_ is a border without title bar _n_ 92 thickness _n_ and a title bar. _pixel_ is a border without title bar _n_
93 pixels thick. Default is _normal_ with border thickness 2. 93 pixels thick. Default is _normal_ with border thickness 2. _csd_ is short
94 for client-side-decorations, which allows the client to draw its own
95 decorations.
94 96
95*border* none|toggle 97*border* toggle
96 Set border style for focused window to _none_ or _toggle_ between the 98 Cycles through the available border styles.
97 available border styles: _normal_, _pixel_, _none_.
98 99
99*exit* 100*exit*
100 Exit sway and end your Wayland session. 101 Exit sway and end your Wayland session.
@@ -420,15 +421,11 @@ The default colors are:
420 _focus\_wrapping force_. This is only available for convenience. Please 421 _focus\_wrapping force_. This is only available for convenience. Please
421 use _focus\_wrapping_ instead when possible. 422 use _focus\_wrapping_ instead when possible.
422 423
423*gaps* edge\_gaps on|off|toggle
424 When _on_, gaps will be added between windows and workspace edges if the
425 inner gap is nonzero. When _off_, gaps will only be added between views.
426 _toggle_ cannot be used in the configuration file.
427
428*gaps* inner|outer <amount> 424*gaps* inner|outer <amount>
429 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner 425 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner
430 affects spacing around each view and outer affects the spacing around each 426 affects spacing around each view and outer affects the spacing around each
431 workspace. Outer gaps are in addition to inner gaps. 427 workspace. Outer gaps are in addition to inner gaps. To reduce or remove
428 outer gaps, outer gaps can be set to a negative value.
432 429
433 This affects new workspaces only, and is used when the workspace doesn't 430 This affects new workspaces only, and is used when the workspace doesn't
434 have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>). 431 have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>).
@@ -501,61 +498,6 @@ The default colors are:
501 Prevents windows matching <criteria> from being focused automatically when 498 Prevents windows matching <criteria> from being focused automatically when
502 they're created. This has no effect on the first window in a workspace. 499 they're created. This has no effect on the first window in a workspace.
503 500
504*output* <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]]
505 Configures the specified output to use the given mode. Modes are a
506 combination of width and height (in pixels) and a refresh rate that your
507 display can be configured to use. For a list of available modes for each
508 output, use *swaymsg -t get\_outputs*.
509
510 Examples:
511
512 output HDMI-A-1 mode 1920x1080
513
514 output HDMI-A-1 mode 1920x1080@60Hz
515
516*output* <name> position|pos <X> <Y>
517 Places the specified output at the specific position in the global
518 coordinate space.
519
520*output* <name> scale <factor>
521 Scales the specified output by the specified scale _factor_. An integer is
522 recommended, but fractional values are also supported. If a fractional
523 value are specified, be warned that it is not possible to faithfully
524 represent the contents of your windows - they will be rendered at the next
525 highest integral scale factor and downscaled. You may be better served by
526 setting an integral scale factor and adjusting the font size of your
527 applications to taste.
528
529*output* <name> background|bg <file> <mode> [<fallback\_color>]
530 Sets the wallpaper for the given output to the specified file, using the
531 given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If
532 the specified file cannot be accessed or if the image does fill the entire
533 output, a fallback color may be provided to cover the rest of the output.
534 __fallback\_color__ should be specified as _#RRGGBB_. Alpha is not
535 supported.
536
537**output** <name> background|bg <color> solid\_color
538 Sets the background of the given output to the specified color. _color_
539 should be specified as _#RRGGBB_. Alpha is not supported.
540
541**output** <name> transform <transform>
542 Sets the background transform to the given value. Can be one of "90", "180",
543 "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270"
544 to apply a rotation and flip, or "normal" to apply no transform.
545
546*output* <name> disable|enable
547 Enables or disables the specified output (all outputs are enabled by
548 default).
549
550*NOTES FOR THE OUTPUT COMMANDS*
551
552You may combine output commands into one, like so:
553
554 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
555
556You can get a list of output names with *swaymsg -t get\_outputs*. You may also
557match any output by using the output name "\*".
558
559*popup\_during\_fullscreen* smart|ignore|leave\_fullscreen 501*popup\_during\_fullscreen* smart|ignore|leave\_fullscreen
560 Determines what to do when a fullscreen view opens a dialog. 502 Determines what to do when a fullscreen view opens a dialog.
561 If _smart_ (the default), the dialog will be displayed. If _ignore_, the 503 If _smart_ (the default), the dialog will be displayed. If _ignore_, the
@@ -618,6 +560,18 @@ match any output by using the output name "\*".
618*workspace\_layout* default|stacking|tabbed 560*workspace\_layout* default|stacking|tabbed
619 Specifies the initial layout for new workspaces. 561 Specifies the initial layout for new workspaces.
620 562
563# BAR CONTROL
564
565*bar hidden\_state* hide|show|toggle [<bar\_id>]
566 Sets the hidden state of the bar (see *sway-bar*(5)), either individually,
567 by specifying a bar id, or if none is given, for all bar instances.
568 _toggle_ switches between _hide_ and _show_.
569
570*bar mode* dock|hide|invisible|toggle [<bar\_id>]
571 Sets the mode of the bar (see *sway-bar*(5)), either individually,
572 by specifying a bar id, or if none is given, for all bar instances.
573 _toggle_ switches between _dock_ and _hide_.
574
621# CRITERIA 575# CRITERIA
622 576
623A criteria is a string in the form of, for example: 577A criteria is a string in the form of, for example:
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 373460a2..852d53bf 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -180,6 +180,10 @@ void arrange_workspace(struct sway_workspace *workspace) {
180 if (config->reloading) { 180 if (config->reloading) {
181 return; 181 return;
182 } 182 }
183 if (!workspace->output) {
184 // Happens when there are no outputs connected
185 return;
186 }
183 struct sway_output *output = workspace->output; 187 struct sway_output *output = workspace->output;
184 struct wlr_box *area = &output->usable_area; 188 struct wlr_box *area = &output->usable_area;
185 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", 189 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
diff --git a/sway/tree/container.c b/sway/tree/container.c
index f36fe4b0..edab7a17 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1202,8 +1202,8 @@ struct sway_container *container_split(struct sway_container *child,
1202 container_add_child(cont, child); 1202 container_add_child(cont, child);
1203 1203
1204 if (set_focus) { 1204 if (set_focus) {
1205 seat_set_focus_container(seat, cont); 1205 seat_set_raw_focus(seat, &cont->node);
1206 seat_set_focus_container(seat, child); 1206 seat_set_raw_focus(seat, &child->node);
1207 } 1207 }
1208 1208
1209 return cont; 1209 return cont;
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 4b9bbfd0..85998547 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -504,7 +504,16 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
504 } 504 }
505 505
506 // Use the focused workspace 506 // Use the focused workspace
507 return seat_get_focused_workspace(seat); 507 struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
508 if (node && node->type == N_WORKSPACE) {
509 return node->sway_workspace;
510 } else if (node && node->type == N_CONTAINER) {
511 return node->sway_container->workspace;
512 }
513
514 // If there's no focus_inactive workspace then we must be running without
515 // any outputs connected
516 return root->saved_workspaces->items[0];
508} 517}
509 518
510static bool should_focus(struct sway_view *view) { 519static bool should_focus(struct sway_view *view) {
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index d7650560..a1282c1e 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -73,10 +73,10 @@ struct sway_workspace *workspace_create(struct sway_output *output,
73 if (name) { 73 if (name) {
74 struct workspace_config *wsc = workspace_find_config(name); 74 struct workspace_config *wsc = workspace_find_config(name);
75 if (wsc) { 75 if (wsc) {
76 if (wsc->gaps_outer != -1) { 76 if (wsc->gaps_outer != INT_MIN) {
77 ws->gaps_outer = wsc->gaps_outer; 77 ws->gaps_outer = wsc->gaps_outer;
78 } 78 }
79 if (wsc->gaps_inner != -1) { 79 if (wsc->gaps_inner != INT_MIN) {
80 ws->gaps_inner = wsc->gaps_inner; 80 ws->gaps_inner = wsc->gaps_inner;
81 } 81 }
82 } 82 }
@@ -618,9 +618,6 @@ void workspace_add_gaps(struct sway_workspace *ws) {
618 if (ws->current_gaps > 0) { 618 if (ws->current_gaps > 0) {
619 return; 619 return;
620 } 620 }
621 if (!config->edge_gaps) {
622 return;
623 }
624 if (config->smart_gaps) { 621 if (config->smart_gaps) {
625 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 622 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
626 struct sway_container *focus = 623 struct sway_container *focus =
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 5b7fea71..0deba72d 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -18,7 +18,6 @@
18#endif 18#endif
19#include "swaybar/bar.h" 19#include "swaybar/bar.h"
20#include "swaybar/config.h" 20#include "swaybar/config.h"
21#include "swaybar/event_loop.h"
22#include "swaybar/i3bar.h" 21#include "swaybar/i3bar.h"
23#include "swaybar/ipc.h" 22#include "swaybar/ipc.h"
24#include "swaybar/status_line.h" 23#include "swaybar/status_line.h"
@@ -26,15 +25,28 @@
26#include "ipc-client.h" 25#include "ipc-client.h"
27#include "list.h" 26#include "list.h"
28#include "log.h" 27#include "log.h"
28#include "loop.h"
29#include "pool-buffer.h" 29#include "pool-buffer.h"
30#include "wlr-layer-shell-unstable-v1-client-protocol.h" 30#include "wlr-layer-shell-unstable-v1-client-protocol.h"
31#include "xdg-output-unstable-v1-client-protocol.h" 31#include "xdg-output-unstable-v1-client-protocol.h"
32 32
33static void bar_init(struct swaybar *bar) { 33static void bar_init(struct swaybar *bar) {
34 bar->config = init_config(); 34 bar->config = init_config();
35 bar->visible = true;
35 wl_list_init(&bar->outputs); 36 wl_list_init(&bar->outputs);
36} 37}
37 38
39void free_hotspots(struct wl_list *list) {
40 struct swaybar_hotspot *hotspot, *tmp;
41 wl_list_for_each_safe(hotspot, tmp, list, link) {
42 wl_list_remove(&hotspot->link);
43 if (hotspot->destroy) {
44 hotspot->destroy(hotspot->data);
45 }
46 free(hotspot);
47 }
48}
49
38void free_workspaces(struct wl_list *list) { 50void free_workspaces(struct wl_list *list) {
39 struct swaybar_workspace *ws, *tmp; 51 struct swaybar_workspace *ws, *tmp;
40 wl_list_for_each_safe(ws, tmp, list, link) { 52 wl_list_for_each_safe(ws, tmp, list, link) {
@@ -59,14 +71,8 @@ static void swaybar_output_free(struct swaybar_output *output) {
59 wl_output_destroy(output->output); 71 wl_output_destroy(output->output);
60 destroy_buffer(&output->buffers[0]); 72 destroy_buffer(&output->buffers[0]);
61 destroy_buffer(&output->buffers[1]); 73 destroy_buffer(&output->buffers[1]);
74 free_hotspots(&output->hotspots);
62 free_workspaces(&output->workspaces); 75 free_workspaces(&output->workspaces);
63 struct swaybar_hotspot *hotspot, *hotspot_tmp;
64 wl_list_for_each_safe(hotspot, hotspot_tmp, &output->hotspots, link) {
65 if (hotspot->destroy) {
66 hotspot->destroy(hotspot->data);
67 }
68 free(hotspot);
69 }
70 wl_list_remove(&output->link); 76 wl_list_remove(&output->link);
71 free(output->name); 77 free(output->name);
72 free(output); 78 free(output);
@@ -75,9 +81,7 @@ static void swaybar_output_free(struct swaybar_output *output) {
75static void set_output_dirty(struct swaybar_output *output) { 81static void set_output_dirty(struct swaybar_output *output) {
76 if (output->frame_scheduled) { 82 if (output->frame_scheduled) {
77 output->dirty = true; 83 output->dirty = true;
78 return; 84 } else if (output->surface) {
79 }
80 if (output->surface) {
81 render_frame(output); 85 render_frame(output);
82 } 86 }
83} 87}
@@ -335,21 +339,68 @@ const struct wl_seat_listener seat_listener = {
335}; 339};
336 340
337static void add_layer_surface(struct swaybar_output *output) { 341static void add_layer_surface(struct swaybar_output *output) {
338 if (output->surface != NULL) { 342 if (output->layer_surface) {
339 return; 343 return;
340 } 344 }
341 struct swaybar *bar = output->bar; 345 struct swaybar *bar = output->bar;
342 346
343 output->surface = wl_compositor_create_surface(bar->compositor); 347 struct swaybar_config *config = bar->config;
344 assert(output->surface); 348 bool hidden = strcmp(config->mode, "hide") == 0;
345 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( 349 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
346 bar->layer_shell, output->surface, output->output, 350 bar->layer_shell, output->surface, output->output,
351 hidden ? ZWLR_LAYER_SHELL_V1_LAYER_TOP :
347 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); 352 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
348 assert(output->layer_surface); 353 assert(output->layer_surface);
349 zwlr_layer_surface_v1_add_listener(output->layer_surface, 354 zwlr_layer_surface_v1_add_listener(output->layer_surface,
350 &layer_surface_listener, output); 355 &layer_surface_listener, output);
351 zwlr_layer_surface_v1_set_anchor(output->layer_surface, 356
352 bar->config->position); 357 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position);
358 if (hidden) {
359 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1);
360 }
361}
362
363static void destroy_layer_surface(struct swaybar_output *output) {
364 if (!output->layer_surface) {
365 return;
366 }
367 zwlr_layer_surface_v1_destroy(output->layer_surface);
368 wl_surface_attach(output->surface, NULL, 0, 0); // detach buffer
369 output->layer_surface = NULL;
370 output->width = 0;
371 output->frame_scheduled = false;
372}
373
374bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
375 struct swaybar_config *config = bar->config;
376 bool visible = !(strcmp(config->mode, "invisible") == 0 ||
377 (strcmp(config->mode, config->hidden_state) == 0 // both "hide"
378 && !bar->visible_by_modifier && !bar->visible_by_urgency));
379
380 struct swaybar_output *output;
381 if (visible == bar->visible) {
382 if (visible && moving_layer) {
383 // need to destroy layer surface to move to a different layer
384 wl_list_for_each(output, &bar->outputs, link) {
385 destroy_layer_surface(output);
386 add_layer_surface(output);
387 }
388 }
389 } else {
390 bar->visible = visible;
391 wl_list_for_each(output, &bar->outputs, link) {
392 if (visible) {
393 add_layer_surface(output);
394 } else {
395 destroy_layer_surface(output);
396 }
397 }
398 wlr_log(WLR_DEBUG, "Sending %s signal to status command",
399 visible ? "cont" : "stop");
400 kill(bar->status->pid,
401 visible ? bar->status->cont_signal : bar->status->stop_signal);
402 }
403 return visible;
353} 404}
354 405
355static bool bar_uses_output(struct swaybar *bar, const char *name) { 406static bool bar_uses_output(struct swaybar *bar, const char *name) {
@@ -420,8 +471,11 @@ static void xdg_output_handle_done(void *data,
420 wl_list_remove(&output->link); 471 wl_list_remove(&output->link);
421 wl_list_insert(&bar->outputs, &output->link); 472 wl_list_insert(&bar->outputs, &output->link);
422 473
423 add_layer_surface(output); 474 output->surface = wl_compositor_create_surface(bar->compositor);
424 set_output_dirty(output); 475 assert(output->surface);
476 if (bar->visible) {
477 add_layer_surface(output);
478 }
425 } 479 }
426} 480}
427 481
@@ -517,22 +571,26 @@ static void set_bar_dirty(struct swaybar *bar) {
517 } 571 }
518} 572}
519 573
520bool bar_setup(struct swaybar *bar, 574bool bar_setup(struct swaybar *bar, const char *socket_path) {
521 const char *socket_path, const char *bar_id) {
522 bar_init(bar); 575 bar_init(bar);
523 init_event_loop(); 576 bar->eventloop = loop_create();
524 577
525 bar->ipc_socketfd = ipc_open_socket(socket_path); 578 bar->ipc_socketfd = ipc_open_socket(socket_path);
526 bar->ipc_event_socketfd = ipc_open_socket(socket_path); 579 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
527 if (!ipc_initialize(bar, bar_id)) { 580 if (!ipc_initialize(bar)) {
528 return false; 581 return false;
529 } 582 }
530 if (bar->config->status_command) { 583 if (bar->config->status_command) {
531 bar->status = status_line_init(bar->config->status_command); 584 bar->status = status_line_init(bar->config->status_command);
585 bar->status->bar = bar;
532 } 586 }
533 587
534 bar->display = wl_display_connect(NULL); 588 bar->display = wl_display_connect(NULL);
535 assert(bar->display); 589 if (!bar->display) {
590 sway_abort("Unable to connect to the compositor. "
591 "If your compositor is running, check or set the "
592 "WAYLAND_DISPLAY environment variable.");
593 }
536 594
537 struct wl_registry *registry = wl_display_get_registry(bar->display); 595 struct wl_registry *registry = wl_display_get_registry(bar->display);
538 wl_registry_add_listener(registry, &registry_listener, bar); 596 wl_registry_add_listener(registry, &registry_listener, bar);
@@ -565,8 +623,11 @@ bool bar_setup(struct swaybar *bar,
565 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); 623 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
566 assert(pointer->cursor_surface); 624 assert(pointer->cursor_surface);
567 625
568 ipc_get_workspaces(bar); 626 if (bar->config->workspace_buttons) {
569 set_bar_dirty(bar); 627 if (ipc_get_workspaces(bar)) {
628 set_bar_dirty(bar);
629 }
630 }
570 return true; 631 return true;
571} 632}
572 633
@@ -590,21 +651,23 @@ static void status_in(int fd, short mask, void *data) {
590 if (mask & (POLLHUP | POLLERR)) { 651 if (mask & (POLLHUP | POLLERR)) {
591 status_error(bar->status, "[error reading from status command]"); 652 status_error(bar->status, "[error reading from status command]");
592 set_bar_dirty(bar); 653 set_bar_dirty(bar);
593 remove_event(fd); 654 loop_remove_fd(bar->eventloop, fd);
594 } else if (status_handle_readable(bar->status)) { 655 } else if (status_handle_readable(bar->status)) {
595 set_bar_dirty(bar); 656 set_bar_dirty(bar);
596 } 657 }
597} 658}
598 659
599void bar_run(struct swaybar *bar) { 660void bar_run(struct swaybar *bar) {
600 add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); 661 loop_add_fd(bar->eventloop, wl_display_get_fd(bar->display), POLLIN,
601 add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar); 662 display_in, bar);
663 loop_add_fd(bar->eventloop, bar->ipc_event_socketfd, POLLIN, ipc_in, bar);
602 if (bar->status) { 664 if (bar->status) {
603 add_event(bar->status->read_fd, POLLIN, status_in, bar); 665 loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN,
666 status_in, bar);
604 } 667 }
605 while (1) { 668 while (1) {
606 wl_display_flush(bar->display); 669 wl_display_flush(bar->display);
607 event_loop_poll(); 670 loop_poll(bar->eventloop);
608 } 671 }
609} 672}
610 673
@@ -625,4 +688,6 @@ void bar_teardown(struct swaybar *bar) {
625 if (bar->status) { 688 if (bar->status) {
626 status_line_free(bar->status); 689 status_line_free(bar->status);
627 } 690 }
691 free(bar->id);
692 free(bar->mode);
628} 693}
diff --git a/swaybar/config.c b/swaybar/config.c
index 09d40c24..eafb0b69 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -30,7 +30,8 @@ struct swaybar_config *init_config(void) {
30 config->pango_markup = false; 30 config->pango_markup = false;
31 config->position = parse_position("bottom"); 31 config->position = parse_position("bottom");
32 config->font = strdup("monospace 10"); 32 config->font = strdup("monospace 10");
33 config->mode = NULL; 33 config->mode = strdup("dock");
34 config->hidden_state = strdup("hide");
34 config->sep_symbol = NULL; 35 config->sep_symbol = NULL;
35 config->strip_workspace_numbers = false; 36 config->strip_workspace_numbers = false;
36 config->binding_mode_indicator = true; 37 config->binding_mode_indicator = true;
@@ -84,6 +85,7 @@ void free_config(struct swaybar_config *config) {
84 free(config->status_command); 85 free(config->status_command);
85 free(config->font); 86 free(config->font);
86 free(config->mode); 87 free(config->mode);
88 free(config->hidden_state);
87 free(config->sep_symbol); 89 free(config->sep_symbol);
88 for (int i = 0; i < config->bindings->length; i++) { 90 for (int i = 0; i < config->bindings->length; i++) {
89 struct swaybar_binding *binding = config->bindings->items[i]; 91 struct swaybar_binding *binding = config->bindings->items[i];
diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c
deleted file mode 100644
index 686b9962..00000000
--- a/swaybar/event_loop.c
+++ /dev/null
@@ -1,156 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <stdlib.h>
3#include <stdbool.h>
4#include <string.h>
5#include <strings.h>
6#include <poll.h>
7#include <time.h>
8#include "swaybar/event_loop.h"
9#include "list.h"
10
11struct event_item {
12 void (*cb)(int fd, short mask, void *data);
13 void *data;
14};
15
16struct timer_item {
17 timer_t timer;
18 void (*cb)(timer_t timer, void *data);
19 void *data;
20};
21
22static struct {
23 // The order of each must be kept consistent
24 struct { /* pollfd array */
25 struct pollfd *items;
26 int capacity;
27 int length;
28 } fds;
29 list_t *items; /* event_item list */
30
31 // Timer list
32 list_t *timers;
33} event_loop;
34
35void add_timer(timer_t timer,
36 void(*cb)(timer_t timer, void *data),
37 void *data) {
38
39 struct timer_item *item = malloc(sizeof(struct timer_item));
40 item->timer = timer;
41 item->cb = cb;
42 item->data = data;
43
44 list_add(event_loop.timers, item);
45}
46
47void add_event(int fd, short mask,
48 void(*cb)(int fd, short mask, void *data), void *data) {
49
50 struct pollfd pollfd = {
51 fd,
52 mask,
53 0,
54 };
55
56 // Resize
57 if (event_loop.fds.length == event_loop.fds.capacity) {
58 event_loop.fds.capacity += 10;
59 event_loop.fds.items = realloc(event_loop.fds.items,
60 sizeof(struct pollfd) * event_loop.fds.capacity);
61 }
62
63 event_loop.fds.items[event_loop.fds.length++] = pollfd;
64
65 struct event_item *item = malloc(sizeof(struct event_item));
66 item->cb = cb;
67 item->data = data;
68
69 list_add(event_loop.items, item);
70
71 return;
72}
73
74bool remove_event(int fd) {
75 /*
76 * Instead of removing events immediately, we mark them for deletion
77 * and clean them up later. This is so we can call remove_event inside
78 * an event callback safely.
79 */
80 for (int i = 0; i < event_loop.fds.length; ++i) {
81 if (event_loop.fds.items[i].fd == fd) {
82 event_loop.fds.items[i].fd = -1;
83 return true;
84 }
85 }
86 return false;
87}
88
89static int timer_item_timer_cmp(const void *_timer_item, const void *_timer) {
90 const struct timer_item *timer_item = _timer_item;
91 const timer_t *timer = _timer;
92 if (timer_item->timer == *timer) {
93 return 0;
94 } else {
95 return -1;
96 }
97}
98bool remove_timer(timer_t timer) {
99 int index = list_seq_find(event_loop.timers, timer_item_timer_cmp, &timer);
100 if (index != -1) {
101 free(event_loop.timers->items[index]);
102 list_del(event_loop.timers, index);
103 return true;
104 }
105 return false;
106}
107
108void event_loop_poll(void) {
109 poll(event_loop.fds.items, event_loop.fds.length, -1);
110
111 for (int i = 0; i < event_loop.fds.length; ++i) {
112 struct pollfd pfd = event_loop.fds.items[i];
113 struct event_item *item = (struct event_item *)event_loop.items->items[i];
114
115 // Always send these events
116 unsigned events = pfd.events | POLLHUP | POLLERR;
117
118 if (pfd.revents & events) {
119 item->cb(pfd.fd, pfd.revents, item->data);
120 }
121 }
122
123 // Cleanup removed events
124 int end = 0;
125 int length = event_loop.fds.length;
126 for (int i = 0; i < length; ++i) {
127 if (event_loop.fds.items[i].fd == -1) {
128 free(event_loop.items->items[i]);
129 list_del(event_loop.items, i);
130 --event_loop.fds.length;
131 } else if (end != i) {
132 event_loop.fds.items[end++] = event_loop.fds.items[i];
133 } else {
134 end = i + 1;
135 }
136 }
137
138 // check timers
139 // not tested, but seems to work
140 for (int i = 0; i < event_loop.timers->length; ++i) {
141 struct timer_item *item = event_loop.timers->items[i];
142 int overrun = timer_getoverrun(item->timer);
143 if (overrun && overrun != -1) {
144 item->cb(item->timer, item->data);
145 }
146 }
147}
148
149void init_event_loop(void) {
150 event_loop.fds.length = 0;
151 event_loop.fds.capacity = 10;
152 event_loop.fds.items = malloc(
153 event_loop.fds.capacity * sizeof(struct pollfd));
154 event_loop.items = create_list();
155 event_loop.timers = create_list();
156}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index a67814c1..e1b30b52 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -152,12 +152,12 @@ static bool ipc_parse_config(
152 json_object_put(bar_config); 152 json_object_put(bar_config);
153 return false; 153 return false;
154 } 154 }
155 json_object *markup, *mode, *hidden_bar, *position, *status_command; 155 json_object *markup, *mode, *hidden_state, *position, *status_command;
156 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; 156 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers;
157 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; 157 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs;
158 json_object *bindings; 158 json_object *bindings;
159 json_object_object_get_ex(bar_config, "mode", &mode); 159 json_object_object_get_ex(bar_config, "mode", &mode);
160 json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); 160 json_object_object_get_ex(bar_config, "hidden_state", &hidden_state);
161 json_object_object_get_ex(bar_config, "position", &position); 161 json_object_object_get_ex(bar_config, "position", &position);
162 json_object_object_get_ex(bar_config, "status_command", &status_command); 162 json_object_object_get_ex(bar_config, "status_command", &status_command);
163 json_object_object_get_ex(bar_config, "font", &font); 163 json_object_object_get_ex(bar_config, "font", &font);
@@ -220,6 +220,14 @@ static bool ipc_parse_config(
220 list_add(config->bindings, binding); 220 list_add(config->bindings, binding);
221 } 221 }
222 } 222 }
223 if (hidden_state) {
224 free(config->hidden_state);
225 config->hidden_state = strdup(json_object_get_string(hidden_state));
226 }
227 if (mode) {
228 free(config->mode);
229 config->mode = strdup(json_object_get_string(mode));
230 }
223 231
224 struct config_output *output, *tmp; 232 struct config_output *output, *tmp;
225 wl_list_for_each_safe(output, tmp, &config->outputs, link) { 233 wl_list_for_each_safe(output, tmp, &config->outputs, link) {
@@ -254,7 +262,7 @@ static bool ipc_parse_config(
254 return true; 262 return true;
255} 263}
256 264
257void ipc_get_workspaces(struct swaybar *bar) { 265bool ipc_get_workspaces(struct swaybar *bar) {
258 struct swaybar_output *output; 266 struct swaybar_output *output;
259 wl_list_for_each(output, &bar->outputs, link) { 267 wl_list_for_each(output, &bar->outputs, link) {
260 free_workspaces(&output->workspaces); 268 free_workspaces(&output->workspaces);
@@ -266,8 +274,10 @@ void ipc_get_workspaces(struct swaybar *bar) {
266 json_object *results = json_tokener_parse(res); 274 json_object *results = json_tokener_parse(res);
267 if (!results) { 275 if (!results) {
268 free(res); 276 free(res);
269 return; 277 return false;
270 } 278 }
279
280 bar->visible_by_urgency = false;
271 size_t length = json_object_array_length(results); 281 size_t length = json_object_array_length(results);
272 json_object *ws_json; 282 json_object *ws_json;
273 json_object *num, *name, *visible, *focused, *out, *urgent; 283 json_object *num, *name, *visible, *focused, *out, *urgent;
@@ -294,12 +304,16 @@ void ipc_get_workspaces(struct swaybar *bar) {
294 output->focused = true; 304 output->focused = true;
295 } 305 }
296 ws->urgent = json_object_get_boolean(urgent); 306 ws->urgent = json_object_get_boolean(urgent);
307 if (ws->urgent) {
308 bar->visible_by_urgency = true;
309 }
297 wl_list_insert(&output->workspaces, &ws->link); 310 wl_list_insert(&output->workspaces, &ws->link);
298 } 311 }
299 } 312 }
300 } 313 }
301 json_object_put(results); 314 json_object_put(results);
302 free(res); 315 free(res);
316 return determine_bar_visibility(bar, false);
303} 317}
304 318
305static void ipc_get_outputs(struct swaybar *bar) { 319static void ipc_get_outputs(struct swaybar *bar) {
@@ -345,10 +359,10 @@ void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) {
345 IPC_COMMAND, bind->command, &len)); 359 IPC_COMMAND, bind->command, &len));
346} 360}
347 361
348bool ipc_initialize(struct swaybar *bar, const char *bar_id) { 362bool ipc_initialize(struct swaybar *bar) {
349 uint32_t len = strlen(bar_id); 363 uint32_t len = strlen(bar->id);
350 char *res = ipc_single_command(bar->ipc_socketfd, 364 char *res = ipc_single_command(bar->ipc_socketfd,
351 IPC_GET_BAR_CONFIG, bar_id, &len); 365 IPC_GET_BAR_CONFIG, bar->id, &len);
352 if (!ipc_parse_config(bar->config, res)) { 366 if (!ipc_parse_config(bar->config, res)) {
353 free(res); 367 free(res);
354 return false; 368 return false;
@@ -356,56 +370,108 @@ bool ipc_initialize(struct swaybar *bar, const char *bar_id) {
356 free(res); 370 free(res);
357 ipc_get_outputs(bar); 371 ipc_get_outputs(bar);
358 372
359 const char *subscribe = "[ \"workspace\", \"mode\" ]"; 373 struct swaybar_config *config = bar->config;
360 len = strlen(subscribe); 374 char subscribe[128]; // suitably large buffer
375 len = snprintf(subscribe, 128,
376 "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]",
377 config->binding_mode_indicator ? ", \"mode\"" : "",
378 config->workspace_buttons ? ", \"workspace\"" : "");
361 free(ipc_single_command(bar->ipc_event_socketfd, 379 free(ipc_single_command(bar->ipc_event_socketfd,
362 IPC_SUBSCRIBE, subscribe, &len)); 380 IPC_SUBSCRIBE, subscribe, &len));
363 return true; 381 return true;
364} 382}
365 383
384static bool handle_bar_state_update(struct swaybar *bar, json_object *event) {
385 json_object *json_id;
386 json_object_object_get_ex(event, "id", &json_id);
387 const char *id = json_object_get_string(json_id);
388 if (strcmp(id, bar->id) != 0) {
389 return false;
390 }
391
392 json_object *visible_by_modifier;
393 json_object_object_get_ex(event, "visible_by_modifier", &visible_by_modifier);
394 bar->visible_by_modifier = json_object_get_boolean(visible_by_modifier);
395 return determine_bar_visibility(bar, false);
396}
397
398static bool handle_barconfig_update(struct swaybar *bar,
399 json_object *json_config) {
400 json_object *json_id;
401 json_object_object_get_ex(json_config, "id", &json_id);
402 const char *id = json_object_get_string(json_id);
403 if (strcmp(id, bar->id) != 0) {
404 return false;
405 }
406
407 struct swaybar_config *config = bar->config;
408
409 json_object *json_state;
410 json_object_object_get_ex(json_config, "hidden_state", &json_state);
411 const char *new_state = json_object_get_string(json_state);
412 char *old_state = config->hidden_state;
413 if (strcmp(new_state, old_state) != 0) {
414 wlr_log(WLR_DEBUG, "Changing bar hidden state to %s", new_state);
415 free(old_state);
416 config->hidden_state = strdup(new_state);
417 return determine_bar_visibility(bar, false);
418 }
419
420 free(config->mode);
421 json_object *json_mode;
422 json_object_object_get_ex(json_config, "mode", &json_mode);
423 config->mode = strdup(json_object_get_string(json_mode));
424 wlr_log(WLR_DEBUG, "Changing bar mode to %s", config->mode);
425
426 return determine_bar_visibility(bar, true);
427}
428
366bool handle_ipc_readable(struct swaybar *bar) { 429bool handle_ipc_readable(struct swaybar *bar) {
367 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); 430 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
368 if (!resp) { 431 if (!resp) {
369 return false; 432 return false;
370 } 433 }
434
435 json_object *result = json_tokener_parse(resp->payload);
436 if (!result) {
437 wlr_log(WLR_ERROR, "failed to parse payload as json");
438 free_ipc_response(resp);
439 return false;
440 }
441
442 bool bar_is_dirty = true;
371 switch (resp->type) { 443 switch (resp->type) {
372 case IPC_EVENT_WORKSPACE: 444 case IPC_EVENT_WORKSPACE:
373 ipc_get_workspaces(bar); 445 bar_is_dirty = ipc_get_workspaces(bar);
374 break; 446 break;
375 case IPC_EVENT_MODE: { 447 case IPC_EVENT_MODE: {
376 json_object *result = json_tokener_parse(resp->payload);
377 if (!result) {
378 free_ipc_response(resp);
379 wlr_log(WLR_ERROR, "failed to parse payload as json");
380 return false;
381 }
382 json_object *json_change, *json_pango_markup; 448 json_object *json_change, *json_pango_markup;
383 if (json_object_object_get_ex(result, "change", &json_change)) { 449 if (json_object_object_get_ex(result, "change", &json_change)) {
384 const char *change = json_object_get_string(json_change); 450 const char *change = json_object_get_string(json_change);
385 free(bar->config->mode); 451 free(bar->mode);
386 if (strcmp(change, "default") == 0) { 452 bar->mode = strcmp(change, "default") != 0 ? strdup(change) : NULL;
387 bar->config->mode = NULL;
388 } else {
389 bar->config->mode = strdup(change);
390 }
391 } else { 453 } else {
392 wlr_log(WLR_ERROR, "failed to parse response"); 454 wlr_log(WLR_ERROR, "failed to parse response");
393 json_object_put(result); 455 bar_is_dirty = false;
394 free_ipc_response(resp); 456 break;
395 return false;
396 } 457 }
397 if (json_object_object_get_ex(result, 458 if (json_object_object_get_ex(result,
398 "pango_markup", &json_pango_markup)) { 459 "pango_markup", &json_pango_markup)) {
399 bar->config->mode_pango_markup = json_object_get_boolean( 460 bar->mode_pango_markup = json_object_get_boolean(json_pango_markup);
400 json_pango_markup);
401 } 461 }
402 json_object_put(result);
403 break; 462 break;
404 } 463 }
464 case IPC_EVENT_BARCONFIG_UPDATE:
465 bar_is_dirty = handle_barconfig_update(bar, result);
466 break;
467 case IPC_EVENT_BAR_STATE_UPDATE:
468 bar_is_dirty = handle_bar_state_update(bar, result);
469 break;
405 default: 470 default:
406 free_ipc_response(resp); 471 bar_is_dirty = false;
407 return false; 472 break;
408 } 473 }
474 json_object_put(result);
409 free_ipc_response(resp); 475 free_ipc_response(resp);
410 return true; 476 return bar_is_dirty;
411} 477}
diff --git a/swaybar/main.c b/swaybar/main.c
index db204f4a..2672abef 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -22,7 +22,6 @@ void sway_terminate(int code) {
22 22
23int main(int argc, char **argv) { 23int main(int argc, char **argv) {
24 char *socket_path = NULL; 24 char *socket_path = NULL;
25 char *bar_id = NULL;
26 bool debug = false; 25 bool debug = false;
27 26
28 static struct option long_options[] = { 27 static struct option long_options[] = {
@@ -59,7 +58,7 @@ int main(int argc, char **argv) {
59 socket_path = strdup(optarg); 58 socket_path = strdup(optarg);
60 break; 59 break;
61 case 'b': // Type 60 case 'b': // Type
62 bar_id = strdup(optarg); 61 swaybar.id = strdup(optarg);
63 break; 62 break;
64 case 'v': 63 case 'v':
65 fprintf(stdout, "swaybar version " SWAY_VERSION "\n"); 64 fprintf(stdout, "swaybar version " SWAY_VERSION "\n");
@@ -80,7 +79,7 @@ int main(int argc, char **argv) {
80 wlr_log_init(WLR_ERROR, NULL); 79 wlr_log_init(WLR_ERROR, NULL);
81 } 80 }
82 81
83 if (!bar_id) { 82 if (!swaybar.id) {
84 wlr_log(WLR_ERROR, "No bar_id passed. " 83 wlr_log(WLR_ERROR, "No bar_id passed. "
85 "Provide --bar_id or let sway start swaybar"); 84 "Provide --bar_id or let sway start swaybar");
86 return 1; 85 return 1;
@@ -96,13 +95,12 @@ int main(int argc, char **argv) {
96 95
97 signal(SIGTERM, sig_handler); 96 signal(SIGTERM, sig_handler);
98 97
99 if (!bar_setup(&swaybar, socket_path, bar_id)) { 98 if (!bar_setup(&swaybar, socket_path)) {
100 free(socket_path); 99 free(socket_path);
101 return 1; 100 return 1;
102 } 101 }
103 102
104 free(socket_path); 103 free(socket_path);
105 free(bar_id);
106 104
107 bar_run(&swaybar); 105 bar_run(&swaybar);
108 bar_teardown(&swaybar); 106 bar_teardown(&swaybar);
diff --git a/swaybar/meson.build b/swaybar/meson.build
index 7a02a33f..0c116172 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -2,7 +2,6 @@ executable(
2 'swaybar', [ 2 'swaybar', [
3 'bar.c', 3 'bar.c',
4 'config.c', 4 'config.c',
5 'event_loop.c',
6 'i3bar.c', 5 'i3bar.c',
7 'ipc.c', 6 'ipc.c',
8 'main.c', 7 'main.c',
diff --git a/swaybar/render.c b/swaybar/render.c
index dc31a5ea..097eb462 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -296,11 +296,15 @@ static uint32_t render_status_line(cairo_t *cairo,
296 296
297static uint32_t render_binding_mode_indicator(cairo_t *cairo, 297static uint32_t render_binding_mode_indicator(cairo_t *cairo,
298 struct swaybar_output *output, double x) { 298 struct swaybar_output *output, double x) {
299 const char *mode = output->bar->mode;
300 if (!mode) {
301 return 0;
302 }
303
299 struct swaybar_config *config = output->bar->config; 304 struct swaybar_config *config = output->bar->config;
300 const char *mode = config->mode;
301 int text_width, text_height; 305 int text_width, text_height;
302 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 306 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
303 output->scale, config->mode_pango_markup, 307 output->scale, output->bar->mode_pango_markup,
304 "%s", mode); 308 "%s", mode);
305 309
306 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 310 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
@@ -333,8 +337,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
333 double text_y = height / 2.0 - text_height / 2.0; 337 double text_y = height / 2.0 - text_height / 2.0;
334 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 338 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
335 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 339 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
336 pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, 340 pango_printf(cairo, config->font, output->scale,
337 "%s", mode); 341 output->bar->mode_pango_markup, "%s", mode);
338 return output->height; 342 return output->height;
339} 343}
340 344
@@ -465,7 +469,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
465 max_height = h > max_height ? h : max_height; 469 max_height = h > max_height ? h : max_height;
466 } 470 }
467 } 471 }
468 if (config->binding_mode_indicator && config->mode) { 472 if (config->binding_mode_indicator) {
469 uint32_t h = render_binding_mode_indicator(cairo, output, x); 473 uint32_t h = render_binding_mode_indicator(cairo, output, x);
470 max_height = h > max_height ? h : max_height; 474 max_height = h > max_height ? h : max_height;
471 } 475 }
@@ -490,16 +494,12 @@ static const struct wl_callback_listener output_frame_listener = {
490 494
491void render_frame(struct swaybar_output *output) { 495void render_frame(struct swaybar_output *output) {
492 assert(output->surface != NULL); 496 assert(output->surface != NULL);
493 497 if (!output->layer_surface) {
494 struct swaybar_hotspot *hotspot, *tmp; 498 return;
495 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) {
496 if (hotspot->destroy) {
497 hotspot->destroy(hotspot->data);
498 }
499 wl_list_remove(&hotspot->link);
500 free(hotspot);
501 } 499 }
502 500
501 free_hotspots(&output->hotspots);
502
503 cairo_surface_t *recorder = cairo_recording_surface_create( 503 cairo_surface_t *recorder = cairo_recording_surface_create(
504 CAIRO_CONTENT_COLOR_ALPHA, NULL); 504 CAIRO_CONTENT_COLOR_ALPHA, NULL);
505 cairo_t *cairo = cairo_create(recorder); 505 cairo_t *cairo = cairo_create(recorder);
@@ -519,10 +519,12 @@ void render_frame(struct swaybar_output *output) {
519 if (config_height >= 0 && height < (uint32_t)config_height) { 519 if (config_height >= 0 && height < (uint32_t)config_height) {
520 height = config_height; 520 height = config_height;
521 } 521 }
522 if (height != output->height) { 522 if (height != output->height || output->width == 0) {
523 // Reconfigure surface 523 // Reconfigure surface
524 zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height); 524 zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height);
525 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); 525 if (strcmp(output->bar->config->mode, "dock") == 0) {
526 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height);
527 }
526 // TODO: this could infinite loop if the compositor assigns us a 528 // TODO: this could infinite loop if the compositor assigns us a
527 // different height than what we asked for 529 // different height than what we asked for
528 wl_surface_commit(output->surface); 530 wl_surface_commit(output->surface);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index ed6dc7c8..65d6c052 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -7,16 +7,16 @@
7#include <stdio.h> 7#include <stdio.h>
8#include <unistd.h> 8#include <unistd.h>
9#include <wlr/util/log.h> 9#include <wlr/util/log.h>
10#include "loop.h"
10#include "swaybar/bar.h" 11#include "swaybar/bar.h"
11#include "swaybar/config.h" 12#include "swaybar/config.h"
12#include "swaybar/i3bar.h" 13#include "swaybar/i3bar.h"
13#include "swaybar/event_loop.h"
14#include "swaybar/status_line.h" 14#include "swaybar/status_line.h"
15#include "readline.h" 15#include "readline.h"
16 16
17static void status_line_close_fds(struct status_line *status) { 17static void status_line_close_fds(struct status_line *status) {
18 if (status->read_fd != -1) { 18 if (status->read_fd != -1) {
19 remove_event(status->read_fd); 19 loop_remove_fd(status->bar->eventloop, status->read_fd);
20 close(status->read_fd); 20 close(status->read_fd);
21 status->read_fd = -1; 21 status->read_fd = -1;
22 } 22 }
@@ -83,6 +83,17 @@ bool status_handle_readable(struct status_line *status) {
83 return true; 83 return true;
84 } 84 }
85 } 85 }
86
87 json_object *signal;
88 if (json_object_object_get_ex(header, "stop_signal", &signal)) {
89 status->stop_signal = json_object_get_int(signal);
90 wlr_log(WLR_DEBUG, "Setting stop signal to %d", status->stop_signal);
91 }
92 if (json_object_object_get_ex(header, "cont_signal", &signal)) {
93 status->cont_signal = json_object_get_int(signal);
94 wlr_log(WLR_DEBUG, "Setting cont signal to %d", status->cont_signal);
95 }
96
86 json_object_put(header); 97 json_object_put(header);
87 98
88 wl_list_init(&status->blocks); 99 wl_list_init(&status->blocks);
@@ -121,6 +132,9 @@ bool status_handle_readable(struct status_line *status) {
121 132
122struct status_line *status_line_init(char *cmd) { 133struct status_line *status_line_init(char *cmd) {
123 struct status_line *status = calloc(1, sizeof(struct status_line)); 134 struct status_line *status = calloc(1, sizeof(struct status_line));
135 status->stop_signal = SIGSTOP;
136 status->cont_signal = SIGCONT;
137
124 status->buffer_size = 8192; 138 status->buffer_size = 8192;
125 status->buffer = malloc(status->buffer_size); 139 status->buffer = malloc(status->buffer_size);
126 140
diff --git a/swaybg/main.c b/swaybg/main.c
index 742669ef..45b7a913 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -222,7 +222,12 @@ int main(int argc, const char **argv) {
222 } 222 }
223 223
224 state.display = wl_display_connect(NULL); 224 state.display = wl_display_connect(NULL);
225 assert(state.display); 225 if (!state.display) {
226 wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
227 "If your compositor is running, check or set the "
228 "WAYLAND_DISPLAY environment variable.");
229 return 1;
230 }
226 231
227 struct wl_registry *registry = wl_display_get_registry(state.display); 232 struct wl_registry *registry = wl_display_get_registry(state.display);
228 wl_registry_add_listener(registry, &registry_listener, &state); 233 wl_registry_add_listener(registry, &registry_listener, &state);
diff --git a/swayidle/main.c b/swayidle/main.c
index 5b6c95a7..93f4c94b 100644
--- a/swayidle/main.c
+++ b/swayidle/main.c
@@ -388,7 +388,9 @@ int main(int argc, char *argv[]) {
388 388
389 state.display = wl_display_connect(NULL); 389 state.display = wl_display_connect(NULL);
390 if (state.display == NULL) { 390 if (state.display == NULL) {
391 wlr_log(WLR_ERROR, "Failed to create display"); 391 wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
392 "If your compositor is running, check or set the "
393 "WAYLAND_DISPLAY environment variable.");
392 return -3; 394 return -3;
393 } 395 }
394 396
diff --git a/swaylock/main.c b/swaylock/main.c
index d1384c6f..86dfd577 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -21,6 +21,7 @@
21#include "pool-buffer.h" 21#include "pool-buffer.h"
22#include "cairo.h" 22#include "cairo.h"
23#include "log.h" 23#include "log.h"
24#include "loop.h"
24#include "readline.h" 25#include "readline.h"
25#include "stringop.h" 26#include "stringop.h"
26#include "util.h" 27#include "util.h"
@@ -633,13 +634,9 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
633 } 634 }
634 break; 635 break;
635 case 'v': 636 case 'v':
636#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE 637 fprintf(stdout, "swaylock version " SWAY_VERSION "\n");
637 fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n", 638 exit(EXIT_SUCCESS);
638 SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); 639 break;
639#else
640 fprintf(stdout, "version unknown\n");
641#endif
642 return 1;
643 case LO_BS_HL_COLOR: 640 case LO_BS_HL_COLOR:
644 if (state) { 641 if (state) {
645 state->args.colors.bs_highlight = parse_color(optarg); 642 state->args.colors.bs_highlight = parse_color(optarg);
@@ -844,6 +841,10 @@ static int load_config(char *path, struct swaylock_state *state,
844 841
845static struct swaylock_state state; 842static struct swaylock_state state;
846 843
844static void display_in(int fd, short mask, void *data) {
845 wl_display_dispatch(state.display);
846}
847
847int main(int argc, char **argv) { 848int main(int argc, char **argv) {
848 wlr_log_init(WLR_DEBUG, NULL); 849 wlr_log_init(WLR_DEBUG, NULL);
849 initialize_pw_backend(); 850 initialize_pw_backend();
@@ -903,7 +904,11 @@ int main(int argc, char **argv) {
903 wl_list_init(&state.surfaces); 904 wl_list_init(&state.surfaces);
904 state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 905 state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
905 state.display = wl_display_connect(NULL); 906 state.display = wl_display_connect(NULL);
906 assert(state.display); 907 if (!state.display) {
908 sway_abort("Unable to connect to the compositor. "
909 "If your compositor is running, check or set the "
910 "WAYLAND_DISPLAY environment variable.");
911 }
907 912
908 struct wl_registry *registry = wl_display_get_registry(state.display); 913 struct wl_registry *registry = wl_display_get_registry(state.display);
909 wl_registry_add_listener(registry, &registry_listener, &state); 914 wl_registry_add_listener(registry, &registry_listener, &state);
@@ -946,9 +951,14 @@ int main(int argc, char **argv) {
946 daemonize(); 951 daemonize();
947 } 952 }
948 953
954 state.eventloop = loop_create();
955 loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLL_IN,
956 display_in, NULL);
957
949 state.run_display = true; 958 state.run_display = true;
950 while (wl_display_dispatch(state.display) != -1 && state.run_display) { 959 while (state.run_display) {
951 // This space intentionally left blank 960 wl_display_flush(state.display);
961 loop_poll(state.eventloop);
952 } 962 }
953 963
954 free(state.args.font); 964 free(state.args.font);
diff --git a/swaylock/password.c b/swaylock/password.c
index 6a956bcb..fecaecbf 100644
--- a/swaylock/password.c
+++ b/swaylock/password.c
@@ -8,13 +8,14 @@
8#include <xkbcommon/xkbcommon.h> 8#include <xkbcommon/xkbcommon.h>
9#include "swaylock/swaylock.h" 9#include "swaylock/swaylock.h"
10#include "swaylock/seat.h" 10#include "swaylock/seat.h"
11#include "loop.h"
11#include "unicode.h" 12#include "unicode.h"
12 13
13void clear_password_buffer(struct swaylock_password *pw) { 14void clear_password_buffer(struct swaylock_password *pw) {
14 // Use volatile keyword so so compiler can't optimize this out. 15 // Use volatile keyword so so compiler can't optimize this out.
15 volatile char *buffer = pw->buffer; 16 volatile char *buffer = pw->buffer;
16 volatile char zero = '\0'; 17 volatile char zero = '\0';
17 for (size_t i = 0; i < sizeof(buffer); ++i) { 18 for (size_t i = 0; i < sizeof(pw->buffer); ++i) {
18 buffer[i] = zero; 19 buffer[i] = zero;
19 } 20 }
20 pw->len = 0; 21 pw->len = 0;
@@ -39,6 +40,43 @@ static void append_ch(struct swaylock_password *pw, uint32_t codepoint) {
39 pw->len += utf8_size; 40 pw->len += utf8_size;
40} 41}
41 42
43static void clear_indicator(void *data) {
44 struct swaylock_state *state = data;
45 state->clear_indicator_timer = NULL;
46 state->auth_state = AUTH_STATE_IDLE;
47 damage_state(state);
48}
49
50static void schedule_indicator_clear(struct swaylock_state *state) {
51 if (state->clear_indicator_timer) {
52 loop_remove_timer(state->eventloop, state->clear_indicator_timer);
53 }
54 state->clear_indicator_timer = loop_add_timer(
55 state->eventloop, 3000, clear_indicator, state);
56}
57
58static void clear_password(void *data) {
59 struct swaylock_state *state = data;
60 state->clear_password_timer = NULL;
61 state->auth_state = AUTH_STATE_CLEAR;
62 clear_password_buffer(&state->password);
63 damage_state(state);
64 schedule_indicator_clear(state);
65}
66
67static void schedule_password_clear(struct swaylock_state *state) {
68 if (state->clear_password_timer) {
69 loop_remove_timer(state->eventloop, state->clear_password_timer);
70 }
71 state->clear_password_timer = loop_add_timer(
72 state->eventloop, 10000, clear_password, state);
73}
74
75static void handle_preverify_timeout(void *data) {
76 struct swaylock_state *state = data;
77 state->verify_password_timer = NULL;
78}
79
42void swaylock_handle_key(struct swaylock_state *state, 80void swaylock_handle_key(struct swaylock_state *state,
43 xkb_keysym_t keysym, uint32_t codepoint) { 81 xkb_keysym_t keysym, uint32_t codepoint) {
44 switch (keysym) { 82 switch (keysym) {
@@ -50,7 +88,18 @@ void swaylock_handle_key(struct swaylock_state *state,
50 88
51 state->auth_state = AUTH_STATE_VALIDATING; 89 state->auth_state = AUTH_STATE_VALIDATING;
52 damage_state(state); 90 damage_state(state);
53 while (wl_display_dispatch(state->display) != -1 && state->run_display) { 91
92 // We generally want to wait until all surfaces are showing the
93 // "verifying" state before we go and verify the password, because
94 // verifying it is a blocking operation. However, if the surface is on
95 // an output with DPMS off then it won't update, so we set a timer.
96 state->verify_password_timer = loop_add_timer(
97 state->eventloop, 50, handle_preverify_timeout, state);
98
99 while (state->run_display && state->verify_password_timer) {
100 wl_display_flush(state->display);
101 loop_poll(state->eventloop);
102
54 bool ok = 1; 103 bool ok = 1;
55 struct swaylock_surface *surface; 104 struct swaylock_surface *surface;
56 wl_list_for_each(surface, &state->surfaces, link) { 105 wl_list_for_each(surface, &state->surfaces, link) {
@@ -70,6 +119,7 @@ void swaylock_handle_key(struct swaylock_state *state,
70 } 119 }
71 state->auth_state = AUTH_STATE_INVALID; 120 state->auth_state = AUTH_STATE_INVALID;
72 damage_state(state); 121 damage_state(state);
122 schedule_indicator_clear(state);
73 break; 123 break;
74 case XKB_KEY_Delete: 124 case XKB_KEY_Delete:
75 case XKB_KEY_BackSpace: 125 case XKB_KEY_BackSpace:
@@ -79,11 +129,14 @@ void swaylock_handle_key(struct swaylock_state *state,
79 state->auth_state = AUTH_STATE_CLEAR; 129 state->auth_state = AUTH_STATE_CLEAR;
80 } 130 }
81 damage_state(state); 131 damage_state(state);
132 schedule_indicator_clear(state);
133 schedule_password_clear(state);
82 break; 134 break;
83 case XKB_KEY_Escape: 135 case XKB_KEY_Escape:
84 clear_password_buffer(&state->password); 136 clear_password_buffer(&state->password);
85 state->auth_state = AUTH_STATE_CLEAR; 137 state->auth_state = AUTH_STATE_CLEAR;
86 damage_state(state); 138 damage_state(state);
139 schedule_indicator_clear(state);
87 break; 140 break;
88 case XKB_KEY_Caps_Lock: 141 case XKB_KEY_Caps_Lock:
89 /* The state is getting active after this 142 /* The state is getting active after this
@@ -91,6 +144,8 @@ void swaylock_handle_key(struct swaylock_state *state,
91 state->xkb.caps_lock = !state->xkb.caps_lock; 144 state->xkb.caps_lock = !state->xkb.caps_lock;
92 state->auth_state = AUTH_STATE_INPUT_NOP; 145 state->auth_state = AUTH_STATE_INPUT_NOP;
93 damage_state(state); 146 damage_state(state);
147 schedule_indicator_clear(state);
148 schedule_password_clear(state);
94 break; 149 break;
95 case XKB_KEY_Shift_L: 150 case XKB_KEY_Shift_L:
96 case XKB_KEY_Shift_R: 151 case XKB_KEY_Shift_R:
@@ -104,12 +159,15 @@ void swaylock_handle_key(struct swaylock_state *state,
104 case XKB_KEY_Super_R: 159 case XKB_KEY_Super_R:
105 state->auth_state = AUTH_STATE_INPUT_NOP; 160 state->auth_state = AUTH_STATE_INPUT_NOP;
106 damage_state(state); 161 damage_state(state);
162 schedule_indicator_clear(state);
163 schedule_password_clear(state);
107 break; 164 break;
108 case XKB_KEY_u: 165 case XKB_KEY_u:
109 if (state->xkb.control) { 166 if (state->xkb.control) {
110 clear_password_buffer(&state->password); 167 clear_password_buffer(&state->password);
111 state->auth_state = AUTH_STATE_CLEAR; 168 state->auth_state = AUTH_STATE_CLEAR;
112 damage_state(state); 169 damage_state(state);
170 schedule_indicator_clear(state);
113 break; 171 break;
114 } 172 }
115 // fallthrough 173 // fallthrough
@@ -118,6 +176,8 @@ void swaylock_handle_key(struct swaylock_state *state,
118 append_ch(&state->password, codepoint); 176 append_ch(&state->password, codepoint);
119 state->auth_state = AUTH_STATE_INPUT; 177 state->auth_state = AUTH_STATE_INPUT;
120 damage_state(state); 178 damage_state(state);
179 schedule_indicator_clear(state);
180 schedule_password_clear(state);
121 } 181 }
122 break; 182 break;
123 } 183 }
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index 69da851e..fa6bbe05 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -342,7 +342,11 @@ static const struct wl_registry_listener registry_listener = {
342 342
343void swaynag_setup(struct swaynag *swaynag) { 343void swaynag_setup(struct swaynag *swaynag) {
344 swaynag->display = wl_display_connect(NULL); 344 swaynag->display = wl_display_connect(NULL);
345 assert(swaynag->display); 345 if (!swaynag->display) {
346 sway_abort("Unable to connect to the compositor. "
347 "If your compositor is running, check or set the "
348 "WAYLAND_DISPLAY environment variable.");
349 }
346 350
347 swaynag->scale = 1; 351 swaynag->scale = 1;
348 wl_list_init(&swaynag->outputs); 352 wl_list_init(&swaynag->outputs);