diff options
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 @@ [IRC канала](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на irc.freenode.net). -**Внимание**: На този етап в Sway няма да бъдат добавяни нови функции, докато не приключим с интеграцията на Sway и wlroots. Все още се оправят бъгове. - [](https://sr.ht/ICd5.png) Ако желаете, може да дарите на [Patreon страницата на автора](https://patreon.com/sircmpwn), което ще помогне за цялостното здраве и развитие на проекта. @@ -21,14 +19,6 @@ irc.freenode.net). Версии подписани с ключ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) и публикувани в [GitHub](https://github.com/swaywm/sway/releases). -## Статус - -- [i3 поддръжка](https://github.com/swaywm/sway/issues/2) -- [i3-bar поддръжка](https://github.com/swaywm/sway/issues/343) -- [i3-gaps поддръжка](https://github.com/swaywm/sway/issues/307) -- [IPC поддръжка](https://github.com/swaywm/sway/issues/98) -- [Сигурност](https://github.com/swaywm/sway/issues/984) - ## Инсталация ### От пакети 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 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) signiert und [auf Github](https://github.com/swaywm/sway/releases) veröffentlicht. -## Status - -- [i3-Features](https://github.com/swaywm/sway/issues/2) -- [IPC-Features](https://github.com/swaywm/sway/issues/98) -- [i3bar-Features](https://github.com/swaywm/sway/issues/343) -- [i3-gaps-Features](https://github.com/swaywm/sway/issues/307) -- [Sicherheitsfeatures](https://github.com/swaywm/sway/issues/984) - ## Installation ### 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 και θα με βρείτ� Οι εκδόσεις υπογράφονται ως [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) και δημοσιεύονται στο [GitHub](https://github.com/swaywm/sway/releases). -## Κατάσταση - -- [Υποστήριξη δυνατοτήτων του i3](https://github.com/swaywm/sway/issues/2) -- [Υποστήριξη δυνατοτήτων IPC](https://github.com/swaywm/sway/issues/98) -- [Υποστήριξη δυνατοτήτων i3bar](https://github.com/swaywm/sway/issues/343) -- [Υποστήριξη δυνατοτήτων i3-gaps](https://github.com/swaywm/sway/issues/307) -- [Δυνατότητες Ασφαλείας](https://github.com/swaywm/sway/issues/984) - ## Εγκατάσταση ### Από Πακέτα 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. Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) et publiées [sur GitHub](https://github.com/swaywm/sway/releases). -## Statut - -- [support des fonctionnalités d'i3](https://github.com/swaywm/sway/issues/2) -- [support des fonctionnalités d'IPC](https://github.com/swaywm/sway/issues/98) -- [support des fonctionnalités d'i3bar](https://github.com/swaywm/sway/issues/343) -- [support des fonctionnalités d'i3-gaps](https://github.com/swaywm/sway/issues/307) -- [fonctionnalités de sécurité](https://github.com/swaywm/sway/issues/984) - ## Installation ### À 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] Le release sono firmate con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) e pubblicate [su GitHub](https://github.com/swaywm/sway/releases). -## Status - -- [supporto funzionalità i3](https://github.com/swaywm/sway/issues/2) -- [supporto funzionalità IPC](https://github.com/swaywm/sway/issues/98) -- [supporto funzionalità i3bar](https://github.com/swaywm/sway/issues/343) -- [supporto funzionalità i3-gaps](https://github.com/swaywm/sway/issues/307) -- [sicurezza](https://github.com/swaywm/sway/issues/984) - ## Installazione ### 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/)コンポジタです。 [FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 [IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 -**注意**: 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)をご覧ください。 - [](https://sr.ht/ICd5.png) Swayの開発を支援したい場合は、[SirCmpwnのPatreon](https://patreon.com/sircmpwn)や、特定の機能に対する[報奨金のページ](https://github.com/swaywm/sway/issues/986)から寄付ができます。誰でも報奨金を請求できますし、自分の欲しい機能に報奨金を懸ける事も出来ます。またSwayのメンテナンスを支援するには、Patreonがより有用です。 ## 日本語サポート + SirCmpwnは、日本語でのサポートをIRCとGitHubで行います。タイムゾーンはUTC-4です。 ## リリースの署名 Swayのリリースは[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)で署名され、[GitHub](https://github.com/swaywm/sway/releases)で公開されています。 -## 開発状況 - -- [i3の機能のサポート](https://github.com/swaywm/sway/issues/2) -- [IPCの機能のサポート](https://github.com/swaywm/sway/issues/98) -- [i3barの機能のサポート](https://github.com/swaywm/sway/issues/343) -- [i3-gapsの機能のサポート](https://github.com/swaywm/sway/issues/307) -- [セキュリティ機能](https://github.com/swaywm/sway/issues/984) - ## インストール ### パッケージから @@ -9,11 +9,6 @@ Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net). -**Notice**: work is well underway to port sway to -[wlroots](https://github.com/swaywm/wlroots). This is **unstable** and -**unsupported** - we accept patches, but are not fond of bug reports. We are no -longer accepting bugs for 0.15. - If you'd like to support sway development, please contribute to [SirCmpwn's Patreon page](https://patreon.com/sircmpwn). @@ -22,14 +17,6 @@ Patreon page](https://patreon.com/sircmpwn). Releases are signed with [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) and published [on GitHub](https://github.com/swaywm/sway/releases). -## Status - -- [i3 feature support](https://github.com/swaywm/sway/issues/2) -- [IPC feature support](https://github.com/swaywm/sway/issues/98) -- [i3bar feature support](https://github.com/swaywm/sway/issues/343) -- [i3-gaps feature support](https://github.com/swaywm/sway/issues/307) -- [security features](https://github.com/swaywm/sway/issues/984) - ## Installation ### 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. [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) e publicadas [no GitHub](https://github.com/swaywm/sway/releases). -## Status - -- [Suporte aos recursos do i3](https://github.com/swaywm/sway/issues/2) -- [Suporte aos recursos IPC](https://github.com/swaywm/sway/issues/98) -- [Suporte aos recursos do i3bar](https://github.com/swaywm/sway/issues/343) -- [Suporte aos recursos do i3-gaps](https://github.com/swaywm/sway/issues/307) -- [Recursos de segurança](https://github.com/swaywm/sway/issues/984) - ## Instalação ### 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/) компо� [IRC каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на irc.freenode.net). -**Внимание**: на данный момент ведется активная интеграция wlroots, в связи с чем разработка sway приостановлена, однако патчи продолжают приниматься. - [](https://sr.ht/ICd5.png) При желании поддержать разработку Sway вы можете пожертвовать [автору @@ -26,14 +24,6 @@ DarkReef оказывает поддержку на русском языке в Версии подписаны ключом [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) и опубликованы [на GitHub'е](https://github.com/swaywm/sway/releases). -## Статус - -- [Поддержка i3](https://github.com/swaywm/sway/issues/2) -- [Поддержка i3-bar](https://github.com/swaywm/sway/issues/343) -- [Поддержка i3-gaps](https://github.com/swaywm/sway/issues/307) -- [Поддержка IPC](https://github.com/swaywm/sway/issues/98) -- [Безопасность](https://github.com/swaywm/sway/issues/984) - ## Установка ### Из пакета 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-спільноті. Будьте терплячі, вам о� Випуски підписані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) та публікуються на сторінці [GitHub](https://github.com/swaywm/sway/releases). -## Стан розробки - -- [Підтримка функцій i3](https://github.com/swaywm/sway/issues/2) -- [Реалізація IPC-протоколу i3](https://github.com/swaywm/sway/issues/98) -- [Підтримка функцій i3bar](https://github.com/swaywm/sway/issues/343) -- [Підтримка функцій i3-gaps](https://github.com/swaywm/sway/issues/307) -- [Функції безпеки](https://github.com/swaywm/sway/issues/984) - ## Встановлення ### З пакунків 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 @@ +#define _POSIX_C_SOURCE 199309L +#include <limits.h> +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <poll.h> +#include <time.h> +#include <unistd.h> +#include "list.h" +#include "log.h" +#include "loop.h" + +struct loop_fd_event { + void (*callback)(int fd, short mask, void *data); + void *data; +}; + +struct loop_timer { + void (*callback)(void *data); + void *data; + struct timespec expiry; +}; + +struct loop { + struct pollfd *fds; + int fd_length; + int fd_capacity; + + list_t *fd_events; // struct loop_fd_event + list_t *timers; // struct loop_timer +}; + +struct loop *loop_create(void) { + struct loop *loop = calloc(1, sizeof(struct loop)); + if (!loop) { + wlr_log(WLR_ERROR, "Unable to allocate memory for loop"); + return NULL; + } + loop->fd_capacity = 10; + loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity); + loop->fd_events = create_list(); + loop->timers = create_list(); + return loop; +} + +void loop_destroy(struct loop *loop) { + list_foreach(loop->fd_events, free); + list_foreach(loop->timers, free); + list_free(loop->fd_events); + list_free(loop->timers); + free(loop->fds); + free(loop); +} + +void loop_poll(struct loop *loop) { + // Calculate next timer in ms + int ms = INT_MAX; + if (loop->timers->length) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + for (int i = 0; i < loop->timers->length; ++i) { + struct loop_timer *timer = loop->timers->items[i]; + int timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000; + timer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000; + if (timer_ms < ms) { + ms = timer_ms; + } + } + } + if (ms < 0) { + ms = 0; + } + + poll(loop->fds, loop->fd_length, ms); + + // Dispatch fds + for (int i = 0; i < loop->fd_length; ++i) { + struct pollfd pfd = loop->fds[i]; + struct loop_fd_event *event = loop->fd_events->items[i]; + + // Always send these events + unsigned events = pfd.events | POLLHUP | POLLERR; + + if (pfd.revents & events) { + event->callback(pfd.fd, pfd.revents, event->data); + } + } + + // Dispatch timers + if (loop->timers->length) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + for (int i = 0; i < loop->timers->length; ++i) { + struct loop_timer *timer = loop->timers->items[i]; + bool expired = timer->expiry.tv_sec < now.tv_sec || + (timer->expiry.tv_sec == now.tv_sec && + timer->expiry.tv_nsec < now.tv_nsec); + if (expired) { + timer->callback(timer->data); + loop_remove_timer(loop, timer); + --i; + } + } + } +} + +void loop_add_fd(struct loop *loop, int fd, short mask, + void (*callback)(int fd, short mask, void *data), void *data) { + struct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event)); + if (!event) { + wlr_log(WLR_ERROR, "Unable to allocate memory for event"); + return; + } + event->callback = callback; + event->data = data; + list_add(loop->fd_events, event); + + struct pollfd pfd = {fd, mask, 0}; + + if (loop->fd_length == loop->fd_capacity) { + loop->fd_capacity += 10; + loop->fds = realloc(loop->fds, + sizeof(struct pollfd) * loop->fd_capacity); + } + + loop->fds[loop->fd_length++] = pfd; +} + +struct loop_timer *loop_add_timer(struct loop *loop, int ms, + void (*callback)(void *data), void *data) { + struct loop_timer *timer = calloc(1, sizeof(struct loop_timer)); + if (!timer) { + wlr_log(WLR_ERROR, "Unable to allocate memory for timer"); + return NULL; + } + timer->callback = callback; + timer->data = data; + + clock_gettime(CLOCK_MONOTONIC, &timer->expiry); + timer->expiry.tv_sec += ms / 1000; + + long int nsec = (ms % 1000) * 1000000; + if (timer->expiry.tv_nsec + nsec >= 1000000000) { + timer->expiry.tv_sec++; + nsec -= 1000000000; + } + timer->expiry.tv_nsec += nsec; + + list_add(loop->timers, timer); + + return timer; +} + +bool loop_remove_fd(struct loop *loop, int fd) { + for (int i = 0; i < loop->fd_length; ++i) { + if (loop->fds[i].fd == fd) { + free(loop->fd_events->items[i]); + list_del(loop->fd_events, i); + + loop->fd_length--; + memmove(&loop->fds[i], &loop->fds[i + 1], + sizeof(struct pollfd) * (loop->fd_length - i)); + + return true; + } + } + return false; +} + +bool loop_remove_timer(struct loop *loop, struct loop_timer *timer) { + for (int i = 0; i < loop->timers->length; ++i) { + if (loop->timers->items[i] == timer) { + list_del(loop->timers, i); + free(timer); + return true; + } + } + return false; +} 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( 'cairo.c', 'ipc-client.c', 'log.c', + 'loop.c', 'list.c', 'pango.c', '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 @@ #include "log.h" #include "stringop.h" +static const char overflow[] = "[buffer overflow]"; +static const int max_chars = 16384; + size_t escape_markup_text(const char *src, char *dest) { size_t length = 0; if (dest) { @@ -84,12 +87,12 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...) { - static char buf[2048]; + char buf[max_chars]; va_list args; va_start(args, fmt); - if (vsnprintf(buf, 2048, fmt, args) >= 2048) { - strcpy(buf, "[buffer overflow]"); + if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) { + strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow); } va_end(args); @@ -104,12 +107,12 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, void pango_printf(cairo_t *cairo, const char *font, double scale, bool markup, const char *fmt, ...) { - static char buf[2048]; + char buf[max_chars]; va_list args; va_start(args, fmt); - if (vsnprintf(buf, 2048, fmt, args) >= 2048) { - strcpy(buf, "[buffer overflow]"); + if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) { + strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow); } va_end(args); 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 { IPC_EVENT_BINDING = ((1<<31) | 5), IPC_EVENT_SHUTDOWN = ((1<<31) | 6), IPC_EVENT_TICK = ((1<<31) | 7), + + // sway-specific event types + IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20), }; #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 @@ +#ifndef _SWAY_LOOP_H +#define _SWAY_LOOP_H +#include <stdbool.h> + +/** + * This is an event loop system designed for sway clients, not sway itself. + * + * The loop consists of file descriptors and timers. Typically the Wayland + * display's file descriptor will be one of the fds in the loop. + */ + +struct loop; +struct loop_timer; + +/** + * Create an event loop. + */ +struct loop *loop_create(void); + +/** + * Destroy the event loop (eg. on program termination). + */ +void loop_destroy(struct loop *loop); + +/** + * Poll the event loop. This will block until one of the fds has data. + */ +void loop_poll(struct loop *loop); + +/** + * Add a file descriptor to the loop. + */ +void loop_add_fd(struct loop *loop, int fd, short mask, + void (*func)(int fd, short mask, void *data), void *data); + +/** + * Add a timer to the loop. + * + * When the timer expires, the timer will be removed from the loop and freed. + */ +struct loop_timer *loop_add_timer(struct loop *loop, int ms, + void (*callback)(void *data), void *data); + +/** + * Remove a file descriptor from the loop. + */ +bool loop_remove_fd(struct loop *loop, int fd); + +/** + * Remove a timer from the loop. + */ +bool loop_remove_timer(struct loop *loop, struct loop_timer *timer); + +#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 { * In "show" mode, it will always be shown on top of the active workspace. */ char *hidden_state; + bool visible_by_modifier; // only relevant in "hide" mode /** * Id name used to identify the bar through IPC. * @@ -389,7 +390,6 @@ struct sway_config { bool show_marks; bool tiling_drag; - bool edge_gaps; bool smart_gaps; int gaps_inner; int gaps_outer; @@ -531,6 +531,8 @@ void free_sway_binding(struct sway_binding *sb); void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); +void load_swaybar(struct bar_config *bar); + void load_swaybars(void); void 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 { bool has_focus; struct wl_list focus_stack; // list of containers in focus order + struct sway_workspace *workspace; // If the focused layer is set, views cannot receive keyboard focus struct wlr_layer_surface_v1 *focused_layer; @@ -112,8 +113,16 @@ void seat_set_focus_container(struct sway_seat *seat, void seat_set_focus_workspace(struct sway_seat *seat, struct sway_workspace *ws); +/** + * Manipulate the focus stack without triggering any other behaviour. + * + * This can be used to set focus_inactive by calling the function a second time + * with the real focus. + */ +void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node); + void seat_set_focus_warp(struct sway_seat *seat, - struct sway_node *node, bool warp, bool notify); + struct sway_node *node, bool warp); void seat_set_focus_surface(struct sway_seat *seat, 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, struct sway_workspace *new, const char *change); void ipc_event_window(struct sway_container *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); +void ipc_event_bar_state_update(struct bar_config *bar); void ipc_event_mode(const char *mode, bool pango); void ipc_event_shutdown(const char *reason); void 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 @@ struct swaybar_config; struct swaybar_output; struct swaybar_workspace; +struct loop; struct swaybar_pointer { struct wl_pointer *pointer; @@ -37,7 +38,7 @@ enum hotspot_event_handling { }; struct swaybar_hotspot { - struct wl_list link; + struct wl_list link; // swaybar_output::hotspots int x, y, width, height; enum hotspot_event_handling (*callback)(struct swaybar_output *output, int x, int y, enum x11_button button, void *data); @@ -46,6 +47,15 @@ struct swaybar_hotspot { }; struct swaybar { + char *id; + char *mode; + bool mode_pango_markup; + + // only relevant when bar is in "hide" mode + bool visible_by_modifier; + bool visible_by_urgency; + bool visible; + struct wl_display *display; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; @@ -57,14 +67,16 @@ struct swaybar { struct swaybar_pointer pointer; struct status_line *status; + struct loop *eventloop; + int ipc_event_socketfd; int ipc_socketfd; - struct wl_list outputs; + struct wl_list outputs; // swaybar_output::link }; struct swaybar_output { - struct wl_list link; + struct wl_list link; // swaybar::outputs struct swaybar *bar; struct wl_output *output; struct zxdg_output_v1 *xdg_output; @@ -72,8 +84,8 @@ struct swaybar_output { struct zwlr_layer_surface_v1 *layer_surface; uint32_t wl_name; - struct wl_list workspaces; - struct wl_list hotspots; + struct wl_list workspaces; // swaybar_workspace::link + struct wl_list hotspots; // swaybar_hotspot::link char *name; bool focused; @@ -88,7 +100,7 @@ struct swaybar_output { }; struct swaybar_workspace { - struct wl_list link; + struct wl_list link; // swaybar_output::workspaces int num; char *name; bool focused; @@ -96,10 +108,24 @@ struct swaybar_workspace { bool urgent; }; -bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id); +bool bar_setup(struct swaybar *bar, const char *socket_path); void bar_run(struct swaybar *bar); void bar_teardown(struct swaybar *bar); +/* + * Determines whether the bar should be visible and changes it to be so. + * If the current visibility of the bar is the different to what it should be, + * then it adds or destroys the layer surface as required, + * as well as sending the cont or stop signal to the status command. + * If the current visibility of the bar is already what it should be, + * then this function is a no-op, unless moving_layer is true, which occurs + * when the bar changes from "hide" to "dock" mode or vice versa, and the bar + * needs to be destroyed and re-added in order to change its layer. + * + * Returns true if the bar is now visible, otherwise false. + */ +bool determine_bar_visibility(struct swaybar *bar, bool moving_layer); +void free_hotspots(struct wl_list *list); void free_workspaces(struct wl_list *list); #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 { }; struct config_output { - struct wl_list link; + struct wl_list link; // swaybar_config::outputs char *name; size_t index; }; @@ -31,7 +31,8 @@ struct swaybar_config { char *font; char *sep_symbol; char *mode; - bool mode_pango_markup; + char *hidden_state; + char *modifier; bool strip_workspace_numbers; bool binding_mode_indicator; 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 @@ -#ifndef _SWAYBAR_EVENT_LOOP_H -#define _SWAYBAR_EVENT_LOOP_H -#include <stdbool.h> -#include <time.h> - -void add_event(int fd, short mask, - void(*cb)(int fd, short mask, void *data), - void *data); - -// Not guaranteed to notify cb immediately -void add_timer(timer_t timer, - void(*cb)(timer_t timer, void *data), - void *data); - -// Returns false if nothing exists, true otherwise -bool remove_event(int fd); - -// Returns false if nothing exists, true otherwise -bool remove_timer(timer_t timer); - -// Blocks and returns after sending callbacks -void event_loop_poll(void); - -void init_event_loop(void); - -#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 @@ #include "status_line.h" struct i3bar_block { - struct wl_list link; + struct wl_list link; // status_link::blocks int ref_count; char *full_text, *short_text, *align; 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 @@ #include <stdbool.h> #include "swaybar/bar.h" -bool ipc_initialize(struct swaybar *bar, const char *bar_id); +bool ipc_initialize(struct swaybar *bar); bool handle_ipc_readable(struct swaybar *bar); -void ipc_get_workspaces(struct swaybar *bar); +bool ipc_get_workspaces(struct swaybar *bar); void ipc_send_workspace_command(struct swaybar *bar, const char *ws); void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind); 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 { }; struct status_line { + struct swaybar *bar; + pid_t pid; int read_fd, write_fd; FILE *read, *write; @@ -22,6 +24,9 @@ struct status_line { const char *text; struct wl_list blocks; // i3bar_block::link + int stop_signal; + int cont_signal; + bool click_events; bool clicked; 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 { }; struct swaylock_state { + struct loop *eventloop; + struct loop_timer *clear_indicator_timer; // clears the indicator + struct loop_timer *clear_password_timer; // clears the password buffer + struct loop_timer *verify_password_timer; struct wl_display *display; struct wl_compositor *compositor; 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() 'sway/sway.5.scd', 'sway/sway-bar.5.scd', 'sway/sway-input.5.scd', + 'sway/sway-output.5.scd', 'swaylock/swaylock.1.scd', 'swaymsg/swaymsg.1.scd', 'swayidle/swayidle.1.scd', @@ -113,7 +114,7 @@ if scdoc.found() endforeach endif -add_project_arguments('-DSYSCONFDIR="/@0@/@1@"'.format(prefix, sysconfdir), language : 'c') +add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c') version = get_option('sway-version') if version != '' @@ -156,7 +157,7 @@ subdir('swaynag') subdir('swaylock') config = configuration_data() -config.set('sysconfdir', join_paths(prefix, sysconfdir)) +config.set('sysconfdir', sysconfdir) config.set('datadir', join_paths(prefix, datadir)) config.set('prefix', prefix) 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[] = { { "height", bar_cmd_height }, { "hidden_state", bar_cmd_hidden_state }, { "icon_theme", bar_cmd_icon_theme }, - { "id", bar_cmd_id }, { "mode", bar_cmd_mode }, { "modifier", bar_cmd_modifier }, { "output", bar_cmd_output }, @@ -27,7 +26,6 @@ static struct cmd_handler bar_handlers[] = { { "separator_symbol", bar_cmd_separator_symbol }, { "status_command", bar_cmd_status_command }, { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, - { "swaybar_command", bar_cmd_swaybar_command }, { "tray_output", bar_cmd_tray_output }, { "tray_padding", bar_cmd_tray_padding }, { "workspace_buttons", bar_cmd_workspace_buttons }, @@ -36,54 +34,49 @@ static struct cmd_handler bar_handlers[] = { // Must be in alphabetical order for bsearch static struct cmd_handler bar_config_handlers[] = { - { "hidden_state", bar_cmd_hidden_state }, - { "mode", bar_cmd_mode } + { "id", bar_cmd_id }, + { "swaybar_command", bar_cmd_swaybar_command }, }; +// Determines whether the subcommand is valid in any bar handler struct +static bool is_subcommand(char *name) { + return find_handler(name, bar_handlers, sizeof(bar_handlers)) || + find_handler(name, bar_config_handlers, sizeof(bar_config_handlers)); +} + struct cmd_results *cmd_bar(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) { + if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 2))) { return error; } - if (find_handler(argv[0], bar_config_handlers, - sizeof(bar_config_handlers))) { - if (config->reading) { - return config_subcommand(argv, argc, bar_config_handlers, - sizeof(bar_config_handlers)); - } - return cmd_results_new(CMD_FAILURE, "bar", - "Can only be used in config file."); - } - - if (argc > 1) { - struct bar_config *bar = NULL; - if (!find_handler(argv[0], bar_handlers, sizeof(bar_handlers)) - && find_handler(argv[1], bar_handlers, sizeof(bar_handlers))) { - for (int i = 0; i < config->bars->length; ++i) { - struct bar_config *item = config->bars->items[i]; - if (strcmp(item->id, argv[0]) == 0) { - wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]); - bar = item; - break; - } + bool spawn = false; + struct bar_config *bar = NULL; + if (strcmp(argv[0], "id") != 0 && is_subcommand(argv[1])) { + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *item = config->bars->items[i]; + if (strcmp(item->id, argv[0]) == 0) { + wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]); + bar = item; + break; } + } + if (!bar) { + spawn = !config->reading; + wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]); + bar = default_bar_config(); if (!bar) { - wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]); - bar = default_bar_config(); - if (!bar) { - return cmd_results_new(CMD_FAILURE, "bar", - "Unable to allocate bar state"); - } - - bar->id = strdup(argv[0]); + return cmd_results_new(CMD_FAILURE, "bar", + "Unable to allocate bar state"); } - config->current_bar = bar; - ++argv; --argc; + + bar->id = strdup(argv[0]); } + config->current_bar = bar; + ++argv; --argc; } - if (!config->current_bar) { + if (!config->current_bar && config->reading) { // Create new bar with default values struct bar_config *bar = default_bar_config(); if (!bar) { @@ -92,18 +85,13 @@ struct cmd_results *cmd_bar(int argc, char **argv) { } // set bar id - for (int i = 0; i < config->bars->length; ++i) { - if (bar == config->bars->items[i]) { - const int len = 5 + numlen(i); // "bar-" + i + \0 - bar->id = malloc(len * sizeof(char)); - if (bar->id) { - snprintf(bar->id, len, "bar-%d", i); - } else { - return cmd_results_new(CMD_FAILURE, - "bar", "Unable to allocate bar ID"); - } - break; - } + const int len = 5 + numlen(config->bars->length - 1); // "bar-"+i+\0 + bar->id = malloc(len * sizeof(char)); + if (bar->id) { + snprintf(bar->id, len, "bar-%d", config->bars->length - 1); + } else { + return cmd_results_new(CMD_FAILURE, + "bar", "Unable to allocate bar ID"); } // Set current bar @@ -111,5 +99,32 @@ struct cmd_results *cmd_bar(int argc, char **argv) { wlr_log(WLR_DEBUG, "Creating bar %s", bar->id); } - return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); + if (find_handler(argv[0], bar_config_handlers, + sizeof(bar_config_handlers))) { + if (config->reading) { + return config_subcommand(argv, argc, bar_config_handlers, + sizeof(bar_config_handlers)); + } else if (spawn) { + for (int i = config->bars->length - 1; i >= 0; i--) { + struct bar_config *bar = config->bars->items[i]; + if (bar == config->current_bar) { + list_del(config->bars, i); + free_bar_config(bar); + break; + } + } + } + return cmd_results_new(CMD_INVALID, "bar", + "Can only be used in the config file."); + } + + struct cmd_results *res = + config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); + if (!config->reading) { + if (spawn) { + load_swaybar(config->current_bar); + } + config->current_bar = NULL; + } + return res; } 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, } // free old mode free(old_state); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return NULL; } struct 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) { const char *state = argv[0]; if (config->reading) { - return bar_set_hidden_state(config->current_bar, state); - } - - const char *id = NULL; - if (argc == 2) { - id = argv[1]; - } - struct bar_config *bar; - for (int i = 0; i < config->bars->length; ++i) { - bar = config->bars->items[i]; - if (id && strcmp(id, bar->id) == 0) { - return bar_set_hidden_state(bar, state); - } - - error = bar_set_hidden_state(bar, state); - if (error) { - return error; + error = bar_set_hidden_state(config->current_bar, state); + } else { + const char *id = argc == 2 ? argv[1] : NULL; + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + if (id) { + if (strcmp(id, bar->id) == 0) { + error = bar_set_hidden_state(bar, state); + break; + } + } else if ((error = bar_set_hidden_state(bar, state))) { + break; + } } } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); } 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) { const char *oldname = config->current_bar->id; if (strcmp(name, oldname) == 0) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); // NOP + } else if (strcmp(name, "id") == 0) { + return cmd_results_new(CMD_INVALID, "id", "id cannot be 'id'"); } // check if id is used by a previously defined bar 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 // free old mode free(old_mode); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return NULL; } struct cmd_results *bar_cmd_mode(int argc, char **argv) { @@ -51,24 +51,20 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { const char *mode = argv[0]; if (config->reading) { - return bar_set_mode(config->current_bar, mode); - } - - const char *id = NULL; - if (argc == 2) { - id = argv[1]; - } - - struct bar_config *bar; - for (int i = 0; i < config->bars->length; ++i) { - bar = config->bars->items[i]; - if (id && strcmp(id, bar->id) == 0) { - return bar_set_mode(bar, mode); - } - error = bar_set_mode(bar, mode); - if (error) { - return error; + error = bar_set_mode(config->current_bar, mode); + } else { + const char *id = argc == 2 ? argv[1] : NULL; + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + if (id) { + if (strcmp(id, bar->id) == 0) { + error = bar_set_mode(bar, mode); + break; + } + } else if ((error = bar_set_mode(bar, mode))) { + break; + } } } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); } 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) { } if (config->active && !config->validating) { - load_swaybars(); + load_swaybar(config->current_bar); } 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) { border_toggle(view); } else { return cmd_results_new(CMD_INVALID, "border", - "Expected 'border <none|normal|pixel|toggle>' " + "Expected 'border <none|normal|pixel|csd|toggle>' " "or 'border pixel <px>'"); } 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 { int amount; }; -// gaps edge_gaps on|off|toggle -static struct cmd_results *gaps_edge_gaps(int argc, char **argv) { - struct cmd_results *error; - if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { - return error; - } - - if (strcmp(argv[1], "on") == 0) { - config->edge_gaps = true; - } else if (strcmp(argv[1], "off") == 0) { - config->edge_gaps = false; - } else if (strcmp(argv[1], "toggle") == 0) { - if (!config->active) { - return cmd_results_new(CMD_INVALID, "gaps", - "Cannot toggle gaps while not running."); - } - config->edge_gaps = !config->edge_gaps; - } else { - return cmd_results_new(CMD_INVALID, "gaps", - "gaps edge_gaps on|off|toggle"); - } - arrange_root(); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); -} - // gaps inner|outer <px> static struct cmd_results *gaps_set_defaults(int argc, char **argv) { 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) { return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps inner|outer <px>'"); } - if (amount < 0) { - amount = 0; - } - if (inner) { - config->gaps_inner = amount; + config->gaps_inner = (amount >= 0) ? amount : 0; } else { config->gaps_outer = amount; } + + // Prevent negative outer gaps from moving windows out of the workspace. + if (config->gaps_outer < -config->gaps_inner) { + config->gaps_outer = -config->gaps_inner; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -95,8 +72,12 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) { *prop -= data->amount; break; } - if (*prop < 0) { - *prop = 0; + // Prevent invalid gaps configurations. + if (ws->gaps_inner < 0) { + ws->gaps_inner = 0; + } + if (ws->gaps_outer < -ws->gaps_inner) { + ws->gaps_outer = -ws->gaps_inner; } arrange_workspace(ws); } @@ -156,7 +137,6 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -// gaps edge_gaps on|off|toggle // gaps inner|outer <px> - sets defaults for workspaces // gaps inner|outer current|all set|plus|minus <px> - runtime only struct cmd_results *cmd_gaps(int argc, char **argv) { @@ -165,10 +145,6 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return error; } - if (strcmp(argv[0], "edge_gaps") == 0) { - return gaps_edge_gaps(argc, argv); - } - if (argc == 2) { return gaps_set_defaults(argc, argv); } 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) { if (new_output_last_ws && new_output_last_ws != new_workspace) { struct sway_node *new_output_last_focus = seat_get_focus_inactive(seat, &new_output_last_ws->node); - seat_set_focus_warp(seat, new_output_last_focus, false, false); + seat_set_raw_focus(seat, new_output_last_focus); } // restore focus @@ -556,7 +556,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { focus = seat_get_focus_inactive(seat, &old_ws->node); } } - seat_set_focus_warp(seat, focus, true, false); + seat_set_focus(seat, focus); // clean-up, destroying parents if the container was the last child if (old_parent) { @@ -593,7 +593,7 @@ static void workspace_move_to_output(struct sway_workspace *workspace, char *ws_name = workspace_next_name(old_output->wlr_output->name); struct sway_workspace *ws = workspace_create(old_output, ws_name); free(ws_name); - seat_set_focus_workspace(seat, ws); + seat_set_raw_focus(seat, &ws->node); } workspace_consider_destroy(new_output_old_ws); @@ -704,7 +704,7 @@ static struct cmd_results *cmd_move_in_direction( } // Hack to re-focus container - seat_set_focus_workspace(config->handler_context.seat, new_ws); + seat_set_raw_focus(config->handler_context.seat, &new_ws->node); seat_set_focus_container(config->handler_context.seat, container); 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) { return error; } struct sway_container *container = config->handler_context.container; + + if (container == NULL) { + return cmd_results_new(CMD_FAILURE, "sticky", "No current container"); + }; + if (!container_is_floating(container)) { return cmd_results_new(CMD_FAILURE, "sticky", "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, temp->width = con1->width; temp->height = con1->height; temp->parent = con1->parent; + temp->workspace = con1->workspace; con1->x = con2->x; con1->y = con2->y; @@ -34,8 +35,18 @@ static void swap_places(struct sway_container *con1, con2->height = temp->height; int temp_index = container_sibling_index(con1); - container_insert_child(con2->parent, con1, container_sibling_index(con2)); - container_insert_child(temp->parent, con2, temp_index); + if (con2->parent) { + container_insert_child(con2->parent, con1, + container_sibling_index(con2)); + } else { + workspace_insert_tiling(con2->workspace, con1, + container_sibling_index(con2)); + } + if (temp->parent) { + container_insert_child(temp->parent, con2, temp_index); + } else { + workspace_insert_tiling(temp->workspace, con2, temp_index); + } free(temp); } @@ -50,13 +61,13 @@ static void swap_focus(struct sway_container *con1, enum sway_container_layout layout2 = container_parent_layout(con2); if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { if (workspace_is_visible(ws2)) { - seat_set_focus_warp(seat, &con2->node, false, true); + seat_set_focus_warp(seat, &con2->node, false); } seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); } else if (focus == con2 && (layout1 == L_TABBED || layout1 == L_STACKED)) { if (workspace_is_visible(ws1)) { - seat_set_focus_warp(seat, &con1->node, false, true); + seat_set_focus_warp(seat, &con1->node, false); } seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); } 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 @@ #define _XOPEN_SOURCE 500 #include <ctype.h> +#include <limits.h> #include <string.h> #include <strings.h> #include "sway/commands.h" @@ -20,8 +21,8 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) { return NULL; } wsc->workspace = strdup(ws_name); - wsc->gaps_inner = -1; - wsc->gaps_outer = -1; + wsc->gaps_inner = INT_MIN; + wsc->gaps_outer = INT_MIN; list_add(config->workspace_configs, wsc); return wsc; } @@ -94,7 +95,16 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "workspace gaps", "Expected 'workspace <ws> gaps inner|outer <px>'"); } - *prop = val >= 0 ? val : 0; + *prop = val; + + // Prevent invalid gaps configurations. + if (wsc->gaps_inner < 0) { + wsc->gaps_inner = 0; + } + if (wsc->gaps_outer < -wsc->gaps_inner) { + wsc->gaps_outer = -wsc->gaps_inner; + } + } else { if (config->reading || !config->active) { return cmd_results_new(CMD_DEFER, "workspace", NULL); @@ -132,7 +142,11 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { strcasecmp(argv[0], "current") == 0) { ws = workspace_by_name(argv[0]); } else if (strcasecmp(argv[0], "back_and_forth") == 0) { - if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) { + if (!prev_workspace_name) { + return cmd_results_new(CMD_INVALID, "workspace", + "There is no previous workspace"); + } + if (!(ws = workspace_by_name(argv[0]))) { ws = workspace_create(NULL, prev_workspace_name); } } 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) { config->show_marks = true; config->tiling_drag = true; - config->edge_gaps = true; config->smart_gaps = false; config->gaps_inner = 0; 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 @@ #include "stringop.h" #include "list.h" #include "log.h" +#include "util.h" static void terminate_swaybar(pid_t pid) { wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid); @@ -101,6 +102,7 @@ struct bar_config *default_bar_config(void) { bar->binding_mode_indicator = true; bar->verbose = false; bar->pid = 0; + bar->modifier = get_modifier_mask_by_name("Mod4"); if (!(bar->mode = strdup("dock"))) { goto cleanup; } @@ -226,13 +228,17 @@ static void invoke_swaybar(struct bar_config *bar) { close(filedes[1]); } +void load_swaybar(struct bar_config *bar) { + if (bar->pid != 0) { + terminate_swaybar(bar->pid); + } + wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); + invoke_swaybar(bar); +} + void load_swaybars(void) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; - if (bar->pid != 0) { - terminate_swaybar(bar->pid); - } - wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); - invoke_swaybar(bar); + load_swaybar(bar); } } 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, } } +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + static void scale_box(struct wlr_box *box, float scale) { - box->x *= scale; - box->y *= scale; - box->width *= scale; - box->height *= scale; + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); } struct 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 { float alpha; }; +/** + * Apply scale to a width or height. + * + * One does not simply multiply the width by the scale. We allow fractional + * scaling, which means the resulting scaled width might be a decimal. + * So we round it. + * + * But even this can produce undesirable results depending on the X or Y offset + * of the box. For example, with a scale of 1.5, a box with width=1 should not + * scale to 2px if its X coordinate is 1, because the X coordinate would have + * scaled to 2px. + */ +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + static void scale_box(struct wlr_box *box, float scale) { - box->x *= scale; - box->y *= scale; - box->width *= scale; - box->height *= scale; + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); } static void scissor_output(struct wlr_output *wlr_output, @@ -392,14 +408,23 @@ static void render_titlebar(struct sway_output *output, render_rect(output->wlr_output, output_damage, &box, color); // Single pixel right edge - box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; + box.x = x + width - TITLEBAR_BORDER_THICKNESS; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = TITLEBAR_BORDER_THICKNESS; + box.height = + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; + scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } size_t inner_width = width - TITLEBAR_H_PADDING * 2; + int bg_y = y + TITLEBAR_BORDER_THICKNESS; + int ob_bg_height = scale_length( + (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + + config->font_height, bg_y, output_scale); // Marks - size_t marks_ob_width = 0; // output-buffer-local + int marks_ob_width = 0; // output-buffer-local if (config->show_marks && marks_texture) { struct wlr_box texture_box; wlr_texture_get_size(marks_texture, @@ -408,15 +433,14 @@ static void render_titlebar(struct sway_output *output, // The marks texture might be shorter than the config->font_height, in // which case we need to pad it as evenly as possible above and below. - int ob_padding_total = config->font_height * output_scale - - texture_box.height; - int ob_padding_above = floor(ob_padding_total / 2); - int ob_padding_below = ceil(ob_padding_total / 2); + int ob_padding_total = ob_bg_height - texture_box.height; + int ob_padding_above = floor(ob_padding_total / 2.0); + int ob_padding_below = ceil(ob_padding_total / 2.0); // Render texture - texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) - * output_scale - texture_box.width; - texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale + + texture_box.x = round((x - output_x + width - TITLEBAR_H_PADDING) + * output_scale) - texture_box.width; + texture_box.y = round((bg_y - output_y) * output_scale) + ob_padding_above; float matrix[9]; @@ -431,29 +455,18 @@ static void render_titlebar(struct sway_output *output, &texture_box, matrix, con->alpha); // Padding above - if (ob_padding_above > 0) { - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = (x + width - TITLEBAR_H_PADDING) * output_scale - - texture_box.width; - box.y = (y + TITLEBAR_V_PADDING) * output_scale; - box.width = texture_box.width; - box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); - } + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x + round(output_x * output_scale); + box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale); + box.width = texture_box.width; + box.height = ob_padding_above; + render_rect(output->wlr_output, output_damage, &box, color); // Padding below - if (ob_padding_below > 0) { - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = (x + width - TITLEBAR_H_PADDING) * output_scale - - texture_box.width; - box.y = (y + TITLEBAR_V_PADDING) * output_scale + - ob_padding_above + texture_box.height; - box.width = texture_box.width; - box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); - } + box.y += ob_padding_above + texture_box.height; + box.height = ob_padding_below; + render_rect(output->wlr_output, output_damage, &box, color); } // Title text @@ -466,89 +479,73 @@ static void render_titlebar(struct sway_output *output, // The title texture might be shorter than the config->font_height, // in which case we need to pad it above and below. - int ob_padding_above = (config->font_baseline - con->title_baseline) - * output_scale; - int ob_padding_below = (config->font_height - con->title_height) - * output_scale - ob_padding_above; + int ob_padding_above = round((config->font_baseline - + con->title_baseline + TITLEBAR_V_PADDING - + TITLEBAR_BORDER_THICKNESS) * output_scale); + int ob_padding_below = ob_bg_height - ob_padding_above - + texture_box.height; // Render texture - texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; - texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale + - ob_padding_above; + texture_box.x = + round((x - output_x + TITLEBAR_H_PADDING) * output_scale); + texture_box.y = + round((bg_y - output_y) * output_scale) + ob_padding_above; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, WL_OUTPUT_TRANSFORM_NORMAL, 0.0, output->wlr_output->transform_matrix); - if (inner_width * output_scale - marks_ob_width < texture_box.width) { - texture_box.width = inner_width * output_scale - marks_ob_width; + int inner_x = x - output_x + TITLEBAR_H_PADDING; + int ob_inner_width = scale_length(inner_width, inner_x, output_scale); + if (ob_inner_width - marks_ob_width < texture_box.width) { + texture_box.width = ob_inner_width - marks_ob_width; } render_texture(output->wlr_output, output_damage, title_texture, &texture_box, matrix, con->alpha); // Padding above - if (ob_padding_above > 0) { - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = (x + TITLEBAR_H_PADDING) * output_scale; - box.y = (y + TITLEBAR_V_PADDING) * output_scale; - box.width = texture_box.width; - box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); - } + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x + round(output_x * output_scale); + box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale); + box.width = texture_box.width; + box.height = ob_padding_above; + render_rect(output->wlr_output, output_damage, &box, color); // Padding below - if (ob_padding_below > 0) { - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = (x + TITLEBAR_H_PADDING) * output_scale; - box.y = (y + TITLEBAR_V_PADDING) * output_scale + - ob_padding_above + texture_box.height; - box.width = texture_box.width; - box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); - } + box.y += ob_padding_above + texture_box.height; + box.height = ob_padding_below; + render_rect(output->wlr_output, output_damage, &box, color); } - // Padding above title - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.y = y + TITLEBAR_BORDER_THICKNESS; - box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; - box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Padding below title - box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - // Filler between title and marks - box.width = inner_width * output_scale - title_ob_width - marks_ob_width; + box.width = + round(inner_width * output_scale) - title_ob_width - marks_ob_width; if (box.width > 0) { - box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; - box.y = (y + TITLEBAR_V_PADDING) * output_scale; - box.height = config->font_height * output_scale; + box.x = round((x + TITLEBAR_H_PADDING) * output_scale) + title_ob_width; + box.y = round(bg_y * output_scale); + box.height = ob_bg_height; render_rect(output->wlr_output, output_damage, &box, color); } // Padding left of title left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; box.x = x + left_offset; - box.y = y + TITLEBAR_V_PADDING; + box.y = y + TITLEBAR_BORDER_THICKNESS; box.width = TITLEBAR_H_PADDING - left_offset; - box.height = config->font_height; + box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + + config->font_height; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Padding right of marks right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; box.x = x + width - TITLEBAR_H_PADDING; - box.y = y + TITLEBAR_V_PADDING; + box.y = y + TITLEBAR_BORDER_THICKNESS; box.width = TITLEBAR_H_PADDING - right_offset; - box.height = config->font_height; + box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + + config->font_height; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); 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, struct sway_output *focused_output = node_get_output(focus); struct sway_output *output = node_get_output(node); if (output != focused_output) { - seat_set_focus_warp(seat, node, false, true); + seat_set_focus_warp(seat, node, false); } } else if (node->type == N_CONTAINER && node->sway_container->view) { // Focus node if the following are true: @@ -607,14 +607,14 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && node != prev_node && view_is_visible(node->sway_container->view)) { - seat_set_focus_warp(seat, node, false, true); + seat_set_focus_warp(seat, node, false); } else { struct sway_node *next_focus = seat_get_focus_inactive(seat, &root->node); if (next_focus && next_focus->type == N_CONTAINER && next_focus->sway_container->view && view_is_visible(next_focus->sway_container->view)) { - seat_set_focus_warp(seat, next_focus, false, true); + seat_set_focus_warp(seat, next_focus, false); } } } 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 @@ #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" +#include "sway/ipc-server.h" #include "log.h" /** @@ -66,10 +67,10 @@ static void update_shortcut_state(struct sway_shortcut_state *state, bool last_key_was_a_modifier = raw_modifiers != state->last_raw_modifiers; state->last_raw_modifiers = raw_modifiers; - if (last_key_was_a_modifier && state->last_keycode) { - // Last pressed key before this one was a modifier - state_erase_key(state, state->last_keycode); - } + if (last_key_was_a_modifier && state->last_keycode) { + // Last pressed key before this one was a modifier + state_erase_key(state, state->last_keycode); + } if (event->state == WLR_KEY_PRESSED) { // Add current key to set; there may be duplicates @@ -235,7 +236,6 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { code_modifiers); } - bool handled = false; // Identify active release binding @@ -337,6 +337,19 @@ static int handle_keyboard_repeat(void *data) { return 0; } +static void determine_bar_visibility(uint32_t modifiers) { + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + if (strcmp(bar->mode, bar->hidden_state) == 0) { // both are "hide" + bool should_be_visible = (~modifiers & bar->modifier) == 0; + if (bar->visible_by_modifier != should_be_visible) { + bar->visible_by_modifier = should_be_visible; + ipc_event_bar_state_update(bar); + } + } + } +} + static void handle_keyboard_modifiers(struct wl_listener *listener, void *data) { struct sway_keyboard *keyboard = @@ -346,6 +359,9 @@ static void handle_keyboard_modifiers(struct wl_listener *listener, keyboard->seat_device->input_device->wlr_device; wlr_seat_set_keyboard(wlr_seat, wlr_device); wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers); + + uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); + determine_bar_visibility(modifiers); } struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, @@ -464,7 +480,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { keyboard->keyboard_key.notify = handle_keyboard_key; wl_list_remove(&keyboard->keyboard_modifiers.link); - wl_signal_add( &wlr_device->keyboard->events.modifiers, + wl_signal_add(&wlr_device->keyboard->events.modifiers, &keyboard->keyboard_modifiers); keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; } 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) { seat_set_focus(seat, next_focus); } else { // Setting focus_inactive - seat_set_focus_warp(seat, next_focus, false, false); - seat_set_focus_warp(seat, focus, false, false); + seat_set_raw_focus(seat, next_focus); + seat_set_raw_focus(seat, focus); } } @@ -623,8 +623,25 @@ static void container_raise_floating(struct sway_container *con) { } } +static void set_workspace(struct sway_seat *seat, + struct sway_workspace *new_ws) { + if (seat->workspace == new_ws) { + return; + } + ipc_event_workspace(seat->workspace, new_ws, "focus"); + seat->workspace = new_ws; +} + +void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { + struct sway_seat_node *seat_node = seat_node_from_node(seat, node); + wl_list_remove(&seat_node->link); + wl_list_insert(&seat->focus_stack, &seat_node->link); + node_set_dirty(node); + node_set_dirty(node_get_parent(node)); +} + void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, - bool warp, bool notify) { + bool warp) { if (seat->focused_layer) { return; } @@ -688,34 +705,20 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, if (container) { struct sway_container *parent = container->parent; while (parent) { - struct sway_seat_node *seat_node = - seat_node_from_node(seat, &parent->node); - wl_list_remove(&seat_node->link); - wl_list_insert(&seat->focus_stack, &seat_node->link); - node_set_dirty(&parent->node); + seat_set_raw_focus(seat, &parent->node); parent = parent->parent; } } if (new_workspace) { - struct sway_seat_node *seat_node = - seat_node_from_node(seat, &new_workspace->node); - wl_list_remove(&seat_node->link); - wl_list_insert(&seat->focus_stack, &seat_node->link); - node_set_dirty(&new_workspace->node); + seat_set_raw_focus(seat, &new_workspace->node); } if (container) { - struct sway_seat_node *seat_node = - seat_node_from_node(seat, &container->node); - wl_list_remove(&seat_node->link); - wl_list_insert(&seat->focus_stack, &seat_node->link); - node_set_dirty(&container->node); + seat_set_raw_focus(seat, &container->node); seat_send_focus(&container->node, seat); } // emit ipc events - if (notify && new_workspace && last_workspace != new_workspace) { - ipc_event_workspace(last_workspace, new_workspace, "focus"); - } + set_workspace(seat, new_workspace); if (container && container->view) { ipc_event_window(container, "focus"); } @@ -798,17 +801,17 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, } void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { - seat_set_focus_warp(seat, node, true, true); + seat_set_focus_warp(seat, node, true); } void seat_set_focus_container(struct sway_seat *seat, struct sway_container *con) { - seat_set_focus_warp(seat, con ? &con->node : NULL, true, true); + seat_set_focus_warp(seat, con ? &con->node : NULL, true); } void seat_set_focus_workspace(struct sway_seat *seat, struct sway_workspace *ws) { - seat_set_focus_warp(seat, ws ? &ws->node : NULL, true, true); + seat_set_focus_warp(seat, ws ? &ws->node : NULL, true); } void 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) { json_object_put(json); } +void ipc_event_bar_state_update(struct bar_config *bar) { + if (!ipc_has_event_listeners(IPC_EVENT_BAR_STATE_UPDATE)) { + return; + } + wlr_log(WLR_DEBUG, "Sending bar_state_update event"); + + json_object *json = json_object_new_object(); + json_object_object_add(json, "id", json_object_new_string(bar->id)); + json_object_object_add(json, "visible_by_modifier", + json_object_new_boolean(bar->visible_by_modifier)); + + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_BAR_STATE_UPDATE); + json_object_put(json); +} + void ipc_event_mode(const char *mode, bool pango) { if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { return; @@ -651,6 +667,8 @@ void ipc_client_handle_command(struct ipc_client *client) { client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); } else if (strcmp(event_type, "barconfig_update") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); + } else if (strcmp(event_type, "bar_state_update") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_BAR_STATE_UPDATE); } else if (strcmp(event_type, "mode") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_MODE); } 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) { } } - // TODO: switch logging over to wlroots? if (debug) { wlr_log_init(WLR_DEBUG, NULL); } 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. is given, when mouse button _n_ has been released). To disable the default behavior for a button, use the command _nop_. +*mode* dock|hide|invisible + Specifies the visibility of the bar. In _dock_ mode, it is permanently + visible at one edge of the screen. In _hide_ mode, it is hidden unless the + modifier key is pressed, though this behaviour depends on the hidden state. + In _invisible_ mode, it is permanently hidden. Default is _dock_. + +*hidden\_state* hide|show + Specifies the behaviour of the bar when it is in _hide_ mode. When the + hidden state is _hide_, then it is normally hidden, and only unhidden by + pressing the modifier key or in case of urgency hints. When the hidden + state is _show_, then it is permanently visible, drawn on top of the + currently visible workspace. Default is _hide_. + +*modifier* <Modifier>|none + Specifies the modifier key that shows a hidden bar. Default is _Mod4_. + ## TRAY Swaybar 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"). # SEE ALSO -*sway*(5) +*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 @@ +sway-output(5) + +# NAME + +sway-output - output configuration commands for sway + +# DESCRIPTION + +You may combine output commands into one, like so: + + output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch + +You can get a list of output names with *swaymsg -t get\_outputs*. You may also +match any output by using the output name "\*". + +# COMMANDS + +*output* <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]] + Configures the specified output to use the given mode. Modes are a + combination of width and height (in pixels) and a refresh rate that your + display can be configured to use. For a list of available modes for each + output, use *swaymsg -t get\_outputs*. + + Examples: + + output HDMI-A-1 mode 1920x1080 + + output HDMI-A-1 mode 1920x1080@60Hz + +*output* <name> position|pos <X> <Y> + Places the specified output at the specific position in the global + coordinate space. + +*output* <name> scale <factor> + Scales the specified output by the specified scale _factor_. An integer is + recommended, but fractional values are also supported. If a fractional + value are specified, be warned that it is not possible to faithfully + represent the contents of your windows - they will be rendered at the next + highest integral scale factor and downscaled. You may be better served by + setting an integral scale factor and adjusting the font size of your + applications to taste. + +*output* <name> background|bg <file> <mode> [<fallback\_color>] + Sets the wallpaper for the given output to the specified file, using the + given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If + the specified file cannot be accessed or if the image does fill the entire + output, a fallback color may be provided to cover the rest of the output. + __fallback\_color__ should be specified as _#RRGGBB_. Alpha is not + supported. + +*output* <name> background|bg <color> solid\_color + Sets the background of the given output to the specified color. _color_ + should be specified as _#RRGGBB_. Alpha is not supported. + +*output* <name> transform <transform> + Sets the background transform to the given value. Can be one of "90", "180", + "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270" + to apply a rotation and flip, or "normal" to apply no transform. + +*output* <name> disable|enable + Enables or disables the specified output (all outputs are enabled by + default). + +# SEE ALSO + +*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 # SEE ALSO -*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) +*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. The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). -*border* normal|pixel [<n>] +*border* none|normal|csd|pixel [<n>] Set border style for focused window. _normal_ includes a border of thickness _n_ and a title bar. _pixel_ is a border without title bar _n_ - pixels thick. Default is _normal_ with border thickness 2. + pixels thick. Default is _normal_ with border thickness 2. _csd_ is short + for client-side-decorations, which allows the client to draw its own + decorations. -*border* none|toggle - Set border style for focused window to _none_ or _toggle_ between the - available border styles: _normal_, _pixel_, _none_. +*border* toggle + Cycles through the available border styles. *exit* Exit sway and end your Wayland session. @@ -420,15 +421,11 @@ The default colors are: _focus\_wrapping force_. This is only available for convenience. Please use _focus\_wrapping_ instead when possible. -*gaps* edge\_gaps on|off|toggle - When _on_, gaps will be added between windows and workspace edges if the - inner gap is nonzero. When _off_, gaps will only be added between views. - _toggle_ cannot be used in the configuration file. - *gaps* inner|outer <amount> Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner affects spacing around each view and outer affects the spacing around each - workspace. Outer gaps are in addition to inner gaps. + workspace. Outer gaps are in addition to inner gaps. To reduce or remove + outer gaps, outer gaps can be set to a negative value. This affects new workspaces only, and is used when the workspace doesn't have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>). @@ -501,61 +498,6 @@ The default colors are: Prevents windows matching <criteria> from being focused automatically when they're created. This has no effect on the first window in a workspace. -*output* <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]] - Configures the specified output to use the given mode. Modes are a - combination of width and height (in pixels) and a refresh rate that your - display can be configured to use. For a list of available modes for each - output, use *swaymsg -t get\_outputs*. - - Examples: - - output HDMI-A-1 mode 1920x1080 - - output HDMI-A-1 mode 1920x1080@60Hz - -*output* <name> position|pos <X> <Y> - Places the specified output at the specific position in the global - coordinate space. - -*output* <name> scale <factor> - Scales the specified output by the specified scale _factor_. An integer is - recommended, but fractional values are also supported. If a fractional - value are specified, be warned that it is not possible to faithfully - represent the contents of your windows - they will be rendered at the next - highest integral scale factor and downscaled. You may be better served by - setting an integral scale factor and adjusting the font size of your - applications to taste. - -*output* <name> background|bg <file> <mode> [<fallback\_color>] - Sets the wallpaper for the given output to the specified file, using the - given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If - the specified file cannot be accessed or if the image does fill the entire - output, a fallback color may be provided to cover the rest of the output. - __fallback\_color__ should be specified as _#RRGGBB_. Alpha is not - supported. - -**output** <name> background|bg <color> solid\_color - Sets the background of the given output to the specified color. _color_ - should be specified as _#RRGGBB_. Alpha is not supported. - -**output** <name> transform <transform> - Sets the background transform to the given value. Can be one of "90", "180", - "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270" - to apply a rotation and flip, or "normal" to apply no transform. - -*output* <name> disable|enable - Enables or disables the specified output (all outputs are enabled by - default). - -*NOTES FOR THE OUTPUT COMMANDS* - -You may combine output commands into one, like so: - - output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch - -You can get a list of output names with *swaymsg -t get\_outputs*. You may also -match any output by using the output name "\*". - *popup\_during\_fullscreen* smart|ignore|leave\_fullscreen Determines what to do when a fullscreen view opens a dialog. If _smart_ (the default), the dialog will be displayed. If _ignore_, the @@ -618,6 +560,18 @@ match any output by using the output name "\*". *workspace\_layout* default|stacking|tabbed Specifies the initial layout for new workspaces. +# BAR CONTROL + +*bar hidden\_state* hide|show|toggle [<bar\_id>] + Sets the hidden state of the bar (see *sway-bar*(5)), either individually, + by specifying a bar id, or if none is given, for all bar instances. + _toggle_ switches between _hide_ and _show_. + +*bar mode* dock|hide|invisible|toggle [<bar\_id>] + Sets the mode of the bar (see *sway-bar*(5)), either individually, + by specifying a bar id, or if none is given, for all bar instances. + _toggle_ switches between _dock_ and _hide_. + # CRITERIA A 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) { if (config->reloading) { return; } + if (!workspace->output) { + // Happens when there are no outputs connected + return; + } struct sway_output *output = workspace->output; struct wlr_box *area = &output->usable_area; 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, container_add_child(cont, child); if (set_focus) { - seat_set_focus_container(seat, cont); - seat_set_focus_container(seat, child); + seat_set_raw_focus(seat, &cont->node); + seat_set_raw_focus(seat, &child->node); } 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) { } // Use the focused workspace - return seat_get_focused_workspace(seat); + struct sway_node *node = seat_get_focus_inactive(seat, &root->node); + if (node && node->type == N_WORKSPACE) { + return node->sway_workspace; + } else if (node && node->type == N_CONTAINER) { + return node->sway_container->workspace; + } + + // If there's no focus_inactive workspace then we must be running without + // any outputs connected + return root->saved_workspaces->items[0]; } static 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, if (name) { struct workspace_config *wsc = workspace_find_config(name); if (wsc) { - if (wsc->gaps_outer != -1) { + if (wsc->gaps_outer != INT_MIN) { ws->gaps_outer = wsc->gaps_outer; } - if (wsc->gaps_inner != -1) { + if (wsc->gaps_inner != INT_MIN) { ws->gaps_inner = wsc->gaps_inner; } } @@ -618,9 +618,6 @@ void workspace_add_gaps(struct sway_workspace *ws) { if (ws->current_gaps > 0) { return; } - if (!config->edge_gaps) { - return; - } if (config->smart_gaps) { struct sway_seat *seat = input_manager_get_default_seat(input_manager); 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 @@ #endif #include "swaybar/bar.h" #include "swaybar/config.h" -#include "swaybar/event_loop.h" #include "swaybar/i3bar.h" #include "swaybar/ipc.h" #include "swaybar/status_line.h" @@ -26,15 +25,28 @@ #include "ipc-client.h" #include "list.h" #include "log.h" +#include "loop.h" #include "pool-buffer.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" static void bar_init(struct swaybar *bar) { bar->config = init_config(); + bar->visible = true; wl_list_init(&bar->outputs); } +void free_hotspots(struct wl_list *list) { + struct swaybar_hotspot *hotspot, *tmp; + wl_list_for_each_safe(hotspot, tmp, list, link) { + wl_list_remove(&hotspot->link); + if (hotspot->destroy) { + hotspot->destroy(hotspot->data); + } + free(hotspot); + } +} + void free_workspaces(struct wl_list *list) { struct swaybar_workspace *ws, *tmp; wl_list_for_each_safe(ws, tmp, list, link) { @@ -59,14 +71,8 @@ static void swaybar_output_free(struct swaybar_output *output) { wl_output_destroy(output->output); destroy_buffer(&output->buffers[0]); destroy_buffer(&output->buffers[1]); + free_hotspots(&output->hotspots); free_workspaces(&output->workspaces); - struct swaybar_hotspot *hotspot, *hotspot_tmp; - wl_list_for_each_safe(hotspot, hotspot_tmp, &output->hotspots, link) { - if (hotspot->destroy) { - hotspot->destroy(hotspot->data); - } - free(hotspot); - } wl_list_remove(&output->link); free(output->name); free(output); @@ -75,9 +81,7 @@ static void swaybar_output_free(struct swaybar_output *output) { static void set_output_dirty(struct swaybar_output *output) { if (output->frame_scheduled) { output->dirty = true; - return; - } - if (output->surface) { + } else if (output->surface) { render_frame(output); } } @@ -335,21 +339,68 @@ const struct wl_seat_listener seat_listener = { }; static void add_layer_surface(struct swaybar_output *output) { - if (output->surface != NULL) { + if (output->layer_surface) { return; } struct swaybar *bar = output->bar; - output->surface = wl_compositor_create_surface(bar->compositor); - assert(output->surface); + struct swaybar_config *config = bar->config; + bool hidden = strcmp(config->mode, "hide") == 0; output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( bar->layer_shell, output->surface, output->output, + hidden ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); assert(output->layer_surface); zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output); - zwlr_layer_surface_v1_set_anchor(output->layer_surface, - bar->config->position); + + zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); + if (hidden) { + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); + } +} + +static void destroy_layer_surface(struct swaybar_output *output) { + if (!output->layer_surface) { + return; + } + zwlr_layer_surface_v1_destroy(output->layer_surface); + wl_surface_attach(output->surface, NULL, 0, 0); // detach buffer + output->layer_surface = NULL; + output->width = 0; + output->frame_scheduled = false; +} + +bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) { + struct swaybar_config *config = bar->config; + bool visible = !(strcmp(config->mode, "invisible") == 0 || + (strcmp(config->mode, config->hidden_state) == 0 // both "hide" + && !bar->visible_by_modifier && !bar->visible_by_urgency)); + + struct swaybar_output *output; + if (visible == bar->visible) { + if (visible && moving_layer) { + // need to destroy layer surface to move to a different layer + wl_list_for_each(output, &bar->outputs, link) { + destroy_layer_surface(output); + add_layer_surface(output); + } + } + } else { + bar->visible = visible; + wl_list_for_each(output, &bar->outputs, link) { + if (visible) { + add_layer_surface(output); + } else { + destroy_layer_surface(output); + } + } + wlr_log(WLR_DEBUG, "Sending %s signal to status command", + visible ? "cont" : "stop"); + kill(bar->status->pid, + visible ? bar->status->cont_signal : bar->status->stop_signal); + } + return visible; } static bool bar_uses_output(struct swaybar *bar, const char *name) { @@ -420,8 +471,11 @@ static void xdg_output_handle_done(void *data, wl_list_remove(&output->link); wl_list_insert(&bar->outputs, &output->link); - add_layer_surface(output); - set_output_dirty(output); + output->surface = wl_compositor_create_surface(bar->compositor); + assert(output->surface); + if (bar->visible) { + add_layer_surface(output); + } } } @@ -517,22 +571,26 @@ static void set_bar_dirty(struct swaybar *bar) { } } -bool bar_setup(struct swaybar *bar, - const char *socket_path, const char *bar_id) { +bool bar_setup(struct swaybar *bar, const char *socket_path) { bar_init(bar); - init_event_loop(); + bar->eventloop = loop_create(); bar->ipc_socketfd = ipc_open_socket(socket_path); bar->ipc_event_socketfd = ipc_open_socket(socket_path); - if (!ipc_initialize(bar, bar_id)) { + if (!ipc_initialize(bar)) { return false; } if (bar->config->status_command) { bar->status = status_line_init(bar->config->status_command); + bar->status->bar = bar; } bar->display = wl_display_connect(NULL); - assert(bar->display); + if (!bar->display) { + sway_abort("Unable to connect to the compositor. " + "If your compositor is running, check or set the " + "WAYLAND_DISPLAY environment variable."); + } struct wl_registry *registry = wl_display_get_registry(bar->display); wl_registry_add_listener(registry, ®istry_listener, bar); @@ -565,8 +623,11 @@ bool bar_setup(struct swaybar *bar, pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); assert(pointer->cursor_surface); - ipc_get_workspaces(bar); - set_bar_dirty(bar); + if (bar->config->workspace_buttons) { + if (ipc_get_workspaces(bar)) { + set_bar_dirty(bar); + } + } return true; } @@ -590,21 +651,23 @@ static void status_in(int fd, short mask, void *data) { if (mask & (POLLHUP | POLLERR)) { status_error(bar->status, "[error reading from status command]"); set_bar_dirty(bar); - remove_event(fd); + loop_remove_fd(bar->eventloop, fd); } else if (status_handle_readable(bar->status)) { set_bar_dirty(bar); } } void bar_run(struct swaybar *bar) { - add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); - add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar); + loop_add_fd(bar->eventloop, wl_display_get_fd(bar->display), POLLIN, + display_in, bar); + loop_add_fd(bar->eventloop, bar->ipc_event_socketfd, POLLIN, ipc_in, bar); if (bar->status) { - add_event(bar->status->read_fd, POLLIN, status_in, bar); + loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN, + status_in, bar); } while (1) { wl_display_flush(bar->display); - event_loop_poll(); + loop_poll(bar->eventloop); } } @@ -625,4 +688,6 @@ void bar_teardown(struct swaybar *bar) { if (bar->status) { status_line_free(bar->status); } + free(bar->id); + free(bar->mode); } 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) { config->pango_markup = false; config->position = parse_position("bottom"); config->font = strdup("monospace 10"); - config->mode = NULL; + config->mode = strdup("dock"); + config->hidden_state = strdup("hide"); config->sep_symbol = NULL; config->strip_workspace_numbers = false; config->binding_mode_indicator = true; @@ -84,6 +85,7 @@ void free_config(struct swaybar_config *config) { free(config->status_command); free(config->font); free(config->mode); + free(config->hidden_state); free(config->sep_symbol); for (int i = 0; i < config->bindings->length; i++) { 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 @@ -#define _XOPEN_SOURCE 700 -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <strings.h> -#include <poll.h> -#include <time.h> -#include "swaybar/event_loop.h" -#include "list.h" - -struct event_item { - void (*cb)(int fd, short mask, void *data); - void *data; -}; - -struct timer_item { - timer_t timer; - void (*cb)(timer_t timer, void *data); - void *data; -}; - -static struct { - // The order of each must be kept consistent - struct { /* pollfd array */ - struct pollfd *items; - int capacity; - int length; - } fds; - list_t *items; /* event_item list */ - - // Timer list - list_t *timers; -} event_loop; - -void add_timer(timer_t timer, - void(*cb)(timer_t timer, void *data), - void *data) { - - struct timer_item *item = malloc(sizeof(struct timer_item)); - item->timer = timer; - item->cb = cb; - item->data = data; - - list_add(event_loop.timers, item); -} - -void add_event(int fd, short mask, - void(*cb)(int fd, short mask, void *data), void *data) { - - struct pollfd pollfd = { - fd, - mask, - 0, - }; - - // Resize - if (event_loop.fds.length == event_loop.fds.capacity) { - event_loop.fds.capacity += 10; - event_loop.fds.items = realloc(event_loop.fds.items, - sizeof(struct pollfd) * event_loop.fds.capacity); - } - - event_loop.fds.items[event_loop.fds.length++] = pollfd; - - struct event_item *item = malloc(sizeof(struct event_item)); - item->cb = cb; - item->data = data; - - list_add(event_loop.items, item); - - return; -} - -bool remove_event(int fd) { - /* - * Instead of removing events immediately, we mark them for deletion - * and clean them up later. This is so we can call remove_event inside - * an event callback safely. - */ - for (int i = 0; i < event_loop.fds.length; ++i) { - if (event_loop.fds.items[i].fd == fd) { - event_loop.fds.items[i].fd = -1; - return true; - } - } - return false; -} - -static int timer_item_timer_cmp(const void *_timer_item, const void *_timer) { - const struct timer_item *timer_item = _timer_item; - const timer_t *timer = _timer; - if (timer_item->timer == *timer) { - return 0; - } else { - return -1; - } -} -bool remove_timer(timer_t timer) { - int index = list_seq_find(event_loop.timers, timer_item_timer_cmp, &timer); - if (index != -1) { - free(event_loop.timers->items[index]); - list_del(event_loop.timers, index); - return true; - } - return false; -} - -void event_loop_poll(void) { - poll(event_loop.fds.items, event_loop.fds.length, -1); - - for (int i = 0; i < event_loop.fds.length; ++i) { - struct pollfd pfd = event_loop.fds.items[i]; - struct event_item *item = (struct event_item *)event_loop.items->items[i]; - - // Always send these events - unsigned events = pfd.events | POLLHUP | POLLERR; - - if (pfd.revents & events) { - item->cb(pfd.fd, pfd.revents, item->data); - } - } - - // Cleanup removed events - int end = 0; - int length = event_loop.fds.length; - for (int i = 0; i < length; ++i) { - if (event_loop.fds.items[i].fd == -1) { - free(event_loop.items->items[i]); - list_del(event_loop.items, i); - --event_loop.fds.length; - } else if (end != i) { - event_loop.fds.items[end++] = event_loop.fds.items[i]; - } else { - end = i + 1; - } - } - - // check timers - // not tested, but seems to work - for (int i = 0; i < event_loop.timers->length; ++i) { - struct timer_item *item = event_loop.timers->items[i]; - int overrun = timer_getoverrun(item->timer); - if (overrun && overrun != -1) { - item->cb(item->timer, item->data); - } - } -} - -void init_event_loop(void) { - event_loop.fds.length = 0; - event_loop.fds.capacity = 10; - event_loop.fds.items = malloc( - event_loop.fds.capacity * sizeof(struct pollfd)); - event_loop.items = create_list(); - event_loop.timers = create_list(); -} 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( json_object_put(bar_config); return false; } - json_object *markup, *mode, *hidden_bar, *position, *status_command; + json_object *markup, *mode, *hidden_state, *position, *status_command; json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; json_object *bindings; json_object_object_get_ex(bar_config, "mode", &mode); - json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); + json_object_object_get_ex(bar_config, "hidden_state", &hidden_state); json_object_object_get_ex(bar_config, "position", &position); json_object_object_get_ex(bar_config, "status_command", &status_command); json_object_object_get_ex(bar_config, "font", &font); @@ -220,6 +220,14 @@ static bool ipc_parse_config( list_add(config->bindings, binding); } } + if (hidden_state) { + free(config->hidden_state); + config->hidden_state = strdup(json_object_get_string(hidden_state)); + } + if (mode) { + free(config->mode); + config->mode = strdup(json_object_get_string(mode)); + } struct config_output *output, *tmp; wl_list_for_each_safe(output, tmp, &config->outputs, link) { @@ -254,7 +262,7 @@ static bool ipc_parse_config( return true; } -void ipc_get_workspaces(struct swaybar *bar) { +bool ipc_get_workspaces(struct swaybar *bar) { struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { free_workspaces(&output->workspaces); @@ -266,8 +274,10 @@ void ipc_get_workspaces(struct swaybar *bar) { json_object *results = json_tokener_parse(res); if (!results) { free(res); - return; + return false; } + + bar->visible_by_urgency = false; size_t length = json_object_array_length(results); json_object *ws_json; json_object *num, *name, *visible, *focused, *out, *urgent; @@ -294,12 +304,16 @@ void ipc_get_workspaces(struct swaybar *bar) { output->focused = true; } ws->urgent = json_object_get_boolean(urgent); + if (ws->urgent) { + bar->visible_by_urgency = true; + } wl_list_insert(&output->workspaces, &ws->link); } } } json_object_put(results); free(res); + return determine_bar_visibility(bar, false); } static void ipc_get_outputs(struct swaybar *bar) { @@ -345,10 +359,10 @@ void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) { IPC_COMMAND, bind->command, &len)); } -bool ipc_initialize(struct swaybar *bar, const char *bar_id) { - uint32_t len = strlen(bar_id); +bool ipc_initialize(struct swaybar *bar) { + uint32_t len = strlen(bar->id); char *res = ipc_single_command(bar->ipc_socketfd, - IPC_GET_BAR_CONFIG, bar_id, &len); + IPC_GET_BAR_CONFIG, bar->id, &len); if (!ipc_parse_config(bar->config, res)) { free(res); return false; @@ -356,56 +370,108 @@ bool ipc_initialize(struct swaybar *bar, const char *bar_id) { free(res); ipc_get_outputs(bar); - const char *subscribe = "[ \"workspace\", \"mode\" ]"; - len = strlen(subscribe); + struct swaybar_config *config = bar->config; + char subscribe[128]; // suitably large buffer + len = snprintf(subscribe, 128, + "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", + config->binding_mode_indicator ? ", \"mode\"" : "", + config->workspace_buttons ? ", \"workspace\"" : ""); free(ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe, &len)); return true; } +static bool handle_bar_state_update(struct swaybar *bar, json_object *event) { + json_object *json_id; + json_object_object_get_ex(event, "id", &json_id); + const char *id = json_object_get_string(json_id); + if (strcmp(id, bar->id) != 0) { + return false; + } + + json_object *visible_by_modifier; + json_object_object_get_ex(event, "visible_by_modifier", &visible_by_modifier); + bar->visible_by_modifier = json_object_get_boolean(visible_by_modifier); + return determine_bar_visibility(bar, false); +} + +static bool handle_barconfig_update(struct swaybar *bar, + json_object *json_config) { + json_object *json_id; + json_object_object_get_ex(json_config, "id", &json_id); + const char *id = json_object_get_string(json_id); + if (strcmp(id, bar->id) != 0) { + return false; + } + + struct swaybar_config *config = bar->config; + + json_object *json_state; + json_object_object_get_ex(json_config, "hidden_state", &json_state); + const char *new_state = json_object_get_string(json_state); + char *old_state = config->hidden_state; + if (strcmp(new_state, old_state) != 0) { + wlr_log(WLR_DEBUG, "Changing bar hidden state to %s", new_state); + free(old_state); + config->hidden_state = strdup(new_state); + return determine_bar_visibility(bar, false); + } + + free(config->mode); + json_object *json_mode; + json_object_object_get_ex(json_config, "mode", &json_mode); + config->mode = strdup(json_object_get_string(json_mode)); + wlr_log(WLR_DEBUG, "Changing bar mode to %s", config->mode); + + return determine_bar_visibility(bar, true); +} + bool handle_ipc_readable(struct swaybar *bar) { struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); if (!resp) { return false; } + + json_object *result = json_tokener_parse(resp->payload); + if (!result) { + wlr_log(WLR_ERROR, "failed to parse payload as json"); + free_ipc_response(resp); + return false; + } + + bool bar_is_dirty = true; switch (resp->type) { case IPC_EVENT_WORKSPACE: - ipc_get_workspaces(bar); + bar_is_dirty = ipc_get_workspaces(bar); break; case IPC_EVENT_MODE: { - json_object *result = json_tokener_parse(resp->payload); - if (!result) { - free_ipc_response(resp); - wlr_log(WLR_ERROR, "failed to parse payload as json"); - return false; - } json_object *json_change, *json_pango_markup; if (json_object_object_get_ex(result, "change", &json_change)) { const char *change = json_object_get_string(json_change); - free(bar->config->mode); - if (strcmp(change, "default") == 0) { - bar->config->mode = NULL; - } else { - bar->config->mode = strdup(change); - } + free(bar->mode); + bar->mode = strcmp(change, "default") != 0 ? strdup(change) : NULL; } else { wlr_log(WLR_ERROR, "failed to parse response"); - json_object_put(result); - free_ipc_response(resp); - return false; + bar_is_dirty = false; + break; } if (json_object_object_get_ex(result, "pango_markup", &json_pango_markup)) { - bar->config->mode_pango_markup = json_object_get_boolean( - json_pango_markup); + bar->mode_pango_markup = json_object_get_boolean(json_pango_markup); } - json_object_put(result); break; } + case IPC_EVENT_BARCONFIG_UPDATE: + bar_is_dirty = handle_barconfig_update(bar, result); + break; + case IPC_EVENT_BAR_STATE_UPDATE: + bar_is_dirty = handle_bar_state_update(bar, result); + break; default: - free_ipc_response(resp); - return false; + bar_is_dirty = false; + break; } + json_object_put(result); free_ipc_response(resp); - return true; + return bar_is_dirty; } 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) { int main(int argc, char **argv) { char *socket_path = NULL; - char *bar_id = NULL; bool debug = false; static struct option long_options[] = { @@ -59,7 +58,7 @@ int main(int argc, char **argv) { socket_path = strdup(optarg); break; case 'b': // Type - bar_id = strdup(optarg); + swaybar.id = strdup(optarg); break; case 'v': fprintf(stdout, "swaybar version " SWAY_VERSION "\n"); @@ -80,7 +79,7 @@ int main(int argc, char **argv) { wlr_log_init(WLR_ERROR, NULL); } - if (!bar_id) { + if (!swaybar.id) { wlr_log(WLR_ERROR, "No bar_id passed. " "Provide --bar_id or let sway start swaybar"); return 1; @@ -96,13 +95,12 @@ int main(int argc, char **argv) { signal(SIGTERM, sig_handler); - if (!bar_setup(&swaybar, socket_path, bar_id)) { + if (!bar_setup(&swaybar, socket_path)) { free(socket_path); return 1; } free(socket_path); - free(bar_id); bar_run(&swaybar); 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( 'swaybar', [ 'bar.c', 'config.c', - 'event_loop.c', 'i3bar.c', 'ipc.c', '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, static uint32_t render_binding_mode_indicator(cairo_t *cairo, struct swaybar_output *output, double x) { + const char *mode = output->bar->mode; + if (!mode) { + return 0; + } + struct swaybar_config *config = output->bar->config; - const char *mode = config->mode; int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, NULL, - output->scale, config->mode_pango_markup, + output->scale, output->bar->mode_pango_markup, "%s", mode); int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; @@ -333,8 +337,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, config->colors.binding_mode.text); cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); - pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, - "%s", mode); + pango_printf(cairo, config->font, output->scale, + output->bar->mode_pango_markup, "%s", mode); return output->height; } @@ -465,7 +469,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { max_height = h > max_height ? h : max_height; } } - if (config->binding_mode_indicator && config->mode) { + if (config->binding_mode_indicator) { uint32_t h = render_binding_mode_indicator(cairo, output, x); max_height = h > max_height ? h : max_height; } @@ -490,16 +494,12 @@ static const struct wl_callback_listener output_frame_listener = { void render_frame(struct swaybar_output *output) { assert(output->surface != NULL); - - struct swaybar_hotspot *hotspot, *tmp; - wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { - if (hotspot->destroy) { - hotspot->destroy(hotspot->data); - } - wl_list_remove(&hotspot->link); - free(hotspot); + if (!output->layer_surface) { + return; } + free_hotspots(&output->hotspots); + cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); @@ -519,10 +519,12 @@ void render_frame(struct swaybar_output *output) { if (config_height >= 0 && height < (uint32_t)config_height) { height = config_height; } - if (height != output->height) { + if (height != output->height || output->width == 0) { // Reconfigure surface zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height); - zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); + if (strcmp(output->bar->config->mode, "dock") == 0) { + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); + } // TODO: this could infinite loop if the compositor assigns us a // different height than what we asked for 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 @@ #include <stdio.h> #include <unistd.h> #include <wlr/util/log.h> +#include "loop.h" #include "swaybar/bar.h" #include "swaybar/config.h" #include "swaybar/i3bar.h" -#include "swaybar/event_loop.h" #include "swaybar/status_line.h" #include "readline.h" static void status_line_close_fds(struct status_line *status) { if (status->read_fd != -1) { - remove_event(status->read_fd); + loop_remove_fd(status->bar->eventloop, status->read_fd); close(status->read_fd); status->read_fd = -1; } @@ -83,6 +83,17 @@ bool status_handle_readable(struct status_line *status) { return true; } } + + json_object *signal; + if (json_object_object_get_ex(header, "stop_signal", &signal)) { + status->stop_signal = json_object_get_int(signal); + wlr_log(WLR_DEBUG, "Setting stop signal to %d", status->stop_signal); + } + if (json_object_object_get_ex(header, "cont_signal", &signal)) { + status->cont_signal = json_object_get_int(signal); + wlr_log(WLR_DEBUG, "Setting cont signal to %d", status->cont_signal); + } + json_object_put(header); wl_list_init(&status->blocks); @@ -121,6 +132,9 @@ bool status_handle_readable(struct status_line *status) { struct status_line *status_line_init(char *cmd) { struct status_line *status = calloc(1, sizeof(struct status_line)); + status->stop_signal = SIGSTOP; + status->cont_signal = SIGCONT; + status->buffer_size = 8192; status->buffer = malloc(status->buffer_size); 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) { } state.display = wl_display_connect(NULL); - assert(state.display); + if (!state.display) { + wlr_log(WLR_ERROR, "Unable to connect to the compositor. " + "If your compositor is running, check or set the " + "WAYLAND_DISPLAY environment variable."); + return 1; + } struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_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[]) { state.display = wl_display_connect(NULL); if (state.display == NULL) { - wlr_log(WLR_ERROR, "Failed to create display"); + wlr_log(WLR_ERROR, "Unable to connect to the compositor. " + "If your compositor is running, check or set the " + "WAYLAND_DISPLAY environment variable."); return -3; } 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 @@ #include "pool-buffer.h" #include "cairo.h" #include "log.h" +#include "loop.h" #include "readline.h" #include "stringop.h" #include "util.h" @@ -633,13 +634,9 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state, } break; case 'v': -#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE - fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n", - SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); -#else - fprintf(stdout, "version unknown\n"); -#endif - return 1; + fprintf(stdout, "swaylock version " SWAY_VERSION "\n"); + exit(EXIT_SUCCESS); + break; case LO_BS_HL_COLOR: if (state) { state->args.colors.bs_highlight = parse_color(optarg); @@ -844,6 +841,10 @@ static int load_config(char *path, struct swaylock_state *state, static struct swaylock_state state; +static void display_in(int fd, short mask, void *data) { + wl_display_dispatch(state.display); +} + int main(int argc, char **argv) { wlr_log_init(WLR_DEBUG, NULL); initialize_pw_backend(); @@ -903,7 +904,11 @@ int main(int argc, char **argv) { wl_list_init(&state.surfaces); state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); state.display = wl_display_connect(NULL); - assert(state.display); + if (!state.display) { + sway_abort("Unable to connect to the compositor. " + "If your compositor is running, check or set the " + "WAYLAND_DISPLAY environment variable."); + } struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_listener, &state); @@ -946,9 +951,14 @@ int main(int argc, char **argv) { daemonize(); } + state.eventloop = loop_create(); + loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLL_IN, + display_in, NULL); + state.run_display = true; - while (wl_display_dispatch(state.display) != -1 && state.run_display) { - // This space intentionally left blank + while (state.run_display) { + wl_display_flush(state.display); + loop_poll(state.eventloop); } 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 @@ #include <xkbcommon/xkbcommon.h> #include "swaylock/swaylock.h" #include "swaylock/seat.h" +#include "loop.h" #include "unicode.h" void clear_password_buffer(struct swaylock_password *pw) { // Use volatile keyword so so compiler can't optimize this out. volatile char *buffer = pw->buffer; volatile char zero = '\0'; - for (size_t i = 0; i < sizeof(buffer); ++i) { + for (size_t i = 0; i < sizeof(pw->buffer); ++i) { buffer[i] = zero; } pw->len = 0; @@ -39,6 +40,43 @@ static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { pw->len += utf8_size; } +static void clear_indicator(void *data) { + struct swaylock_state *state = data; + state->clear_indicator_timer = NULL; + state->auth_state = AUTH_STATE_IDLE; + damage_state(state); +} + +static void schedule_indicator_clear(struct swaylock_state *state) { + if (state->clear_indicator_timer) { + loop_remove_timer(state->eventloop, state->clear_indicator_timer); + } + state->clear_indicator_timer = loop_add_timer( + state->eventloop, 3000, clear_indicator, state); +} + +static void clear_password(void *data) { + struct swaylock_state *state = data; + state->clear_password_timer = NULL; + state->auth_state = AUTH_STATE_CLEAR; + clear_password_buffer(&state->password); + damage_state(state); + schedule_indicator_clear(state); +} + +static void schedule_password_clear(struct swaylock_state *state) { + if (state->clear_password_timer) { + loop_remove_timer(state->eventloop, state->clear_password_timer); + } + state->clear_password_timer = loop_add_timer( + state->eventloop, 10000, clear_password, state); +} + +static void handle_preverify_timeout(void *data) { + struct swaylock_state *state = data; + state->verify_password_timer = NULL; +} + void swaylock_handle_key(struct swaylock_state *state, xkb_keysym_t keysym, uint32_t codepoint) { switch (keysym) { @@ -50,7 +88,18 @@ void swaylock_handle_key(struct swaylock_state *state, state->auth_state = AUTH_STATE_VALIDATING; damage_state(state); - while (wl_display_dispatch(state->display) != -1 && state->run_display) { + + // We generally want to wait until all surfaces are showing the + // "verifying" state before we go and verify the password, because + // verifying it is a blocking operation. However, if the surface is on + // an output with DPMS off then it won't update, so we set a timer. + state->verify_password_timer = loop_add_timer( + state->eventloop, 50, handle_preverify_timeout, state); + + while (state->run_display && state->verify_password_timer) { + wl_display_flush(state->display); + loop_poll(state->eventloop); + bool ok = 1; struct swaylock_surface *surface; wl_list_for_each(surface, &state->surfaces, link) { @@ -70,6 +119,7 @@ void swaylock_handle_key(struct swaylock_state *state, } state->auth_state = AUTH_STATE_INVALID; damage_state(state); + schedule_indicator_clear(state); break; case XKB_KEY_Delete: case XKB_KEY_BackSpace: @@ -79,11 +129,14 @@ void swaylock_handle_key(struct swaylock_state *state, state->auth_state = AUTH_STATE_CLEAR; } damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); break; case XKB_KEY_Escape: clear_password_buffer(&state->password); state->auth_state = AUTH_STATE_CLEAR; damage_state(state); + schedule_indicator_clear(state); break; case XKB_KEY_Caps_Lock: /* The state is getting active after this @@ -91,6 +144,8 @@ void swaylock_handle_key(struct swaylock_state *state, state->xkb.caps_lock = !state->xkb.caps_lock; state->auth_state = AUTH_STATE_INPUT_NOP; damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); break; case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: @@ -104,12 +159,15 @@ void swaylock_handle_key(struct swaylock_state *state, case XKB_KEY_Super_R: state->auth_state = AUTH_STATE_INPUT_NOP; damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); break; case XKB_KEY_u: if (state->xkb.control) { clear_password_buffer(&state->password); state->auth_state = AUTH_STATE_CLEAR; damage_state(state); + schedule_indicator_clear(state); break; } // fallthrough @@ -118,6 +176,8 @@ void swaylock_handle_key(struct swaylock_state *state, append_ch(&state->password, codepoint); state->auth_state = AUTH_STATE_INPUT; damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); } break; } 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 = { void swaynag_setup(struct swaynag *swaynag) { swaynag->display = wl_display_connect(NULL); - assert(swaynag->display); + if (!swaynag->display) { + sway_abort("Unable to connect to the compositor. " + "If your compositor is running, check or set the " + "WAYLAND_DISPLAY environment variable."); + } swaynag->scale = 1; wl_list_init(&swaynag->outputs); |