diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 7f0bef02..456837df 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -21,7 +21,7 @@ packages: - xwayland sources: - https://github.com/swaywm/sway - - https://github.com/swaywm/wlroots + - https://gitlab.freedesktop.org/wlroots/wlroots.git#0.15.0 tasks: - wlroots: | cd wlroots diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index a8f1dfed..c79e3fe2 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -18,7 +18,7 @@ packages: - seatd sources: - https://github.com/swaywm/sway - - https://github.com/swaywm/wlroots + - https://gitlab.freedesktop.org/wlroots/wlroots.git#0.15.0 tasks: - wlroots: | cd wlroots diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 1a3c8512..5aa0e3dd 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -26,7 +26,7 @@ packages: - x11/xcb-util-wm sources: - https://github.com/swaywm/sway -- https://github.com/swaywm/wlroots +- https://gitlab.freedesktop.org/wlroots/wlroots.git#0.15.0 tasks: - setup: | cd sway diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 24ea869d..00000000 --- a/.clang-format +++ /dev/null @@ -1,18 +0,0 @@ -BasedOnStyle: LLVM -IndentWidth: 4 -TabWidth: 4 -UseTab: Always -BreakBeforeBraces: Attach -AllowShortIfStatementsOnASingleLine: false -IndentCaseLabels: false -SortIncludes: false -ColumnLimit: 80 -AlignAfterOpenBracket: DontAlign -BinPackParameters: true -BinPackArguments: true -ContinuationIndentWidth: 8 -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortLoopsOnASingleLine: true -ReflowComments: false -AllowAllArgumentsOnNextLine: false -AlignOperands: DontAlign diff --git a/README.de.md b/README.de.md index 6e1e8ca9..01b5e9ad 100644 --- a/README.de.md +++ b/README.de.md @@ -15,7 +15,7 @@ Falls du sway für deine eigene Distribution als Paket bereitstellen möchtest, sway benötigt die folgenden Pakete: * meson\* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols\* * pcre diff --git a/README.dk.md b/README.dk.md index 94c0b9eb..f712e96b 100644 --- a/README.dk.md +++ b/README.dk.md @@ -70,5 +70,5 @@ support til dem (gdm er kendt for at fungere temmelig godt). [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.es.md b/README.es.md index 951a2eba..7af7d90b 100644 --- a/README.es.md +++ b/README.es.md @@ -25,7 +25,7 @@ escriba un email a sir@cmpwn.com Instale las dependencias: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.fr.md b/README.fr.md index d1f4f934..359a30f9 100644 --- a/README.fr.md +++ b/README.fr.md @@ -79,5 +79,5 @@ bien fonctionner). [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [versions GitHub]: https://github.com/swaywm/sway/releases [Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.gr.md b/README.gr.md index 5bb04932..4c30e29d 100644 --- a/README.gr.md +++ b/README.gr.md @@ -69,5 +69,5 @@ _\*Compile-time dep_ [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc \ No newline at end of file diff --git a/README.hu.md b/README.hu.md index 75999071..4e006f25 100644 --- a/README.hu.md +++ b/README.hu.md @@ -73,5 +73,5 @@ gdm-ről ismeretes, hogy egész jól működik.) [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.ir.md b/README.ir.md index 890a0fd2..4542b93b 100644 --- a/README.ir.md +++ b/README.ir.md @@ -1,9 +1,7 @@ -
- # sway -sway یک کامپوزیتور الهام گرفته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال -IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (#sway sur +‏sway یک کامپوزیتور الهام گرفته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال +IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (‎#sway‏ در irc.libera.chat). برای حمایت از تیم توسعه sway به [صفحه @@ -17,7 +15,7 @@ Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مرا ### از بسته‌های رسمی -sway در بسته‌های رسمی توزیع‌های مختلف وجود دارد. بسته «sway» را نصب کنید. در صورتی که بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صفحه راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید. +‏sway در بسته‌های رسمی توزیع‌های مختلف وجود دارد. بسته «sway» را نصب کنید. در صورتی که بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صفحه راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید. اگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استفاده کنید یا به sir@cmpwn.com ایمیل بزنید. @@ -28,7 +26,7 @@ sway در بسته‌های رسمی توزیع‌های مختلف وجود د بسته‌های مورد نیاز: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre @@ -42,21 +40,16 @@ sway در بسته‌های رسمی توزیع‌های مختلف وجود د _\*نیازمندی‌های زمان کامپایل برنامه_ این فرمان‌ها را اجرا کنید: -
meson build ninja -C build sudo ninja -C build install -
- روی سیستم‌های بدون logind، باید فرمان زیر را برای suid کردن باینری sway اجرا کنید: -
sudo chmod a+s /usr/local/bin/sway -
-sway پس از startup مجوزهای دسترسی root را رها می‌کند. +‏sway پس از startup مجوزهای دسترسی root را رها می‌کند. ### شخصی سازی و تنظیمات @@ -64,7 +57,4 @@ sway پس از startup مجوزهای دسترسی root را رها می‌کن ## اجرا -در محیط TTY کافیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طرف sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد). - -
- +در محیط TTY کافیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طرف sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد). diff --git a/README.ja.md b/README.ja.md index 7af54fb2..786e169c 100644 --- a/README.ja.md +++ b/README.ja.md @@ -27,7 +27,7 @@ Swayは沢山のディストリビューションで提供されています。" 次の依存パッケージをインストールしてください: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.ko.md b/README.ko.md index 76ce2f4d..1086da0c 100644 --- a/README.ko.md +++ b/README.ko.md @@ -24,7 +24,7 @@ IRC 채널을 방문하거나 sir@cmpwn.com으로 이메일을 보내 상담 받 다음 의존 패키지들을 설치해 주세요: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.md b/README.md index 3367f846..8c252e9e 100644 --- a/README.md +++ b/README.md @@ -87,5 +87,5 @@ sway (gdm is known to work fairly well). [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.nl.md b/README.nl.md index 3351db39..c0a93063 100644 --- a/README.nl.md +++ b/README.nl.md @@ -25,7 +25,7 @@ kanaal of stuur een e-mail naar sir@cmpwn.com voor advies. Afhankelijkheden installeren: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.pl.md b/README.pl.md index da987b7c..6d376b68 100644 --- a/README.pl.md +++ b/README.pl.md @@ -25,7 +25,7 @@ adres sir@cmpwn.com w celu uzyskania wskazówek. Zainstaluj zależności: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.pt.md b/README.pt.md index 7d449ef3..92a4b54d 100644 --- a/README.pt.md +++ b/README.pt.md @@ -27,7 +27,7 @@ Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development Instale as dependências: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.ro.md b/README.ro.md index 79524b79..f7785b8f 100644 --- a/README.ro.md +++ b/README.ro.md @@ -22,7 +22,7 @@ Dacă sunteți interesați in a crea pachete pentru distribuția voastră, infor Dependențe pentru instalare: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.ru.md b/README.ru.md index 70396905..d563859c 100644 --- a/README.ru.md +++ b/README.ru.md @@ -70,5 +70,5 @@ sway (gdm работает довольно неплохо). [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.tr.md b/README.tr.md index c0f72d72..5c98a538 100644 --- a/README.tr.md +++ b/README.tr.md @@ -64,5 +64,5 @@ TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manag [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://github.com/swaywm/wlroots +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots [scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.uk.md b/README.uk.md index 3d7402de..ff9ebec3 100644 --- a/README.uk.md +++ b/README.uk.md @@ -36,7 +36,7 @@ Sway доступний у багатьох дистрибутивах Linux (а Встановіть залежності: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.zh-CN.md b/README.zh-CN.md index ecb46789..561d6c14 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -25,7 +25,7 @@ Sway 在很多发行版中可用. 尝试在你的发行版中安装 "sway" 包. 安装依赖: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/README.zh-TW.md b/README.zh-TW.md index 4fd656da..bc30b903 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -25,7 +25,7 @@ Sway 在許多發行版都有提供。請自己嘗試於你的發行版安裝 相依套件: * meson \* -* [wlroots](https://github.com/swaywm/wlroots) +* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) * wayland * wayland-protocols \* * pcre diff --git a/common/util.c b/common/util.c index 199f3ee1..5d4c0673 100644 --- a/common/util.c +++ b/common/util.c @@ -80,6 +80,12 @@ enum movement_unit parse_movement_unit(const char *unit) { int parse_movement_amount(int argc, char **argv, struct movement_amount *amount) { + if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) { + amount->amount = 0; + amount->unit = MOVEMENT_UNIT_INVALID; + return 0; + } + char *err; amount->amount = (int)strtol(argv[0], &err, 10); if (*err) { diff --git a/include/ipc-client.h b/include/ipc-client.h index d3895023..9c5712d7 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h @@ -1,6 +1,9 @@ #ifndef _SWAY_IPC_CLIENT_H #define _SWAY_IPC_CLIENT_H +// arbitrary number, it's probably sufficient, higher number = more memory usage +#define JSON_MAX_DEPTH 512 + #include #include #include diff --git a/include/sway/commands.h b/include/sway/commands.h index 4be40870..2746ef28 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -112,6 +112,7 @@ sway_cmd cmd_border; sway_cmd cmd_client_noop; sway_cmd cmd_client_focused; sway_cmd cmd_client_focused_inactive; +sway_cmd cmd_client_focused_tab_title; sway_cmd cmd_client_unfocused; sway_cmd cmd_client_urgent; sway_cmd cmd_client_placeholder; @@ -284,6 +285,7 @@ sway_cmd output_cmd_max_render_time; sway_cmd output_cmd_mode; sway_cmd output_cmd_modeline; sway_cmd output_cmd_position; +sway_cmd output_cmd_render_bit_depth; sway_cmd output_cmd_scale; sway_cmd output_cmd_scale_filter; sway_cmd output_cmd_subpixel; diff --git a/include/sway/config.h b/include/sway/config.h index 46dd4ffe..fda0e83f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -247,6 +247,12 @@ enum scale_filter_mode { SCALE_FILTER_SMART, }; +enum render_bit_depth { + RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 + RENDER_BIT_DEPTH_8, + RENDER_BIT_DEPTH_10, +}; + /** * Size and position configuration for a particular output. * @@ -266,6 +272,7 @@ struct output_config { enum wl_output_subpixel subpixel; int max_render_time; // In milliseconds int adaptive_sync; + enum render_bit_depth render_bit_depth; char *background; char *background_option; @@ -283,6 +290,12 @@ struct side_gaps { int left; }; +enum smart_gaps_mode { + SMART_GAPS_OFF, + SMART_GAPS_ON, + SMART_GAPS_INVERSE_OUTER, +}; + /** * Stores configuration for a workspace, regardless of whether the workspace * exists. @@ -512,7 +525,7 @@ struct sway_config { bool tiling_drag; int tiling_drag_threshold; - bool smart_gaps; + enum smart_gaps_mode smart_gaps; int gaps_inner; struct side_gaps gaps_outer; @@ -535,12 +548,15 @@ struct sway_config { struct { struct border_colors focused; struct border_colors focused_inactive; + struct border_colors focused_tab_title; struct border_colors unfocused; struct border_colors urgent; struct border_colors placeholder; float background[4]; } border_colors; + bool has_focused_tab_title; + // floating view int32_t floating_maximum_width; int32_t floating_maximum_height; diff --git a/include/sway/layers.h b/include/sway/layers.h index 224dc5e6..14816861 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -25,6 +25,8 @@ struct sway_layer_surface { bool mapped; struct wlr_box extent; enum zwlr_layer_shell_v1_layer layer; + + struct wl_list subsurfaces; }; struct sway_layer_popup { @@ -44,6 +46,7 @@ struct sway_layer_popup { struct sway_layer_subsurface { struct wlr_subsurface *wlr_subsurface; struct sway_layer_surface *layer_surface; + struct wl_list link; struct wl_listener map; struct wl_listener unmap; diff --git a/include/sway/output.h b/include/sway/output.h index 5dfe0fff..26b9709f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -48,7 +48,7 @@ struct sway_output { struct wl_listener damage_frame; struct { - struct wl_signal destroy; + struct wl_signal disable; } events; struct timespec last_presentation; diff --git a/include/sway/server.h b/include/sway/server.h index 88dda097..0bd860b2 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -32,13 +33,16 @@ struct sway_server { const char *socket; struct wlr_backend *backend; - struct wlr_backend *noop_backend; // secondary headless backend used for creating virtual outputs on-the-fly struct wlr_backend *headless_backend; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_compositor *compositor; struct wl_listener compositor_new_surface; + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; + struct wlr_data_device_manager *data_device_manager; struct sway_input_manager *input; @@ -137,6 +141,8 @@ void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); void server_run(struct sway_server *server); +void restore_nofile_limit(void); + void handle_compositor_new_surface(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 97fa98c1..05761150 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -117,12 +117,14 @@ struct sway_container { struct wlr_texture *title_focused; struct wlr_texture *title_focused_inactive; + struct wlr_texture *title_focused_tab_title; struct wlr_texture *title_unfocused; struct wlr_texture *title_urgent; list_t *marks; // char * struct wlr_texture *marks_focused; struct wlr_texture *marks_focused_inactive; + struct wlr_texture *marks_focused_tab_title; struct wlr_texture *marks_unfocused; struct wlr_texture *marks_urgent; diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index e8f4d573..5d4a2f2d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -31,7 +31,7 @@ struct sway_root { list_t *scratchpad; // struct sway_container // For when there's no connected outputs - struct sway_output *noop_output; + struct sway_output *fallback_output; struct sway_container *fullscreen_global; diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 9e39e716..baa6ee8b 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -5,7 +5,6 @@ #include "list.h" #include "pool-buffer.h" #include "swaynag/types.h" -#include "xdg-output-unstable-v1-client-protocol.h" #define SWAYNAG_MAX_HEIGHT 500 @@ -75,13 +74,11 @@ struct swaynag_details { struct swaynag { bool run_display; - int querying_outputs; struct wl_display *display; struct wl_compositor *compositor; struct wl_seat *seat; struct wl_shm *shm; - struct zxdg_output_manager_v1 *xdg_output_manager; struct wl_list outputs; // swaynag_output::link struct wl_list seats; // swaynag_seat::link struct swaynag_output *output; diff --git a/meson.build b/meson.build index 436b84d1..5e4de87f 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,9 @@ project( 'sway', 'c', - version: '1.6', + version: '1.7', license: 'MIT', - meson_version: '>=0.59.0', + meson_version: '>=0.60.0', default_options: [ 'c_std=c11', 'warning_level=2', @@ -37,11 +37,11 @@ endif jsonc = dependency('json-c', version: '>=0.13') pcre = dependency('libpcre') -wayland_server = dependency('wayland-server') +wayland_server = dependency('wayland-server', version: '>=1.20.0') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_egl = dependency('wayland-egl') -wayland_protos = dependency('wayland-protocols', version: '>=1.14') +wayland_protos = dependency('wayland-protocols', version: '>=1.24') xkbcommon = dependency('xkbcommon') cairo = dependency('cairo') pango = dependency('pango') @@ -92,18 +92,10 @@ if get_option('sd-bus-provider') == 'auto' if not get_option('tray').disabled() assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') endif - sdbus = dependency('libsystemd', + sdbus = dependency(['libsystemd', 'libelogind'], required: false, version: '>=239', - not_found_message: 'libsystemd not found, trying libelogind', ) - if not sdbus.found() - sdbus = dependency('libelogind', - required: false, - version: '>=239', - not_found_message: 'libelogind not found, trying basu', - ) - endif if not sdbus.found() sdbus = dependency('basu', required: false) endif @@ -171,8 +163,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir version = '"@0@"'.format(meson.project_version()) git = find_program('git', native: true, required: false) if git.found() - git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) - git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) + git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) + git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) if git_commit.returncode() == 0 and git_branch.returncode() == 0 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( meson.project_version(), diff --git a/protocols/meson.build b/protocols/meson.build index 8e9e65be..df24a4e5 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -15,6 +15,7 @@ protocols = [ [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], + [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['idle.xml'], ['wlr-input-inhibitor-unstable-v1.xml'], diff --git a/sway/commands.c b/sway/commands.c index 205406ad..5a1fd32e 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -51,6 +51,7 @@ static const struct cmd_handler handlers[] = { { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, { "client.focused_inactive", cmd_client_focused_inactive }, + { "client.focused_tab_title", cmd_client_focused_tab_title }, { "client.placeholder", cmd_client_noop }, { "client.unfocused", cmd_client_unfocused }, { "client.urgent", cmd_client_urgent }, diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 1f08a5d2..8b661e3a 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c @@ -54,7 +54,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { } const char *state = argv[0]; - if (config->reading) { + if (config->current_bar) { error = bar_set_hidden_state(config->current_bar, state); } else { const char *id = argc == 2 ? argv[1] : NULL; diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 8b3fb275..7c2f423b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -58,7 +58,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { } const char *mode = argv[0]; - if (config->reading) { + if (config->current_bar) { error = bar_set_mode(config->current_bar, mode); } else { const char *id = argc == 2 ? argv[1] : NULL; diff --git a/sway/commands/client.c b/sway/commands/client.c index dd0694df..77263145 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -18,6 +18,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, return error; } + if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) { + sway_log(SWAY_ERROR, + "Warning: indicator and child_border colors have no effect for %s", + cmd_name); + } + struct border_colors colors = {0}; const char *ind_hex = argc > 3 ? argv[3] : default_indicator; const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background @@ -80,3 +86,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) { sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); return cmd_results_new(CMD_SUCCESS, NULL); } + +struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { + struct cmd_results *result = handle_command(argc, argv, + "client.focused_tab_title", + &config->border_colors.focused_tab_title, "#2e9ef4ff"); + if (result && result->status == CMD_SUCCESS) { + config->has_focused_tab_title = true; + } + return result; +} diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index fce337d5..b35065c1 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -7,6 +7,7 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" @@ -53,6 +54,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { // Fork process if ((pid = fork()) == 0) { // Fork child process again + restore_nofile_limit(); setsid(); sigset_t set; sigemptyset(&set); diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 6771ca2f..b8d28480 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, new_focus = seat_get_focus_inactive_tiling(seat, ws); } if (new_focus) { + struct sway_container *new_focus_view = + seat_get_focus_inactive_view(seat, &new_focus->node); + if (new_focus_view) { + new_focus = new_focus_view; + } seat_set_focus_container(seat, new_focus); // If we're on the floating layer and the floating container area @@ -446,7 +451,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, ""); } struct sway_node *next_focus = NULL; - if (container_is_floating(container)) { + if (container_is_floating(container) && + container->pending.fullscreen_mode == FULLSCREEN_NONE) { next_focus = node_get_in_direction_floating(container, seat, direction); } else { next_focus = node_get_in_direction_tiling(container, seat, direction, descend); diff --git a/sway/commands/move.c b/sway/commands/move.c index f2702fa1..1a05a7a6 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -874,6 +874,10 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Invalid x position specified"); } + if (argc < 1) { + return cmd_results_new(CMD_FAILURE, expected_position_syntax); + } + struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; // Y direction num_consumed_args = parse_movement_amount(argc, argv, &ly); diff --git a/sway/commands/output.c b/sway/commands/output.c index d8ef2885..125df5a7 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = { { "modeline", output_cmd_modeline }, { "pos", output_cmd_position }, { "position", output_cmd_position }, + { "render_bit_depth", output_cmd_render_bit_depth }, { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, { "scale", output_cmd_scale }, @@ -33,9 +34,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { return error; } - // The NOOP-1 output is a dummy output used when there's no outputs + // The HEADLESS-1 output is a dummy output used when there's no outputs // connected. It should never be configured. - if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { + if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) { return cmd_results_new(CMD_FAILURE, "Refusing to configure the no op output"); } @@ -52,7 +53,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { if (!sway_output) { return cmd_results_new(CMD_FAILURE, "Unknown output"); } - if (sway_output == root->noop_output) { + if (sway_output == root->fallback_output) { return cmd_results_new(CMD_FAILURE, "Refusing to configure the no op output"); } diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c new file mode 100644 index 00000000..c419321e --- /dev/null +++ b/sway/commands/output/render_bit_depth.c @@ -0,0 +1,29 @@ +#include +#include +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); + } + + if (strcmp(*argv, "8") == 0) { + config->handler_context.output_config->render_bit_depth = + RENDER_BIT_DEPTH_8; + } else if (strcmp(*argv, "10") == 0) { + config->handler_context.output_config->render_bit_depth = + RENDER_BIT_DEPTH_10; + } else { + return cmd_results_new(CMD_INVALID, + "Invalid bit depth. Must be a value in (8|10)."); + } + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; +} + diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index b27f9ccd..a6d165dc 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { return error; } - config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); + if (strcmp(argv[0], "inverse_outer") == 0) { + config->smart_gaps = SMART_GAPS_INVERSE_OUTER; + } else { + config->smart_gaps = parse_boolean(argv[0], config->smart_gaps) + ? SMART_GAPS_ON : SMART_GAPS_OFF; + } arrange_root(); diff --git a/sway/commands/swap.c b/sway/commands/swap.c index ce5e5128..9355944d 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -126,10 +126,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { } enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; - enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; if (fs1) { container_fullscreen_disable(con1); } + enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; if (fs2) { container_fullscreen_disable(con2); } @@ -247,6 +247,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } else if (!current) { error = cmd_results_new(CMD_FAILURE, "Can only swap with containers and views"); + } else if (current == other) { + error = cmd_results_new(CMD_FAILURE, + "Cannot swap a container with itself"); } else if (container_has_ancestor(current, other) || container_has_ancestor(other, current)) { error = cmd_results_new(CMD_FAILURE, diff --git a/sway/config.c b/sway/config.c index e3daacda..e4745a5c 100644 --- a/sway/config.c +++ b/sway/config.c @@ -266,7 +266,7 @@ static void config_defaults(struct sway_config *config) { config->tiling_drag = true; config->tiling_drag_threshold = 9; - config->smart_gaps = false; + config->smart_gaps = SMART_GAPS_OFF; config->gaps_inner = 0; config->gaps_outer.top = 0; config->gaps_outer.right = 0; @@ -290,6 +290,8 @@ static void config_defaults(struct sway_config *config) { config->hide_edge_borders_smart = ESMART_OFF; config->hide_lone_tab = false; + config->has_focused_tab_title = false; + // border colors color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); color_to_rgba(config->border_colors.focused.background, 0x285577FF); diff --git a/sway/config/bar.c b/sway/config/bar.c index e09add44..d1b342e6 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -219,6 +219,8 @@ static void invoke_swaybar(struct bar_config *bar) { sigprocmask(SIG_SETMASK, &set, NULL); signal(SIGPIPE, SIG_DFL); + restore_nofile_limit(); + pid = fork(); if (pid < 0) { sway_log_errno(SWAY_ERROR, "fork failed"); diff --git a/sway/config/output.c b/sway/config/output.c index 8e937b28..fa509252 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -67,6 +68,7 @@ struct output_config *new_output_config(const char *name) { oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; oc->max_render_time = -1; oc->adaptive_sync = -1; + oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; return oc; } @@ -113,6 +115,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->adaptive_sync != -1) { dst->adaptive_sync = src->adaptive_sync; } + if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + dst->render_bit_depth = src->render_bit_depth; + } if (src->background) { free(dst->background); dst->background = strdup(src->background); @@ -351,9 +356,26 @@ static int compute_default_scale(struct wlr_output *output) { return 2; } +/* Lists of formats to try, in order, when a specific render bit depth has + * been asked for. The second to last format in each list should always + * be XRGB8888, as a reliable backup in case the others are not available; + * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ +static const uint32_t *bit_depth_preferences[] = { + [RENDER_BIT_DEPTH_8] = (const uint32_t []){ + DRM_FORMAT_XRGB8888, + DRM_FORMAT_INVALID, + }, + [RENDER_BIT_DEPTH_10] = (const uint32_t []){ + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_INVALID, + }, +}; + static void queue_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->noop_output) { + if (output == root->fallback_output) { return; } @@ -437,10 +459,26 @@ static void queue_output_config(struct output_config *oc, oc->adaptive_sync); wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); } + + if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; + assert(fmts); + + for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { + wlr_output_set_render_format(wlr_output, fmts[i]); + if (wlr_output_test(wlr_output)) { + break; + } + + sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " + "failed to work, falling back to next in " + "list, 0x%08x", fmts[i], fmts[i + 1]); + } + } } bool apply_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->noop_output) { + if (output == root->fallback_output) { return false; } @@ -535,7 +573,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { } bool test_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->noop_output) { + if (output == root->fallback_output) { return false; } @@ -750,6 +788,8 @@ static bool _spawn_swaybg(char **command) { sway_log_errno(SWAY_ERROR, "fork failed"); return false; } else if (pid == 0) { + restore_nofile_limit(); + pid = fork(); if (pid < 0) { sway_log_errno(SWAY_ERROR, "fork failed"); diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 7f5a337b..27e457f1 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -352,6 +352,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { sway_layer->layer_surface->surface, true); } +static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); + static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, sway_layer, destroy); @@ -360,6 +362,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (sway_layer->layer_surface->mapped) { unmap(sway_layer); } + + struct sway_layer_subsurface *subsurface, *subsurface_tmp; + wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { + layer_subsurface_destroy(subsurface); + } + wl_list_remove(&sway_layer->link); wl_list_remove(&sway_layer->destroy.link); wl_list_remove(&sway_layer->map.link); @@ -428,11 +436,8 @@ static void subsurface_handle_commit(struct wl_listener *listener, void *data) { subsurface_damage(subsurface, false); } -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - +static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { + wl_list_remove(&subsurface->link); wl_list_remove(&subsurface->map.link); wl_list_remove(&subsurface->unmap.link); wl_list_remove(&subsurface->destroy.link); @@ -440,6 +445,13 @@ static void subsurface_handle_destroy(struct wl_listener *listener, free(subsurface); } +static void subsurface_handle_destroy(struct wl_listener *listener, + void *data) { + struct sway_layer_subsurface *subsurface = + wl_container_of(listener, subsurface, destroy); + layer_subsurface_destroy(subsurface); +} + static struct sway_layer_subsurface *create_subsurface( struct wlr_subsurface *wlr_subsurface, struct sway_layer_surface *layer_surface) { @@ -451,6 +463,7 @@ static struct sway_layer_subsurface *create_subsurface( subsurface->wlr_subsurface = wlr_subsurface; subsurface->layer_surface = layer_surface; + wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); subsurface->map.notify = subsurface_handle_map; wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); @@ -624,7 +637,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { output = ws->output; } } - if (!output || output == root->noop_output) { + if (!output || output == root->fallback_output) { if (!root->outputs->length) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", @@ -643,6 +656,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { return; } + wl_list_init(&sway_layer->subsurfaces); + sway_layer->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, &sway_layer->surface_commit); @@ -664,7 +679,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct sway_output *output = layer_surface->output->data; sway_layer->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); + wl_signal_add(&output->events.disable, &sway_layer->output_destroy); wl_list_insert(&output->layers[layer_surface->pending.layer], &sway_layer->link); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index edec71ad..68f095c0 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -633,21 +634,19 @@ static void damage_surface_iterator(struct sway_output *output, struct wlr_box box = *_box; scale_box(&box, output->wlr_output->scale); - if (pixman_region32_not_empty(&surface->buffer_damage)) { - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_surface_get_effective_damage(surface, &damage); - wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceil(output->wlr_output->scale) > surface->current.scale) { - // When scaling up a surface, it'll become blurry so we need to - // expand the damage region - wlr_region_expand(&damage, &damage, - ceil(output->wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box.x, box.y); - wlr_output_damage_add(output->damage, &damage); - pixman_region32_fini(&damage); + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(surface, &damage); + wlr_region_scale(&damage, &damage, output->wlr_output->scale); + if (ceil(output->wlr_output->scale) > surface->current.scale) { + // When scaling up a surface, it'll become blurry so we need to + // expand the damage region + wlr_region_expand(&damage, &damage, + ceil(output->wlr_output->scale) - surface->current.scale); } + pixman_region32_translate(&damage, box.x, box.y); + wlr_output_damage_add(output->damage, &damage); + pixman_region32_fini(&damage); if (whole) { wlr_output_damage_add_box(output->damage, &box); @@ -733,7 +732,7 @@ static void update_output_manager_config(struct sway_server *server) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output == root->noop_output) { + if (output == root->fallback_output) { continue; } struct wlr_output_configuration_head_v1 *config_head = @@ -755,18 +754,22 @@ static void update_output_manager_config(struct sway_server *server) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_server *server = output->server; - wl_signal_emit(&output->events.destroy, output); + output_begin_destroy(output); if (output->enabled) { output_disable(output); } - output_begin_destroy(output); + + wl_list_remove(&output->link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + output->wlr_output->data = NULL; + output->wlr_output = NULL; + transaction_commit_dirty(); update_output_manager_config(server); @@ -835,9 +838,22 @@ static void handle_present(struct wl_listener *listener, void *data) { output->refresh_nsec = output_event->refresh; } +static unsigned int last_headless_num = 0; + void handle_new_output(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + + if (wlr_output == root->fallback_output->wlr_output) { + return; + } + + if (wlr_output_is_headless(wlr_output)) { + char name[64]; + snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); + wlr_output_set_name(wlr_output, name); + } + sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", wlr_output, wlr_output->name, wlr_output->non_desktop); @@ -850,6 +866,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } + if (!wlr_output_init_render(wlr_output, server->allocator, + server->renderer)) { + sway_log(SWAY_ERROR, "Failed to init output render"); + return; + } + struct sway_output *output = output_create(wlr_output); if (!output) { return; diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 17fc8f6f..c088c936 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -52,7 +52,7 @@ static int scale_length(int length, int offset, float scale) { static void scissor_output(struct wlr_output *wlr_output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = wlr_output->renderer; assert(renderer); struct wlr_box box = { @@ -100,8 +100,7 @@ static void render_texture(struct wlr_output *wlr_output, pixman_region32_t *output_damage, struct wlr_texture *texture, const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], float alpha) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = wlr_output->renderer; struct sway_output *output = wlr_output->data; pixman_region32_t damage; @@ -218,8 +217,7 @@ void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]) { struct wlr_output *wlr_output = output->wlr_output; - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = wlr_output->renderer; struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); @@ -764,6 +762,14 @@ static void render_containers_linear(struct sway_output *output, } } +static bool container_is_focused(struct sway_container *con, void *data) { + return con->current.focused; +} + +static bool container_has_focused_child(struct sway_container *con) { + return container_find_child(con, container_is_focused, NULL); +} + /** * Render a container's children using the L_TABBED layout. */ @@ -795,6 +801,10 @@ static void render_containers_tabbed(struct sway_output *output, colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = child->marks_focused; + } else if (config->has_focused_tab_title && container_has_focused_child(child)) { + colors = &config->border_colors.focused_tab_title; + title_texture = child->title_focused_tab_title; + marks_texture = child->marks_focused_tab_title; } else if (child == parent->active_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; @@ -860,7 +870,11 @@ static void render_containers_stacked(struct sway_output *output, colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = child->marks_focused; - } else if (child == parent->active_child) { + } else if (config->has_focused_tab_title && container_has_focused_child(child)) { + colors = &config->border_colors.focused_tab_title; + title_texture = child->title_focused_tab_title; + marks_texture = child->marks_focused_tab_title; + } else if (child == parent->active_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = child->marks_focused_inactive; @@ -1013,13 +1027,7 @@ static void render_seatops(struct sway_output *output, void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; - - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - if (!sway_assert(renderer != NULL, - "expected the output backend to have a renderer")) { - return; - } + struct wlr_renderer *renderer = output->server->renderer; struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index b1f3fb32..f5a3a053 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -402,7 +402,7 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_node *node = instruction->node; - bool hidden = node_is_view(node) && + bool hidden = node_is_view(node) && !node->destroying && !view_is_visible(node->sway_container->view); if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index c1e5bc68..5fae8296 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -72,8 +72,8 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = output->lx - view->container->pending.content_x, - .y = output->ly - view->container->pending.content_y, + .x = output->lx - view->container->pending.content_x + view->geometry.x, + .y = output->ly - view->container->pending.content_y + view->geometry.y, .width = output->width, .height = output->height, }; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 1af8d248..40288f97 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -436,6 +436,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->commit.link); } + xwayland_view->view.wlr_xwayland_surface = NULL; + wl_list_remove(&xwayland_view->destroy.link); wl_list_remove(&xwayland_view->request_configure.link); wl_list_remove(&xwayland_view->request_fullscreen.link); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 1e3e16d6..6fddee90 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -83,7 +83,28 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - // check for unmanaged views first + // find the output the cursor is on + struct wlr_output *wlr_output = wlr_output_layout_output_at( + root->output_layout, lx, ly); + if (wlr_output == NULL) { + return NULL; + } + struct sway_output *output = wlr_output->data; + if (!output || !output->enabled) { + // output is being destroyed or is being enabled + return NULL; + } + double ox = lx, oy = ly; + wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); + + // layer surfaces on the overlay layer are rendered on top + if ((*surface = layer_surface_at(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + ox, oy, sx, sy))) { + return NULL; + } + + // check for unmanaged views #if HAVE_XWAYLAND struct wl_list *unmanaged = &root->xwayland_unmanaged; struct sway_xwayland_unmanaged *unmanaged_surface; @@ -101,19 +122,6 @@ struct sway_node *node_at_coords( } } #endif - // find the output the cursor is on - struct wlr_output *wlr_output = wlr_output_layout_output_at( - root->output_layout, lx, ly); - if (wlr_output == NULL) { - return NULL; - } - struct sway_output *output = wlr_output->data; - if (!output || !output->enabled) { - // output is being destroyed or is being enabled - return NULL; - } - double ox = lx, oy = ly; - wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); if (root->fullscreen_global) { // Try fullscreen container @@ -131,11 +139,6 @@ struct sway_node *node_at_coords( return NULL; } - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - ox, oy, sx, sy))) { - return NULL; - } if (ws->fullscreen) { // Try transient containers for (int i = 0; i < ws->floating->length; ++i) { @@ -924,6 +927,7 @@ static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_begin); struct wlr_event_pointer_pinch_begin *event = data; + cursor_handle_activity_from_device(cursor, event->device); wlr_pointer_gestures_v1_send_pinch_begin( cursor->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); @@ -933,6 +937,7 @@ static void handle_pointer_pinch_update(struct wl_listener *listener, void *data struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_update); struct wlr_event_pointer_pinch_update *event = data; + cursor_handle_activity_from_device(cursor, event->device); wlr_pointer_gestures_v1_send_pinch_update( cursor->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->dx, event->dy, @@ -943,6 +948,7 @@ static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_end); struct wlr_event_pointer_pinch_end *event = data; + cursor_handle_activity_from_device(cursor, event->device); wlr_pointer_gestures_v1_send_pinch_end( cursor->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); @@ -952,6 +958,7 @@ static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_begin); struct wlr_event_pointer_swipe_begin *event = data; + cursor_handle_activity_from_device(cursor, event->device); wlr_pointer_gestures_v1_send_swipe_begin( cursor->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); @@ -961,6 +968,7 @@ static void handle_pointer_swipe_update(struct wl_listener *listener, void *data struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_update); struct wlr_event_pointer_swipe_update *event = data; + cursor_handle_activity_from_device(cursor, event->device); wlr_pointer_gestures_v1_send_swipe_update( cursor->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->dx, event->dy); @@ -970,6 +978,7 @@ static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_end); struct wlr_event_pointer_swipe_end *event = data; + cursor_handle_activity_from_device(cursor, event->device); wlr_pointer_gestures_v1_send_swipe_end( cursor->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); diff --git a/sway/input/seat.c b/sway/input/seat.c index c5c8459e..ce933b66 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -51,6 +51,16 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { static void seat_node_destroy(struct sway_seat_node *seat_node) { wl_list_remove(&seat_node->destroy.link); wl_list_remove(&seat_node->link); + + /* + * This is the only time we remove items from the focus stack without + * immediately re-adding them. If we just removed the last thing, + * mark that nothing has focus anymore. + */ + if (wl_list_empty(&seat_node->seat->focus_stack)) { + seat_node->seat->has_focus = false; + } + free(seat_node); } @@ -1415,9 +1425,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; } - if (wl_list_empty(&seat->focus_stack)) { - return NULL; - } + sway_assert(!wl_list_empty(&seat->focus_stack), + "focus_stack is empty, but has_focus is true"); struct sway_seat_node *current = wl_container_of(seat->focus_stack.next, current, link); return current->node; diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 8400a4b3..df683026 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -80,17 +80,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { double height = e->ref_height + grow_height; int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, - &min_height, &max_height); - width = fmax(min_width + border_width, fmin(width, max_width)); - height = fmax(min_height + border_height, fmin(height, max_height)); + &min_height, &max_height); + width = fmin(width, max_width - border_width); + width = fmax(width, min_width + border_width); + width = fmax(width, 1); + height = fmin(height, max_height - border_height); + height = fmax(height, min_height + border_height); + height = fmax(height, 1); // Apply the view's min/max size if (con->view) { double view_min_width, view_max_width, view_min_height, view_max_height; view_get_constraints(con->view, &view_min_width, &view_max_width, &view_min_height, &view_max_height); - width = fmax(view_min_width + border_width, fmin(width, view_max_width)); - height = fmax(view_min_height + border_height, fmin(height, view_max_height)); + width = fmin(width, view_max_width - border_width); + width = fmax(width, view_min_width + border_width); + width = fmax(width, 1); + height = fmin(height, view_max_height - border_height); + height = fmax(height, view_min_height + border_height); + height = fmax(height, 1); } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index aad9a7b5..1bf5a05f 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -687,7 +687,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (!output->enabled && output != root->noop_output) { + if (!output->enabled && output != root->fallback_output) { json_object_array_add(outputs, ipc_json_describe_disabled_output(output)); } diff --git a/sway/main.c b/sway/main.c index e960c4e2..b6f8a8bf 100644 --- a/sway/main.c +++ b/sway/main.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ static bool terminate_request = false; static int exit_value = 0; +static struct rlimit original_nofile_rlimit = {0}; struct sway_server server = {0}; struct sway_debug debug = {0}; @@ -63,7 +65,7 @@ void detect_proprietary(int allow_unsupported_gpu) { sway_log(SWAY_ERROR, "Proprietary Nvidia drivers are NOT supported. " "Use Nouveau. To launch sway anyway, launch with " - "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); + "--unsupported-gpu and DO NOT report issues."); exit(EXIT_FAILURE); } break; @@ -151,6 +153,9 @@ static void log_kernel(void) { static bool drop_permissions(void) { if (getuid() != geteuid() || getgid() != getegid()) { + sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: " + "SUID privilege drop will be removed in a future release, please migrate to seatd-launch"); + // Set the gid and uid in the correct order. if (setgid(getgid()) != 0) { sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); @@ -169,6 +174,33 @@ static bool drop_permissions(void) { return true; } +static void increase_nofile_limit(void) { + if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { + sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " + "getrlimit(NOFILE) failed"); + return; + } + + struct rlimit new_rlimit = original_nofile_rlimit; + new_rlimit.rlim_cur = new_rlimit.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) { + sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " + "setrlimit(NOFILE) failed"); + sway_log(SWAY_INFO, "Running with %d max open files", + (int)original_nofile_rlimit.rlim_cur); + } +} + +void restore_nofile_limit(void) { + if (original_nofile_rlimit.rlim_cur == 0) { + return; + } + if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { + sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " + "setrlimit(NOFILE) failed"); + } +} + void enable_debug_flag(const char *flag) { if (strcmp(flag, "damage=highlight") == 0) { debug.damage = DAMAGE_HIGHLIGHT; @@ -220,7 +252,6 @@ int main(int argc, char **argv) { {"verbose", no_argument, NULL, 'V'}, {"get-socketpath", no_argument, NULL, 'p'}, {"unsupported-gpu", no_argument, NULL, 'u'}, - {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'}, {0, 0, 0, 0} }; @@ -314,7 +345,6 @@ int main(int argc, char **argv) { log_kernel(); log_distro(); log_env(); - detect_proprietary(allow_unsupported_gpu); if (optind < argc) { // Behave as IPC client if (optind != 1) { @@ -341,6 +371,8 @@ int main(int argc, char **argv) { return 0; } + detect_proprietary(allow_unsupported_gpu); + if (!server_privileged_prepare(&server)) { return 1; } @@ -350,6 +382,8 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } + increase_nofile_limit(); + // handle SIGTERM signals signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); diff --git a/sway/meson.build b/sway/meson.build index 1402db15..8eab31a2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -188,6 +188,7 @@ sway_sources = files( 'commands/output/max_render_time.c', 'commands/output/mode.c', 'commands/output/position.c', + 'commands/output/render_bit_depth.c', 'commands/output/scale.c', 'commands/output/scale_filter.c', 'commands/output/subpixel.c', diff --git a/sway/server.c b/sway/server.c index b187fcd5..f50a0987 100644 --- a/sway/server.c +++ b/sway/server.c @@ -7,17 +7,18 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -73,12 +74,29 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) { bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); - struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); - assert(renderer); + server->renderer = wlr_renderer_autocreate(server->backend); + if (!server->renderer) { + sway_log(SWAY_ERROR, "Failed to create renderer"); + return false; + } - wlr_renderer_init_wl_display(renderer, server->wl_display); + wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - server->compositor = wlr_compositor_create(server->wl_display, renderer); + if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { + wlr_drm_create(server->wl_display, server->renderer); + server->linux_dmabuf_v1 = + wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer); + } + + server->allocator = wlr_allocator_autocreate(server->backend, + server->renderer); + if (!server->allocator) { + sway_log(SWAY_ERROR, "Failed to create allocator"); + return false; + } + + server->compositor = wlr_compositor_create(server->wl_display, + server->renderer); server->compositor_new_surface.notify = handle_compositor_new_surface; wl_signal_add(&server->compositor->events.new_surface, &server->compositor_new_surface); @@ -206,20 +224,20 @@ bool server_init(struct sway_server *server) { return false; } - server->noop_backend = wlr_noop_backend_create(server->wl_display); - - struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend); - root->noop_output = output_create(wlr_output); - - server->headless_backend = - wlr_headless_backend_create_with_renderer(server->wl_display, renderer); + server->headless_backend = wlr_headless_backend_create(server->wl_display); if (!server->headless_backend) { - sway_log(SWAY_INFO, "Failed to create secondary headless backend, " - "starting without it"); + sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); + wlr_backend_destroy(server->backend); + return false; } else { wlr_multi_backend_add(server->backend, server->headless_backend); } + struct wlr_output *wlr_output = + wlr_headless_add_output(server->headless_backend, 800, 600); + wlr_output_set_name(wlr_output, "FALLBACK"); + root->fallback_output = output_create(wlr_output); + // This may have been set already via -Dtxn-timeout if (!server->txn_timeout_ms) { server->txn_timeout_ms = 200; @@ -276,6 +294,7 @@ bool server_start(struct sway_server *server) { wlr_backend_destroy(server->backend); return false; } + return true; } diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 55d8f719..4159a851 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -157,6 +157,21 @@ must be separated by one space. For example: adaptive sync can improve latency, but can cause flickering on some hardware. +*output* render_bit_depth 8|10 + Controls the color channel bit depth at which frames are rendered; the + default is currently 8 bits per channel. + + Setting higher values will not have an effect if hardware and software lack + support for such bit depths. Successfully increasing the render bit depth + will not necessarily increase the bit depth of the frames sent to a display. + An increased render bit depth may provide smoother rendering of gradients, + and screenshots which can more precisely store the colors of programs + which display high bit depth colors. + + Warnings: this can break screenshot/screencast programs which have not been + updated to work with different bit depths. This command is experimental, + and may be removed or changed in the future. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index e8d3d606..641d0925 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -499,6 +499,12 @@ runtime. *client.focused_inactive* The most recently focused view within a container which is not focused. + *client.focused_tab_title* + A view that has focused descendant container. + Tab or stack container title that is the parent of the focused container + but is not directly focused. Defaults to focused_inactive if not + specified and does not use the indicator and child_border colors. + *client.placeholder* Ignored (present for i3 compatibility). @@ -554,6 +560,12 @@ The default colors are: : #ffffff : #484e50 : #5f676a +| *focused_tab_title* +: #333333 +: #5f676a +: #ffffff +: n/a +: n/a | *unfocused* : #333333 : #222222 @@ -692,9 +704,10 @@ The default colors are: borders will only be enabled if the workspace has more than one visible child and gaps equal to zero. -*smart_gaps* on|off +*smart_gaps* on|off|toggle|inverse_outer If smart_gaps are _on_ gaps will only be enabled if a workspace has more - than one child. + than one child. If smart_gaps are _inverse_outer_ outer gaps will only + be enabled if a workspace has exactly one child. *mark* --add|--replace [--toggle] Marks are arbitrary labels that can be used to identify certain windows and diff --git a/sway/swaynag.c b/sway/swaynag.c index ba582989..4a0a6d30 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -64,6 +64,8 @@ bool swaynag_spawn(const char *swaynag_command, sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); goto failed; } else if (pid == 0) { + restore_nofile_limit(); + pid = fork(); if (pid < 0) { sway_log_errno(SWAY_ERROR, "fork failed"); diff --git a/sway/tree/container.c b/sway/tree/container.c index 6a01eab3..79e04ec0 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -5,8 +5,12 @@ #include #include #include +#include #include +#include #include +#include +#include "linux-dmabuf-unstable-v1-protocol.h" #include "cairo_util.h" #include "pango.h" #include "sway/config.h" @@ -64,6 +68,7 @@ void container_destroy(struct sway_container *con) { wlr_texture_destroy(con->title_focused_inactive); wlr_texture_destroy(con->title_unfocused); wlr_texture_destroy(con->title_urgent); + wlr_texture_destroy(con->title_focused_tab_title); list_free(con->pending.children); list_free(con->current.children); list_free(con->outputs); @@ -73,11 +78,10 @@ void container_destroy(struct sway_container *con) { wlr_texture_destroy(con->marks_focused_inactive); wlr_texture_destroy(con->marks_unfocused); wlr_texture_destroy(con->marks_urgent); + wlr_texture_destroy(con->marks_focused_tab_title); - if (con->view) { - if (con->view->container == con) { - con->view->container = NULL; - } + if (con->view && con->view->container == con) { + con->view->container = NULL; if (con->view->destroying) { view_destroy(con->view); } @@ -382,19 +386,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent, } static bool surface_is_popup(struct wlr_surface *surface) { - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - return true; - } - xdg_surface = xdg_surface->toplevel->parent; + while (!wlr_surface_is_xdg_surface(surface)) { + if (!wlr_surface_is_subsurface(surface)) { + return false; } - return false; + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + surface = subsurface->parent; } - - return false; + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; } struct sway_container *container_at(struct sway_workspace *workspace, @@ -532,6 +534,13 @@ static void render_titlebar_text_texture(struct sway_output *output, cairo_surface_t *surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height); + cairo_status_t status = cairo_surface_status(surface); + if (status != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", + cairo_status_to_string(status)); + return; + } + cairo_t *cairo = cairo_create(surface); cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); cairo_set_font_options(cairo, fo); @@ -549,8 +558,7 @@ static void render_titlebar_text_texture(struct sway_output *output, cairo_surface_flush(surface); unsigned char *data = cairo_image_surface_get_data(surface); int stride = cairo_image_surface_get_stride(surface); - struct wlr_renderer *renderer = wlr_backend_get_renderer( - output->wlr_output->backend); + struct wlr_renderer *renderer = output->wlr_output->renderer; *texture = wlr_texture_from_pixels( renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); cairo_surface_destroy(surface); @@ -585,6 +593,8 @@ void container_update_title_textures(struct sway_container *container) { &config->border_colors.unfocused); update_title_texture(container, &container->title_urgent, &config->border_colors.urgent); + update_title_texture(container, &container->title_focused_tab_title, + &config->border_colors.focused_tab_title); container_damage_whole(container); } @@ -1050,6 +1060,16 @@ void container_end_mouse_operation(struct sway_container *container) { } } +static bool devid_from_fd(int fd, dev_t *devid) { + struct stat stat; + if (fstat(fd, &stat) != 0) { + sway_log_errno(SWAY_ERROR, "fstat failed"); + return false; + } + *devid = stat.st_rdev; + return true; +} + static void set_fullscreen(struct sway_container *con, bool enable) { if (!con->view) { return; @@ -1061,6 +1081,75 @@ static void set_fullscreen(struct sway_container *con, bool enable) { con->view->foreign_toplevel, enable); } } + + if (!server.linux_dmabuf_v1 || !con->view->surface) { + return; + } + if (!enable) { + wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, + con->view->surface, NULL); + return; + } + + if (!con->pending.workspace || !con->pending.workspace->output) { + return; + } + + struct sway_output *output = con->pending.workspace->output; + struct wlr_output *wlr_output = output->wlr_output; + + // TODO: add wlroots helpers for all of this stuff + + const struct wlr_drm_format_set *renderer_formats = + wlr_renderer_get_dmabuf_texture_formats(server.renderer); + assert(renderer_formats); + + int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer); + int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); + if (renderer_drm_fd < 0 || backend_drm_fd < 0) { + return; + } + + dev_t render_dev, scanout_dev; + if (!devid_from_fd(renderer_drm_fd, &render_dev) || + !devid_from_fd(backend_drm_fd, &scanout_dev)) { + return; + } + + const struct wlr_drm_format_set *output_formats = + wlr_output_get_primary_formats(output->wlr_output, + WLR_BUFFER_CAP_DMABUF); + if (!output_formats) { + return; + } + + struct wlr_drm_format_set scanout_formats = {0}; + if (!wlr_drm_format_set_intersect(&scanout_formats, + output_formats, renderer_formats)) { + return; + } + + struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { + { + .target_device = scanout_dev, + .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, + .formats = &scanout_formats, + }, + { + .target_device = render_dev, + .formats = renderer_formats, + }, + }; + + const struct wlr_linux_dmabuf_feedback_v1 feedback = { + .main_device = render_dev, + .tranches = tranches, + .tranches_len = sizeof(tranches) / sizeof(tranches[0]), + }; + wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, + con->view->surface, &feedback); + + wlr_drm_format_set_finish(&scanout_formats); } static void container_fullscreen_workspace(struct sway_container *con) { @@ -1638,6 +1727,8 @@ void container_update_marks_textures(struct sway_container *con) { &config->border_colors.unfocused); update_marks_texture(con, &con->marks_urgent, &config->border_colors.urgent); + update_marks_texture(con, &con->marks_focused_tab_title, + &config->border_colors.focused_tab_title); container_damage_whole(con); } diff --git a/sway/tree/output.c b/sway/tree/output.c index c095dce0..ad8d2482 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -56,8 +56,8 @@ static void restore_workspaces(struct sway_output *output) { } // Saved workspaces - while (root->noop_output->workspaces->length) { - struct sway_workspace *ws = root->noop_output->workspaces->items[0]; + while (root->fallback_output->workspaces->length) { + struct sway_workspace *ws = root->fallback_output->workspaces->items[0]; workspace_detach(ws); output_add_workspace(output, ws); @@ -95,7 +95,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { output->detected_subpixel = wlr_output->subpixel; output->scale_filter = SCALE_FILTER_NEAREST; - wl_signal_init(&output->events.destroy); + wl_signal_init(&output->events.disable); wl_list_insert(&root->all_outputs, &output->link); @@ -192,7 +192,7 @@ static void output_evacuate(struct sway_output *output) { new_output = fallback_output; } if (!new_output) { - new_output = root->noop_output; + new_output = root->fallback_output; } struct sway_workspace *new_output_ws = @@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) { } sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); - wl_signal_emit(&output->events.destroy, output); + wl_signal_emit(&output->events.disable, output); output_evacuate(output); @@ -286,13 +286,10 @@ void output_begin_destroy(struct sway_output *output) { return; } sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); + wl_signal_emit(&output->node.events.destroy, &output->node); output->node.destroying = true; node_set_dirty(&output->node); - - wl_list_remove(&output->link); - output->wlr_output->data = NULL; - output->wlr_output = NULL; } struct sway_output *output_from_wlr_output(struct wlr_output *output) { diff --git a/sway/tree/root.c b/sway/tree/root.c index dd4d8e33..73f3993c 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -374,8 +374,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), } // Saved workspaces - for (int i = 0; i < root->noop_output->workspaces->length; ++i) { - struct sway_workspace *ws = root->noop_output->workspaces->items[i]; + for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { + struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; workspace_for_each_container(ws, f, data); } } @@ -427,8 +427,8 @@ struct sway_container *root_find_container( } // Saved workspaces - for (int i = 0; i < root->noop_output->workspaces->length; ++i) { - struct sway_workspace *ws = root->noop_output->workspaces->items[i]; + for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { + struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; if ((result = workspace_find_container(ws, test, data))) { return result; } diff --git a/sway/tree/view.c b/sway/tree/view.c index ed8c50f8..8b7061ba 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -752,10 +752,29 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) - : seat_get_focus_inactive(seat, &root->node); - struct sway_container *target_sibling = node->type == N_CONTAINER ? - node->sway_container : NULL; + struct sway_node *node = + seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); + struct sway_container *target_sibling = NULL; + if (node && node->type == N_CONTAINER) { + if (container_is_floating(node->sway_container)) { + // If we're about to launch the view into the floating container, then + // launch it as a tiled view instead. + if (ws) { + target_sibling = seat_get_focus_inactive_tiling(seat, ws); + if (target_sibling) { + struct sway_container *con = + seat_get_focus_inactive_view(seat, &target_sibling->node); + if (con) { + target_sibling = con; + } + } + } else { + ws = seat_get_last_known_workspace(seat); + } + } else { + target_sibling = node->sway_container; + } + } view->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); @@ -776,13 +795,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, &view->foreign_minimize); - // If we're about to launch the view into the floating container, then - // launch it as a tiled view in the root of the workspace instead. - if (target_sibling && container_is_floating(target_sibling)) { - target_sibling = NULL; - ws = seat_get_last_known_workspace(seat); - } - struct sway_container *container = view->container; if (target_sibling) { container_add_sibling(target_sibling, container, 1); @@ -1129,9 +1141,12 @@ void view_child_init(struct sway_view_child *child, wl_signal_add(&view->events.unmap, &child->view_unmap); child->view_unmap.notify = view_child_handle_view_unmap; - struct sway_workspace *workspace = child->view->container->pending.workspace; - if (workspace) { - wlr_surface_send_enter(child->surface, workspace->output->wlr_output); + struct sway_container *container = child->view->container; + if (container != NULL) { + struct sway_workspace *workspace = container->pending.workspace; + if (workspace) { + wlr_surface_send_enter(child->surface, workspace->output->wlr_output); + } } view_child_init_subsurfaces(child, surface); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 8dd7789d..c84320bd 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -50,8 +50,8 @@ struct sway_output *workspace_get_initial_output(const char *name) { } else if (focus && focus->type == N_CONTAINER) { return focus->sway_container->pending.workspace->output; } - // Fallback to the first output or noop output for headless - return root->outputs->length ? root->outputs->items[0] : root->noop_output; + // Fallback to the first output or the headless output + return root->outputs->length ? root->outputs->items[0] : root->fallback_output; } struct sway_workspace *workspace_create(struct sway_output *output, @@ -844,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, return con; } +bool workspace_has_single_visible_container(struct sway_workspace *ws) { + struct sway_seat *seat = input_manager_get_default_seat(); + struct sway_container *focus = + seat_get_focus_inactive_tiling(seat, ws); + if (focus && !focus->view) { + focus = seat_get_focus_inactive_view(seat, &focus->node); + } + return (focus && focus->view && view_ancestor_is_only_visible(focus->view)); +} + void workspace_add_gaps(struct sway_workspace *ws) { - if (config->smart_gaps) { - struct sway_seat *seat = input_manager_get_default_seat(); - struct sway_container *focus = - seat_get_focus_inactive_tiling(seat, ws); - if (focus && !focus->view) { - focus = seat_get_focus_inactive_view(seat, &focus->node); - } - if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { - ws->current_gaps.top = 0; - ws->current_gaps.right = 0; - ws->current_gaps.bottom = 0; - ws->current_gaps.left = 0; - return; - } + if (config->smart_gaps == SMART_GAPS_ON + && workspace_has_single_visible_container(ws)) { + ws->current_gaps.top = 0; + ws->current_gaps.right = 0; + ws->current_gaps.bottom = 0; + ws->current_gaps.left = 0; + return; + } + + if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER + && !workspace_has_single_visible_container(ws)) { + ws->current_gaps.top = 0; + ws->current_gaps.right = 0; + ws->current_gaps.bottom = 0; + ws->current_gaps.left = 0; + } else { + ws->current_gaps = ws->gaps_outer; } - ws->current_gaps = ws->gaps_outer; // Add inner gaps and make sure we don't turn out negative ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); diff --git a/swaybar/bar.c b/swaybar/bar.c index 15eab782..6ffdc9b4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -54,7 +54,6 @@ static void swaybar_output_free(struct swaybar_output *output) { if (output->input_region != NULL) { wl_region_destroy(output->input_region); } - zxdg_output_v1_destroy(output->xdg_output); wl_output_destroy(output->output); destroy_buffer(&output->buffers[0]); destroy_buffer(&output->buffers[1]); @@ -172,7 +171,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) { if (bar->status) { sway_log(SWAY_DEBUG, "Sending %s signal to status command", visible ? "cont" : "stop"); - kill(bar->status->pid, visible ? + kill(-bar->status->pid, visible ? bar->status->cont_signal : bar->status->stop_signal); } } diff --git a/swaybar/ipc.c b/swaybar/ipc.c index a64aa1ab..2cb235bf 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -550,7 +550,7 @@ bool handle_ipc_readable(struct swaybar *bar) { // The default depth of 32 is too small to represent some nested layouts, but // we can't pass INT_MAX here because json-c (as of this writing) prefaults // all the memory for its stack. - json_tokener *tok = json_tokener_new_ex(256); + json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); if (!tok) { sway_log_errno(SWAY_ERROR, "failed to create tokener"); free_ipc_response(resp); diff --git a/swaybar/status_line.c b/swaybar/status_line.c index ecd91032..2e9bb7f1 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -117,11 +117,11 @@ bool status_handle_readable(struct status_line *status) { status->text = status->buffer; // intentional fall-through case PROTOCOL_TEXT: - errno = 0; while (true) { if (status->buffer[read_bytes - 1] == '\n') { status->buffer[read_bytes - 1] = '\0'; } + errno = 0; read_bytes = getline(&status->buffer, &status->buffer_size, status->read); if (errno == EAGAIN) { @@ -157,7 +157,12 @@ struct status_line *status_line_init(char *cmd) { assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " " starting `status-command`; WAYLAND_SOCKET should not be set"); status->pid = fork(); - if (status->pid == 0) { + if (status->pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + exit(1); + } else if (status->pid == 0) { + setpgid(0, 0); + dup2(pipe_read_fd[1], STDOUT_FILENO); close(pipe_read_fd[0]); close(pipe_read_fd[1]); @@ -185,8 +190,8 @@ struct status_line *status_line_init(char *cmd) { void status_line_free(struct status_line *status) { status_line_close_fds(status); - kill(status->pid, status->cont_signal); - kill(status->pid, SIGTERM); + kill(-status->pid, status->cont_signal); + kill(-status->pid, SIGTERM); waitpid(status->pid, NULL, 0); if (status->protocol == PROTOCOL_I3BAR) { struct i3bar_block *block, *tmp; diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 19f4beac..6d4b17bf 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -493,24 +493,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, cairo_destroy(cairo_icon); } - int padded_size = icon_size + 2*padding; - *x -= padded_size; - int y = floor((height - padded_size) / 2.0); + double descaled_padding = (double)padding / output->scale; + double descaled_icon_size = (double)icon_size / output->scale; + + int size = descaled_icon_size + 2 * descaled_padding; + *x -= size; + int icon_y = floor((output->height - size) / 2.0); cairo_operator_t op = cairo_get_operator(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cairo, icon, *x + padding, y + padding); - cairo_rectangle(cairo, *x, y, padded_size, padded_size); + + cairo_matrix_t scale_matrix; + cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon); + // TODO: check cairo_pattern_status for "ENOMEM" + cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale); + cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding)); + cairo_pattern_set_matrix(icon_pattern, &scale_matrix); + cairo_set_source(cairo, icon_pattern); + cairo_rectangle(cairo, *x, icon_y, size, size); cairo_fill(cairo); + cairo_set_operator(cairo, op); + cairo_pattern_destroy(icon_pattern); cairo_surface_destroy(icon); struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); hotspot->x = *x; hotspot->y = 0; - hotspot->width = height; - hotspot->height = height; + hotspot->width = size; + hotspot->height = output->height; hotspot->callback = icon_hotspot_callback; hotspot->destroy = free; hotspot->data = strdup(sni->watcher_id); diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 5fe6f9c3..b0545f4a 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -116,8 +116,8 @@ uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { } } // else display on all - if ((int) output->height*output->scale <= 2*config->tray_padding) { - return 2*config->tray_padding + 1; + if ((int)(output->height * output->scale) <= 2 * config->tray_padding) { + return (2 * config->tray_padding + 1) / output->scale; } uint32_t max_height = 0; diff --git a/swaymsg/main.c b/swaymsg/main.c index 574d3b75..0d9dc5a0 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -1,4 +1,6 @@ #define _POSIX_C_SOURCE 200809L + +#include #include #include #include @@ -480,12 +482,20 @@ int main(int argc, char **argv) { char *resp = ipc_single_command(socketfd, type, command, &len); // pretty print the json - json_object *obj = json_tokener_parse(resp); - if (obj == NULL) { + json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); + if (tok == NULL) { + if (quiet) { + exit(EXIT_FAILURE); + } + sway_abort("failed allocating json_tokener"); + } + json_object *obj = json_tokener_parse_ex(tok, resp, -1); + enum json_tokener_error err = json_tokener_get_error(tok); + json_tokener_free(tok); + if (obj == NULL || err != json_tokener_success) { if (!quiet) { - fprintf(stderr, "ERROR: Could not parse json response from ipc. " - "This is a bug in sway."); - printf("%s\n", resp); + sway_log(SWAY_ERROR, "failed to parse payload as json: %s", + json_tokener_error_desc(err)); } ret = 1; } else { @@ -517,13 +527,22 @@ int main(int argc, char **argv) { break; } - json_object *obj = json_tokener_parse(reply->payload); - if (obj == NULL) { - if (!quiet) { - fprintf(stderr, "ERROR: Could not parse json response from" - " ipc. This is a bug in sway."); - ret = 1; + json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); + if (tok == NULL) { + if (quiet) { + exit(EXIT_FAILURE); } + sway_abort("failed allocating json_tokener"); + } + json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1); + enum json_tokener_error err = json_tokener_get_error(tok); + json_tokener_free(tok); + if (obj == NULL || err != json_tokener_success) { + if (!quiet) { + sway_log(SWAY_ERROR, "failed to parse payload as json: %s", + json_tokener_error_desc(err)); + } + ret = 1; break; } else if (quiet) { json_object_put(obj); diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 6d4a7a58..9b57d578 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -307,33 +307,25 @@ static void output_scale(void *data, struct wl_output *output, } } +static void output_name(void *data, struct wl_output *output, + const char *name) { + struct swaynag_output *swaynag_output = data; + swaynag_output->name = strdup(name); + + const char *outname = swaynag_output->swaynag->type->output; + if (!swaynag_output->swaynag->output && outname && + strcmp(outname, name) == 0) { + sway_log(SWAY_DEBUG, "Using output %s", name); + swaynag_output->swaynag->output = swaynag_output; + } +} + static const struct wl_output_listener output_listener = { .geometry = nop, .mode = nop, .done = nop, .scale = output_scale, -}; - -static void xdg_output_handle_name(void *data, - struct zxdg_output_v1 *xdg_output, const char *name) { - struct swaynag_output *swaynag_output = data; - char *outname = swaynag_output->swaynag->type->output; - sway_log(SWAY_DEBUG, "Checking against output %s for %s", name, outname); - if (!swaynag_output->swaynag->output && outname && name - && strcmp(outname, name) == 0) { - sway_log(SWAY_DEBUG, "Using output %s", name); - swaynag_output->swaynag->output = swaynag_output; - } - swaynag_output->name = strdup(name); - zxdg_output_v1_destroy(xdg_output); - swaynag_output->swaynag->querying_outputs--; -} - -static const struct zxdg_output_v1_listener xdg_output_listener = { - .logical_position = nop, - .logical_size = nop, - .done = nop, - .name = xdg_output_handle_name, + .name = output_name, .description = nop, }; @@ -361,33 +353,21 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_shm_interface.name) == 0) { swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - if (!swaynag->output && swaynag->xdg_output_manager) { - swaynag->querying_outputs++; + if (!swaynag->output) { struct swaynag_output *output = calloc(1, sizeof(struct swaynag_output)); output->wl_output = wl_registry_bind(registry, name, - &wl_output_interface, 3); + &wl_output_interface, 4); output->wl_name = name; output->scale = 1; output->swaynag = swaynag; wl_list_insert(&swaynag->outputs, &output->link); wl_output_add_listener(output->wl_output, &output_listener, output); - - struct zxdg_output_v1 *xdg_output; - xdg_output = zxdg_output_manager_v1_get_xdg_output( - swaynag->xdg_output_manager, output->wl_output); - zxdg_output_v1_add_listener(xdg_output, - &xdg_output_listener, output); } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { swaynag->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); - } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 - && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { - swaynag->xdg_output_manager = wl_registry_bind(registry, name, - &zxdg_output_manager_v1_interface, - ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); } } @@ -453,12 +433,11 @@ void swaynag_setup(struct swaynag *swaynag) { assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); - while (swaynag->querying_outputs > 0) { - if (wl_display_roundtrip(swaynag->display) < 0) { - sway_log(SWAY_ERROR, "Error during outputs init."); - swaynag_destroy(swaynag); - exit(EXIT_FAILURE); - } + // Second roundtrip to get wl_output properties + if (wl_display_roundtrip(swaynag->display) < 0) { + sway_log(SWAY_ERROR, "Error during outputs init."); + swaynag_destroy(swaynag); + exit(EXIT_FAILURE); } if (!swaynag->output && swaynag->type->output) {