diff --git a/.builds/alpine.yml b/.builds/alpine.yml new file mode 100644 index 000000000..328625a5d --- /dev/null +++ b/.builds/alpine.yml @@ -0,0 +1,38 @@ +image: alpine/edge +packages: + - cairo-dev + - eudev-dev + - gdk-pixbuf-dev + - json-c-dev + - libevdev-dev + - libinput-dev + - libxcb-dev + - libxkbcommon-dev + - mesa-dev + - meson + - pango-dev + - pixman-dev + - scdoc + - wayland-dev + - wayland-protocols + - xcb-util-image-dev + - xorg-server-xwayland +sources: + - https://github.com/swaywm/sway + - https://github.com/swaywm/wlroots +tasks: + - wlroots: | + cd wlroots + meson --prefix=/usr build -Drootston=false -Dexamples=false + ninja -C build + sudo ninja -C build install + - setup: | + cd sway + meson build + - build: | + cd sway + ninja -C build + - build-no-xwayland: | + cd sway + meson configure build -Dxwayland=disabled + ninja -C build diff --git a/.build.yml b/.builds/archlinux.yml similarity index 85% rename from .build.yml rename to .builds/archlinux.yml index 144333118..c8f116e00 100644 --- a/.build.yml +++ b/.builds/archlinux.yml @@ -1,25 +1,25 @@ -# vim: ft=yaml ts=2 sw=2 et : image: archlinux packages: - - meson - - xorg-server-xwayland - - xcb-util-image - - json-c - - pango - cairo + - gdk-pixbuf2 + - json-c + - libinput + - libxcb + - libxkbcommon + - meson + - pango + - scdoc - wayland - wayland-protocols - - gdk-pixbuf2 - - libinput - - libxkbcommon - - scdoc + - xcb-util-image + - xorg-server-xwayland sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots tasks: - wlroots: | cd wlroots - meson --prefix=/usr build + meson --prefix=/usr build -Drootston=false -Dexamples=false ninja -C build sudo ninja -C build install - setup: | diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 000000000..8fa760257 --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,53 @@ +image: freebsd/latest +packages: +- devel/json-c +- devel/libevdev +- devel/meson +- devel/pkgconf +- graphics/cairo +- graphics/gdk-pixbuf2 +- graphics/wayland +- graphics/wayland-protocols +- textproc/scdoc +- x11-toolkits/pango +- x11/libxcb +- x11/libxkbcommon +# wlroots dependencies +- devel/evdev-proto +- devel/libepoll-shim +- devel/libudev-devd +- graphics/libdrm +- graphics/mesa-libs +- x11/libinput +- x11/libX11 +- x11/pixman +- x11/xcb-util-wm +sources: +- https://github.com/swaywm/sway +- https://github.com/swaywm/wlroots +tasks: +- fixup_epoll: | + cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc + prefix=/usr/local + exec_prefix=\$\{\$prefix\} + libdir=${prefix}/lib + sharedlibdir=${prefix}/lib + includedir=${prefix}/include/libepoll-shim + Name: epoll-shim + Description: epoll shim implemented using kevent + Version: 0 + Requires: + Libs: -L${libdir} -L${sharedlibdir} -lepoll-shim + Libs.private: -pthread -lrt + Cflags: -I${includedir} + EOF +- setup: | + cd sway + mkdir subprojects + cd subprojects + ln -s ../../wlroots wlroots + cd .. + meson build +- build: | + cd sway + ninja -C build diff --git a/README.es.md b/README.es.md new file mode 100644 index 000000000..daa1fc093 --- /dev/null +++ b/README.es.md @@ -0,0 +1,70 @@ +# sway + +[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) + +sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con i3. +Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on +irc.freenode.net). + +Si quiere apoyar el desarrollo de sway, por favor, contribuya en [la página de Patreon de SirCmpwn](https://patreon.com/sircmpwn). + +## Firmas de las versiones + +Las distintas versiones están firmadas con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) +y publicadas [en GitHub](https://github.com/swaywm/sway/releases). + +## Instalación + +### Usando paquetes + +Sway está disponible en muchas distribuciones. Pruebe instalando el paquete "sway" desde la suya. +Si no está disponible, puede consultar [esta documentación](https://github.com/swaywm/sway/wiki/Unsupported-packages) +y así obtener información acerca de como instalarlo. + +Si está interesado en crear un paquete para su distribución, únase al canal de IRC o +escriba un email a sir@cmpwn.com + +### Compilando el código fuente + +Instale las dependencias: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* git \* + +_\*Compile-time dep_ + +_\*\*opcional: necesario para swaybg_ + +Desde su consola, ejecute las órdenes: + + meson build + ninja -C build + sudo ninja -C build install + +En sistemas sin `logind`, necesitará cambiar los permisos del archivo compilado de sway: + + sudo chmod a+s /usr/local/bin/sway + +Sway abandonará los permisos de super-usuario al poco de arrancar. + +## Configuración + +Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y +sway funcionará sin tener que configurar nada más. En otro caso, copie el archivo de +configuración básico a `~/.config/sway/config`, normalmente se encuentra en `/etc/sway/config`. +Ejecute `man 5 sway` para obtener información sobre la configuración. + +## Ejecución + +Ejecute `sway` desde su consola. Algunos gestores de pantalla pueden funcionar sin estar +soportados por `sway` (sabemos que gdm funciona bastante bien). diff --git a/README.md b/README.md index ec958a242..171d71e46 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # sway [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Indonesian](https://github.com/swaywm/sway/blob/master/README.id.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - -[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) + sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC diff --git a/README.pl.md b/README.pl.md new file mode 100644 index 000000000..843fae605 --- /dev/null +++ b/README.pl.md @@ -0,0 +1,71 @@ +# sway + +[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) + +sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z i3. +Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](http://webchat.freenode.net/?channels=sway&uio=d4) +(#sway na irc.freenode.net). + +Jeśli chcesz wesprzeć rozwój sway, rozważ wsparcie SirCmpwn na jego [stronie Patreon](https://patreon.com/sircmpwn). + +## Podpisy cyfrowe wydań + +Wydania są podpisywane przy pomocy klucza [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) +i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). + +## Instalacja + +### Z pakietów + +Sway jest dostępny w wielu dystybucjach. Spróbuj zainstalować pakiet "sway" w swoim +menedżerze pakietów. Jeśli nie jest dostępny, sprawdź [tę stronę wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) +aby uzyskać informacje dotyczące instalacji w swojej dystrybucji. + +Jeśli chciałbyś stworzyć pakiet sway dla swojej dystrybucji, odwiedź kanał IRC lub wyślij email na +adres sir@cmpwn.com w celu uzyskania wskazówek. + +### Kompilacja ze Źródła + +Zainstaluj zależności: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (opcjonalnie: strony pomocy man) \* +* git \* + +_\*zależności kompilacji_ + +_\*\*opcjonalnie: wymagane dla swaybg_ + +Wykonaj następujące polecenia: + + meson build + ninja -C build + sudo ninja -C build install + +Na systemach bez logind należy wykonać polecenie suid na pliku wykonywalnym sway: + + sudo chmod a+s /usr/local/bin/sway + +Sway pozbędzie się uprawnień roota tuż po wystartowaniu. + +## Konfiguracja + +Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i +zadziała od ręki. W przeciwnym razie skopiuj przykładowy plik konfiguracyjny do folderu +`~/.config/sway/config`; zazwyczaj znajduje się w `/etc/sway/config`. +Wykonaj polecenie `man 5 sway` aby uzyskać informacje dotyczące konfiguracji. + +## Uruchamianie + +Wykonaj polecenie `sway` z poziomu TTY. Niektóre menedżery wyświetlania mogą umożliwiać rozruch z ich +poziomu, ale nie jest to wspierane przez sway (w gdm podobno działa to całkiem nieźle). diff --git a/include/sway/commands.h b/include/sway/commands.h index 3ed007635..764821a0e 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -112,7 +112,6 @@ sway_cmd cmd_client_placeholder; sway_cmd cmd_client_background; sway_cmd cmd_commands; sway_cmd cmd_create_output; -sway_cmd cmd_debuglog; sway_cmd cmd_default_border; sway_cmd cmd_default_floating_border; sway_cmd cmd_default_orientation; diff --git a/include/sway/config.h b/include/sway/config.h index 43ea77788..54cdcc908 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -586,6 +586,8 @@ struct output_config *store_output_config(struct output_config *oc); void apply_output_config_to_outputs(struct output_config *oc); +void reset_outputs(void); + void free_output_config(struct output_config *oc); int workspace_output_cmp_workspace(const void *a, const void *b); diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 072a56ca1..98eb4679b 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -72,6 +72,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat); * This chooses a cursor icon and sends a motion event to the surface. */ void cursor_rebase(struct sway_cursor *cursor); +void cursor_rebase_all(void); void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); diff --git a/include/sway/output.h b/include/sway/output.h index 479897ef2..ea7a21741 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -21,6 +21,7 @@ struct sway_output { struct sway_node node; struct wlr_output *wlr_output; struct sway_server *server; + struct wl_list link; struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; @@ -36,6 +37,8 @@ struct sway_output { struct sway_output_state current; + struct wl_client *swaybg_client; + struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -43,10 +46,7 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - - struct wl_list link; - - pid_t bg_pid; + struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; diff --git a/include/sway/tree/output.h b/include/sway/tree/output.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 7abfbff13..41b597963 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -67,11 +67,13 @@ struct sway_workspace *workspace_by_number(const char* name); struct sway_workspace *workspace_by_name(const char*); -struct sway_workspace *workspace_output_next(struct sway_workspace *current); +struct sway_workspace *workspace_output_next( + struct sway_workspace *current, bool create); struct sway_workspace *workspace_next(struct sway_workspace *current); -struct sway_workspace *workspace_output_prev(struct sway_workspace *current); +struct sway_workspace *workspace_output_prev( + struct sway_workspace *current, bool create); struct sway_workspace *workspace_prev(struct sway_workspace *current); diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 2d9ba0d9b..2518d5aa8 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -46,6 +46,8 @@ struct swaybar { #if HAVE_TRAY struct swaybar_tray *tray; #endif + + bool running; }; struct swaybar_output { diff --git a/meson.build b/meson.build index c50fab176..d3172bdd8 100644 --- a/meson.build +++ b/meson.build @@ -60,7 +60,11 @@ rt = cc.find_library('rt') git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency -wlroots_proj = subproject('wlroots', required: false) +wlroots_proj = subproject( + 'wlroots', + default_options: ['rootston=false', 'examples=false'], + required: false, +) if wlroots_proj.found() wlroots = wlroots_proj.get_variable('wlroots') wlroots_conf = wlroots_proj.get_variable('conf_data') @@ -128,12 +132,13 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') +version = '"@0@"'.format(meson.project_version()) if git.found() - git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() - git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() - version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) -else - version = '"@0@"'.format(meson.project_version()) + git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']) + git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']) + if git_commit_hash.returncode() == 0 and git_branch.returncode() == 0 + version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash.stdout().strip(), git_branch.stdout().strip()) + endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index d89ddf24e..68a80abf6 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -11,7 +11,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode if (strcasecmp("toggle", mode) == 0 && !config->reading) { if (strcasecmp("dock", bar->mode) == 0) { bar->mode = strdup("hide"); - } else if (strcasecmp("hide", bar->mode) == 0) { + } else{ bar->mode = strdup("dock"); } } else if (strcasecmp("dock", mode) == 0) { diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 59116d67f..172e6b8ae 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,10 +1,11 @@ #define _POSIX_C_SOURCE 200809L #include #include -#include -#include #include #include +#include +#include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/input/cursor.h" @@ -330,7 +331,20 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { sway_log(SWAY_DEBUG, "running command for binding: %s", binding->command); - list_t *res_list = execute_command(binding->command, seat, NULL); + struct sway_container *con = NULL; + if (binding->type == BINDING_MOUSESYM + || binding->type == BINDING_MOUSECODE) { + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + seat->cursor->cursor->x, seat->cursor->cursor->y, + &surface, &sx, &sy); + if (node && node->type == N_CONTAINER) { + con = node->sway_container; + } + } + + list_t *res_list = execute_command(binding->command, seat, con); bool success = true; for (int i = 0; i < res_list->length; ++i) { struct cmd_results *results = res_list->items[i]; diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 79b2f551f..25df51304 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -37,6 +37,9 @@ static struct sway_node *get_node_in_output_direction( struct sway_output *output, enum wlr_direction dir) { struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return NULL; + } if (ws->fullscreen) { return seat_get_focus_inactive(seat, &ws->fullscreen->node); } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 2aca31dc5..7d61c3be3 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -111,10 +111,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) { "Unable to change layout of floating windows"); } - // Typically we change the layout of the current container, but if the - // current container is a view (it usually is) then we'll change the layout - // of the parent instead, as it doesn't make sense for views to have layout. - if (container && container->view) { + // Operate on parent container, like i3. + if (container) { container = container->parent; } diff --git a/sway/commands/move.c b/sway/commands/move.c index 8c3afae97..4306aac86 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -283,6 +283,9 @@ static bool container_move_in_direction(struct sway_container *container, return false; } struct sway_workspace *ws = output_get_active_workspace(new_output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } container_move_to_workspace(container, ws); return true; } @@ -360,6 +363,9 @@ static bool container_move_in_direction(struct sway_container *container, output_get_in_direction(container->workspace->output, move_dir); if (output) { struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } container_move_to_workspace_from_direction(container, ws, move_dir); return true; } @@ -516,7 +522,6 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // move container if (container->scratchpad) { - root_scratchpad_remove_container(container); root_scratchpad_show(container); } switch (destination->type) { @@ -526,6 +531,10 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { case N_OUTPUT: { struct sway_output *output = destination->sway_output; struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } container_move_to_workspace(container, ws); } break; @@ -539,6 +548,10 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // restore focus on destination output back to its last active workspace struct sway_workspace *new_workspace = output_get_active_workspace(new_output); + if (!sway_assert(new_workspace, "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } 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); @@ -586,6 +599,9 @@ static void workspace_move_to_output(struct sway_workspace *workspace, workspace_detach(workspace); struct sway_workspace *new_output_old_ws = output_get_active_workspace(output); + if (!sway_assert(new_output_old_ws, "Expected output to have a workspace")) { + return; + } output_add_workspace(output, workspace); @@ -838,11 +854,11 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { } } - if (con->scratchpad) { - return cmd_results_new(CMD_INVALID, - "Container is already in the scratchpad"); + if (!con->scratchpad) { + root_scratchpad_add_container(con); + } else if (con->workspace) { + root_scratchpad_hide(con); } - root_scratchpad_add_container(con); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 5b23dcc6a..aa36ba955 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c @@ -50,5 +50,5 @@ struct cmd_results *cmd_seat(int argc, char **argv) { } config->handler_context.seat_config = NULL; - return cmd_results_new(CMD_SUCCESS, NULL); + return res ? res : cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 0c7609eaf..085e6a33f 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -12,7 +12,7 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, static const char expected_syntax[] = "Expected 'cursor ' or " "'cursor ' or " - "'curor '"; + "'cursor '"; static struct cmd_results *handle_command(struct sway_cursor *cursor, int argc, char **argv) { diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 5b70199cb..9df1fe09d 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -9,6 +9,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" +#include "log.h" #include "util.h" struct cmd_results *cmd_sticky(int argc, char **argv) { @@ -29,6 +30,11 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { // move container to active workspace struct sway_workspace *active_workspace = output_get_active_workspace(container->workspace->output); + if (!sway_assert(active_workspace, + "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } if (container->workspace != active_workspace) { struct sway_workspace *old_workspace = container->workspace; container_detach(container); diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 0e2c2d105..b978af162 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -118,6 +118,10 @@ static void container_swap(struct sway_container *con1, output_get_active_workspace(con1->workspace->output); struct sway_workspace *vis2 = output_get_active_workspace(con2->workspace->output); + if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" + "workspace. This should not happen")) { + return; + } char *stored_prev_name = NULL; if (seat->prev_workspace_name) { diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 3c5e9ba12..7c27c163a 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c @@ -21,7 +21,12 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - arrange_workspace(output_get_active_workspace(output)); + struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } + arrange_workspace(ws); output_damage_whole(output); } diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 65a3f4075..5fde81299 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -181,7 +181,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { ++argv; } - + bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; + struct sway_seat *seat = config->handler_context.seat; + struct sway_workspace *current = seat_get_focused_workspace(seat); struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { @@ -199,12 +201,13 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } } else if (strcasecmp(argv[0], "next") == 0 || strcasecmp(argv[0], "prev") == 0 || - strcasecmp(argv[0], "next_on_output") == 0 || - strcasecmp(argv[0], "prev_on_output") == 0 || strcasecmp(argv[0], "current") == 0) { ws = workspace_by_name(argv[0]); + } else if (strcasecmp(argv[0], "next_on_output") == 0) { + ws = workspace_output_next(current, create); + } else if (strcasecmp(argv[0], "prev_on_output") == 0) { + ws = workspace_output_prev(current, create); } else if (strcasecmp(argv[0], "back_and_forth") == 0) { - struct sway_seat *seat = config->handler_context.seat; if (!seat->prev_workspace_name) { return cmd_results_new(CMD_INVALID, "There is no previous workspace"); @@ -220,7 +223,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { free(name); } workspace_switch(ws, no_auto_back_and_forth); - seat_consider_warp_to_focus(config->handler_context.seat); + seat_consider_warp_to_focus(seat); } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config.c b/sway/config.c index ae8d11e35..206ca95cb 100644 --- a/sway/config.c +++ b/sway/config.c @@ -118,7 +118,7 @@ void free_config(struct sway_config *config) { } list_free(config->no_focus); list_free(config->active_bar_modifiers); - list_free(config->config_chain); + list_free_items_and_destroy(config->config_chain); list_free(config->command_policies); list_free(config->feature_policies); list_free(config->ipc_policies); @@ -471,9 +471,8 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } if (is_active) { - for (int i = 0; i < config->output_configs->length; i++) { - apply_output_config_to_outputs(config->output_configs->items[i]); - } + reset_outputs(); + config->reloading = false; if (config->swaynag_config_errors.pid > 0) { swaynag_show(&config->swaynag_config_errors); diff --git a/sway/config/bar.c b/sway/config/bar.c index bafef307c..2e28fa1ef 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -46,6 +46,7 @@ void free_bar_config(struct bar_config *bar) { free(bar->position); free(bar->hidden_state); free(bar->status_command); + free(bar->swaybar_command); free(bar->font); free(bar->separator_symbol); for (int i = 0; i < bar->bindings->length; i++) { diff --git a/sway/config/output.c b/sway/config/output.c index 970764b03..e7fbad833 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,16 +1,17 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include -#include +#include #include #include -#include #include +#include +#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" -#include "log.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -165,14 +166,89 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -void terminate_swaybg(pid_t pid) { - int ret = kill(pid, SIGTERM); - if (ret != 0) { - sway_log(SWAY_ERROR, "Unable to terminate swaybg [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + struct sway_output *output = + wl_container_of(listener, output, swaybg_client_destroy); + wl_list_remove(&output->swaybg_client_destroy.link); + wl_list_init(&output->swaybg_client_destroy.link); + output->swaybg_client = NULL; +} + +static bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} + +static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + return false; + } + + output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (output->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(output->swaybg_client, + &output->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(cmd[0], cmd); + sway_log_errno(SWAY_ERROR, "execvp failed"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; } bool apply_output_config(struct output_config *oc, struct sway_output *output) { @@ -199,6 +275,11 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { return true; } + if (oc && oc->dpms_state == DPMS_ON) { + sway_log(SWAY_DEBUG, "Turning on screen"); + wlr_output_enable(wlr_output, true); + } + bool modeset_success; if (oc && oc->width > 0 && oc->height > 0) { sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, @@ -238,8 +319,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - if (output->bg_pid != 0) { - terminate_swaybg(output->bg_pid); + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } if (oc && oc->background && config->swaybg_command) { sway_log(SWAY_DEBUG, "Setting background for output %s to %s", @@ -253,29 +334,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { oc->background_fallback ? oc->background_fallback : NULL, NULL, }; - - output->bg_pid = fork(); - if (output->bg_pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - } else if (output->bg_pid == 0) { - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "Failed to execute swaybg"); + if (!spawn_swaybg(output, cmd)) { + return false; } } - if (oc) { - switch (oc->dpms_state) { - case DPMS_ON: - sway_log(SWAY_DEBUG, "Turning on screen"); - wlr_output_enable(wlr_output, true); - break; - case DPMS_OFF: - sway_log(SWAY_DEBUG, "Turning off screen"); - wlr_output_enable(wlr_output, false); - break; - case DPMS_IGNORE: - break; - } + if (oc && oc->dpms_state == DPMS_OFF) { + sway_log(SWAY_DEBUG, "Turning off screen"); + wlr_output_enable(wlr_output, false); } return true; @@ -294,6 +360,7 @@ static void default_output_config(struct output_config *oc, oc->x = oc->y = -1; oc->scale = 1; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; + oc->dpms_state = DPMS_ON; } static struct output_config *get_output_config(char *identifier, @@ -395,6 +462,17 @@ void apply_output_config_to_outputs(struct output_config *oc) { } } +void reset_outputs(void) { + struct output_config *oc = NULL; + int i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + oc = config->output_configs->items[i]; + } else { + oc = store_output_config(new_output_config("*")); + } + apply_output_config_to_outputs(oc); +} + void free_output_config(struct output_config *oc) { if (!oc) { return; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de8db75df..0767247c4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -6,7 +6,9 @@ #include #include #include +#include "log.h" #include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -14,7 +16,6 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" -#include "log.h" static void apply_exclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, @@ -302,6 +303,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { if (seat->focused_layer == sway_layer->layer_surface) { seat_set_focus_layer(seat, NULL); } + + cursor_rebase_all(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -321,7 +324,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = sway_layer->layer_surface->output->data; if (output != NULL) { arrange_layers(output); - arrange_output(output); transaction_commit_dirty(); } wl_list_remove(&sway_layer->output_destroy.link); @@ -339,6 +341,7 @@ static void handle_map(struct wl_listener *listener, void *data) { // TODO: send enter to subsurfaces and popups wlr_surface_send_enter(sway_layer->layer_surface->surface, sway_layer->layer_surface->output); + cursor_rebase_all(); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ad75bb35a..d7d3fc07e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -251,6 +251,9 @@ static void output_for_each_surface(struct sway_output *output, }; struct sway_workspace *workspace = output_get_active_workspace(output); + if (!workspace) { + return; + } struct sway_container *fullscreen_con = root->fullscreen_global; if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { fullscreen_con = NULL; @@ -320,6 +323,9 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); if (!focus) { + if (!output->workspaces->length) { + return NULL; + } return output->workspaces->items[0]; } return focus->sway_workspace; @@ -506,6 +512,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); + wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); } @@ -612,6 +619,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_frame.notify = damage_handle_frame; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; + wl_list_init(&output->swaybg_client_destroy.link); struct output_config *oc = output_find_config(output); if (!oc || oc->enabled) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e0c3a5d1e..4098ed22c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -314,14 +314,7 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - if (root->outputs->length) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (!seat_doing_seatop(seat)) { - cursor_rebase(seat->cursor); - } - } - } + cursor_rebase_all(); } static void transaction_commit(struct sway_transaction *transaction); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 1bf548db7..fb1edd0b0 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -104,6 +104,9 @@ struct sway_node *node_at_coords( // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); + if (!ws) { + return NULL; + } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -283,6 +286,19 @@ void cursor_rebase(struct sway_cursor *cursor) { cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); } +void cursor_rebase_all(void) { + if (!root->outputs->length) { + return; + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + if (!seat_doing_seatop(seat)) { + cursor_rebase(seat->cursor); + } + } +} + static int hide_notify(void *data) { struct sway_cursor *cursor = data; wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); @@ -408,15 +424,16 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { wlr_relative_pointer_manager_v1_send_relative_motion( server.relative_pointer_manager, - cursor->seat->wlr_seat, event->time_msec, dx, dy, - dx_unaccel, dy_unaccel); + cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, + dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; + struct sway_node *node = NULL; double sx, sy; - struct sway_node *node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (cursor->active_constraint) { + node = node_at_coords(cursor->seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + if (cursor->active_constraint->surface != surface) { return; } @@ -432,8 +449,13 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { } wlr_cursor_move(cursor->cursor, event->device, dx, dy); + + // Recalculate pointer location after layout checks + node = node_at_coords(cursor->seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_send_pointer_motion(cursor, event->time_msec, node, surface, - sx + dx, sy + dy); + sx, sy); transaction_commit_dirty(); } @@ -530,10 +552,11 @@ static void state_add_button(struct sway_cursor *cursor, uint32_t button) { static struct sway_binding* get_active_mouse_binding( const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, bool on_border, bool on_content, - const char *identifier) { - uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | - (on_border ? BINDING_BORDER : 0) | - (on_content ? BINDING_CONTENTS : 0); + bool on_workspace, const char *identifier) { + uint32_t click_region = + ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | + ((on_border || on_workspace) ? BINDING_BORDER : 0) | + ((on_content || on_workspace) ? BINDING_CONTENTS : 0); struct sway_binding *current = NULL; for (int i = 0; i < bindings->length; ++i) { @@ -542,6 +565,8 @@ static struct sway_binding* get_active_mouse_binding( cursor->pressed_button_count != (size_t)binding->keys->length || release != (binding->flags & BINDING_RELEASE) || !(click_region & binding->flags) || + (on_workspace && + (click_region & binding->flags) != click_region) || (strcmp(binding->input, identifier) != 0 && strcmp(binding->input, "*") != 0)) { continue; @@ -607,6 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, find_resize_edge(cont, cursor) : WLR_EDGE_NONE; bool on_border = edge != WLR_EDGE_NONE; bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; bool on_titlebar = cont && !on_border && !surface; // Handle mouse bindings @@ -620,11 +646,13 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state_add_button(cursor, button); binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, device_identifier); + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); } else { binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, device_identifier); + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); state_erase_button(cursor, button); } free(device_identifier); @@ -814,6 +842,7 @@ void dispatch_cursor_axis(struct sway_cursor *cursor, bool on_titlebar_border = cont && on_border && cursor->cursor->y < cont->content_y; bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; float scroll_factor = (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; @@ -832,7 +861,7 @@ void dispatch_cursor_axis(struct sway_cursor *cursor, state_add_button(cursor, button); binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, dev_id); + on_titlebar, on_border, on_contents, on_workspace, dev_id); if (binding) { seat_execute_command(seat, binding); handled = true; @@ -873,7 +902,7 @@ void dispatch_cursor_axis(struct sway_cursor *cursor, // Handle mouse bindings - x11 mouse buttons 4-7 - release event binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, dev_id); + on_titlebar, on_border, on_contents, on_workspace, dev_id); state_erase_button(cursor, button); if (binding) { seat_execute_command(seat, binding); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index efd27f70d..00fc6a13f 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -549,13 +549,17 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_keyboard_led_update(wlr_device->keyboard, leds); } - if (input_config && input_config->repeat_delay != INT_MIN - && input_config->repeat_rate != INT_MIN) { - wlr_keyboard_set_repeat_info(wlr_device->keyboard, - input_config->repeat_rate, input_config->repeat_delay); - } else { - wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600); + int repeat_rate = 25; + if (input_config && input_config->repeat_rate != INT_MIN) { + repeat_rate = input_config->repeat_rate; } + int repeat_delay = 600; + if (input_config && input_config->repeat_delay != INT_MIN) { + repeat_delay = input_config->repeat_delay; + } + wlr_keyboard_set_repeat_info(wlr_device->keyboard, repeat_rate, + repeat_delay); + xkb_context_unref(context); struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; wlr_seat_set_keyboard(seat, wlr_device); diff --git a/sway/input/seat.c b/sway/input/seat.c index 18664d7cb..69b048430 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -287,6 +287,7 @@ static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&icon->link); wl_list_remove(&icon->surface_commit.link); wl_list_remove(&icon->unmap.link); + wl_list_remove(&icon->map.link); wl_list_remove(&icon->destroy.link); free(icon); } @@ -725,6 +726,10 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { if (seat->focused_layer) { + struct wlr_layer_surface_v1 *layer = seat->focused_layer; + seat_set_focus_layer(seat, NULL); + seat_set_focus(seat, node); + seat_set_focus_layer(seat, layer); return; } diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 8b541f80d..1e548f5ae 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -150,6 +150,9 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (edge) { e->target_node = node_get_parent(&con->node); + if (e->target_node == &e->con->node) { + e->target_node = node_get_parent(e->target_node); + } e->target_edge = edge; node_get_box(e->target_node, &e->drop_box); resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); @@ -161,7 +164,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view->surface || node == &e->con->node) { + if (!con->view->surface || node == &e->con->node + || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; return; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e10989420..a2ab2bba8 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -198,6 +198,9 @@ static void ipc_json_describe_output(struct sway_output *output, ipc_json_output_transform_description(wlr_output->transform))); struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return; + } json_object_object_add(object, "current_workspace", json_object_new_string(ws->name)); @@ -344,6 +347,9 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "app_id", app_id ? json_object_new_string(app_id) : NULL); + bool visible = view_is_visible(c->view); + json_object_object_add(object, "visible", json_object_new_boolean(visible)); + json_object *marks = json_object_new_array(); list_t *con_marks = c->marks; for (int i = 0; i < con_marks->length; ++i) { @@ -597,7 +603,7 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); json_object_object_add(object, "xkb_active_layout_name", - json_object_new_string(layout)); + layout ? json_object_new_string(layout) : NULL); break; } } diff --git a/sway/main.c b/sway/main.c index 12f92bd80..6754190f9 100644 --- a/sway/main.c +++ b/sway/main.c @@ -126,6 +126,7 @@ void run_as_ipc_client(char *command, char *socket_path) { uint32_t len = strlen(command); char *resp = ipc_single_command(socketfd, IPC_COMMAND, command, &len); printf("%s\n", resp); + free(resp); close(socketfd); } @@ -299,6 +300,14 @@ int main(int argc, char **argv) { } } + // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the + // clear error message (when not running as an IPC client). + if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { + fprintf(stderr, + "XDG_RUNTIME_DIR is not set in the environment. Aborting.\n"); + exit(EXIT_FAILURE); + } + // As the 'callback' function for wlr_log is equivalent to that for // sway, we do not need to override it. if (debug) { @@ -339,6 +348,7 @@ int main(int argc, char **argv) { } char *command = join_args(argv + optind, argc - optind); run_as_ipc_client(command, socket_path); + free(command); return 0; } diff --git a/sway/server.c b/sway/server.c index 712d8022b..0fcd58003 100644 --- a/sway/server.c +++ b/sway/server.c @@ -10,13 +10,14 @@ #include #include #include -#include #include +#include #include #include #include -#include #include +#include +#include #include #include #include @@ -122,6 +123,7 @@ bool server_init(struct sway_server *server) { wlr_export_dmabuf_manager_v1_create(server->wl_display); wlr_screencopy_manager_v1_create(server->wl_display); wlr_data_control_manager_v1_create(server->wl_display); + wlr_primary_selection_v1_device_manager_create(server->wl_display); server->socket = wl_display_add_socket_auto(server->wl_display); if (!server->socket) { diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 376e18338..8c3a8225f 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -8,6 +8,13 @@ sway-input - input configuration file and commands Sway allows for configuration of devices within the sway configuration file. To obtain a list of available device identifiers, run *swaymsg -t get_inputs*. +Settings can also be applied to all input devices by using the wildcard, _\*_, +in place of _\_ in the commands below. + +Tip: If the configuration settings do not appear to be taking effect, you could +try using _\*_ instead of _\_. If it works with the wildcard, try +using a different identifier from *swaymsg -t get_inputs* until you find the +correct input device. # INPUT COMMANDS @@ -144,7 +151,13 @@ configured. While sway is running, _-_ (hyphen) can be used as an alias for the current seat. Each seat has an independent keyboard focus and a separate cursor that is controlled by the pointer devices of the seat. This is useful for multiple people using the desktop at the same time with their own devices (each -sitting in their own "seat"). +sitting in their own "seat"). The wildcard character, _\*_, can also be used in +place of _\_ to change settings for all seats. + +Tip: If the configuration settings do not appear to be taking effect, you could +try using _\*_ instead of _\_. If it works with the wildcard, try +using a different identifier from *swaymsg -t get_seats* until you find the +correct seat. *seat* attach Attach an input device to this seat by its input identifier. A special diff --git a/sway/sway.5.scd b/sway/sway.5.scd index fd0a22dc7..1affca5fe 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -293,12 +293,13 @@ runtime. overwrite a binding, swaynag will give you a warning. To silence this, use the _--no-warn_ flag. - Mouse buttons can either be specified in the form _button[1-9]_ or by using - the name of the event code (ex _BTN\_LEFT_ or _BTN\_RIGHT_). For the former - option, the buttons will be mapped to their values in X11 (1=left, 2=middle, - 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back, - 9=forward). For the latter option, you can find the event names using - _libinput debug-events_. + Mouse bindings operate on the container under the cursor instead of the + container that has focus. Mouse buttons can either be specified in the form + _button[1-9]_ or by using the name of the event code (ex _BTN\_LEFT_ or + _BTN\_RIGHT_). For the former option, the buttons will be mapped to their + values in X11 (1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down, + 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, + you can find the event names using _libinput debug-events_. _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options which affect the region in which the mouse bindings can be triggered. By @@ -309,9 +310,9 @@ runtime. conjunction with any other option to specify that the titlebar should be excluded from the region of consideration. - There is currently, however, no way to execute a mouse binding over a layer - surface (which includes the background of an empty workspace). This behaviour - is carried over from i3. + If _--whole-window_ is given, the command can be triggered when the cursor + is over an empty workspace. Using a mouse binding over a layer surface's + exclusive region is not currently possible. Example: ``` @@ -413,10 +414,6 @@ The default colors are: : #0c0c0c -*debuglog* on|off|toggle - Enables, disables or toggles debug logging. _toggle_ cannot be used in the - configuration file. - *default_border* normal|none|pixel [] Set default border style for new tiled windows. diff --git a/sway/tree/container.c b/sway/tree/container.c index 0ebdc51de..933907f4e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -251,10 +251,12 @@ static struct sway_container *container_at_stacked(struct sway_node *parent, // Title bars int title_height = container_titlebar_height(); - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; + if (title_height > 0) { + int child_index = (ly - box.y) / title_height; + if (child_index < children->length) { + struct sway_container *child = children->items[child_index]; + return child; + } } // Surfaces @@ -829,7 +831,7 @@ void container_floating_move_to(struct sway_container *con, } struct sway_workspace *new_workspace = output_get_active_workspace(new_output); - if (old_workspace != new_workspace) { + if (new_workspace && old_workspace != new_workspace) { container_detach(con); workspace_add_floating(new_workspace, con); arrange_workspace(old_workspace); diff --git a/sway/tree/output.c b/sway/tree/output.c index 5a992f2db..e0a66e0bd 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -8,7 +8,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" -#include "sway/tree/output.h" #include "sway/tree/workspace.h" #include "log.h" #include "util.h" @@ -144,6 +143,9 @@ void output_enable(struct sway_output *output, struct output_config *oc) { static void evacuate_sticky(struct sway_workspace *old_ws, struct sway_output *new_output) { struct sway_workspace *new_ws = output_get_active_workspace(new_output); + if (!sway_assert(new_ws, "New output does not have a workspace")) { + return; + } while (old_ws->floating->length) { struct sway_container *sticky = old_ws->floating->items[0]; container_detach(sticky); @@ -190,10 +192,19 @@ static void output_evacuate(struct sway_output *output) { continue; } + struct sway_workspace *new_output_ws = + output_get_active_workspace(new_output); + workspace_output_add_priority(workspace, new_output); output_add_workspace(new_output, workspace); output_sort_workspaces(new_output); ipc_event_workspace(NULL, workspace, "move"); + + // If there is an old workspace (the noop output may not have one), + // check to see if it is empty and should be destroyed. + if (new_output_ws) { + workspace_consider_destroy(new_output_ws); + } } } @@ -234,9 +245,8 @@ void output_disable(struct sway_output *output) { root_for_each_container(untrack_output, output); - if (output->bg_pid) { - terminate_swaybg(output->bg_pid); - output->bg_pid = 0; + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } int index = list_find(root->outputs, output); diff --git a/sway/tree/view.c b/sway/tree/view.c index 612cf96ae..ca13def72 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -664,6 +664,13 @@ void view_unmap(struct sway_view *view) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat->cursor->image_surface = NULL; + if (seat->cursor->active_constraint) { + struct wlr_surface *constrain_surface = + seat->cursor->active_constraint->surface; + if (view_from_wlr_surface(constrain_surface) == view) { + sway_cursor_constrain(seat->cursor, NULL); + } + } seat_consider_warp_to_focus(seat); } @@ -701,6 +708,9 @@ static void subsurface_get_root_coords(struct sway_view_child *child, while (surface && wlr_surface_is_subsurface(surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + break; + } *root_sx += subsurface->current.x; *root_sy += subsurface->current.y; surface = subsurface->parent; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 8b3eb2ad2..73322491b 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -42,10 +42,16 @@ struct sway_output *workspace_get_initial_output(const char *name) { } } } - // Otherwise put it on the focused output + // Otherwise try to put it on the focused output struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *focus = seat_get_focused_workspace(seat); - return focus->output; + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + if (focus && focus->type == N_WORKSPACE) { + return focus->sway_workspace->output; + } else if (focus && focus->type == N_CONTAINER) { + return focus->sway_container->workspace->output; + } + // Fallback to the first output or noop output for headless + return root->outputs->length ? root->outputs->items[0] : root->noop_output; } static void prevent_invalid_outer_gaps(struct sway_workspace *ws) { @@ -224,8 +230,10 @@ static void workspace_name_from_binding(const struct sway_binding * binding, // not a command about workspaces if (strcmp(_target, "next") == 0 || strcmp(_target, "prev") == 0 || - strcmp(_target, "next_on_output") == 0 || - strcmp(_target, "prev_on_output") == 0 || + strncmp(_target, "next_on_output", + strlen("next_on_output")) == 0 || + strncmp(_target, "prev_on_output", + strlen("next_on_output")) == 0 || strcmp(_target, "number") == 0 || strcmp(_target, "back_and_forth") == 0 || strcmp(_target, "current") == 0) { @@ -328,16 +336,13 @@ char *workspace_next_name(const char *output_name) { if (target != NULL) { return target; } - // As a fall back, get the current number of active workspaces - // and return that + 1 for the next workspace's name - int ws_num = root->outputs->length; - int l = snprintf(NULL, 0, "%d", ws_num); - char *name = malloc(l + 1); - if (!sway_assert(name, "Could not allocate workspace name")) { - return NULL; - } - sprintf(name, "%d", ws_num++); - return name; + // As a fall back, use the next available number + char name[12] = ""; + unsigned int ws_num = 1; + do { + snprintf(name, sizeof(name), "%u", ws_num++); + } while (workspace_by_number(name)); + return strdup(name); } static bool _workspace_by_number(struct sway_workspace *ws, void *data) { @@ -366,11 +371,11 @@ struct sway_workspace *workspace_by_name(const char *name) { if (strcmp(name, "prev") == 0) { return workspace_prev(current); } else if (strcmp(name, "prev_on_output") == 0) { - return workspace_output_prev(current); + return workspace_output_prev(current, false); } else if (strcmp(name, "next") == 0) { return workspace_next(current); } else if (strcmp(name, "next_on_output") == 0) { - return workspace_output_next(current); + return workspace_output_next(current, false); } else if (strcmp(name, "current") == 0) { return current; } else if (strcasecmp(name, "back_and_forth") == 0) { @@ -391,11 +396,18 @@ struct sway_workspace *workspace_by_name(const char *name) { * otherwise the next one is returned. */ static struct sway_workspace *workspace_output_prev_next_impl( - struct sway_output *output, int dir) { + struct sway_output *output, int dir, bool create) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *workspace = seat_get_focused_workspace(seat); int index = list_find(output->workspaces, workspace); + if (!workspace_is_empty(workspace) && create && + (index + dir < 0 || index + dir == output->workspaces->length)) { + struct sway_output *output = workspace->output; + char *next = workspace_next_name(output->wlr_output->name); + workspace_create(output, next); + free(next); + } size_t new_index = wrap(index + dir, output->workspaces->length); return output->workspaces->items[new_index]; } @@ -426,16 +438,18 @@ static struct sway_workspace *workspace_prev_next_impl( } } -struct sway_workspace *workspace_output_next(struct sway_workspace *current) { - return workspace_output_prev_next_impl(current->output, 1); +struct sway_workspace *workspace_output_next( + struct sway_workspace *current, bool create) { + return workspace_output_prev_next_impl(current->output, 1, create); } struct sway_workspace *workspace_next(struct sway_workspace *current) { return workspace_prev_next_impl(current, 1); } -struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { - return workspace_output_prev_next_impl(current->output, -1); +struct sway_workspace *workspace_output_prev( + struct sway_workspace *current, bool create) { + return workspace_output_prev_next_impl(current->output, -1, create); } struct sway_workspace *workspace_prev(struct sway_workspace *current) { @@ -445,9 +459,15 @@ struct sway_workspace *workspace_prev(struct sway_workspace *current) { bool workspace_switch(struct sway_workspace *workspace, bool no_auto_back_and_forth) { struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *active_ws = seat_get_focused_workspace(seat); + struct sway_workspace *active_ws = NULL; + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + if (focus && focus->type == N_WORKSPACE) { + active_ws = focus->sway_workspace; + } else if (focus && focus->type == N_CONTAINER) { + active_ws = focus->sway_container->workspace; + } - if (!no_auto_back_and_forth && config->auto_back_and_forth + if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws && active_ws == workspace && seat->prev_workspace_name) { struct sway_workspace *new_ws = workspace_by_name(seat->prev_workspace_name); @@ -456,9 +476,9 @@ bool workspace_switch(struct sway_workspace *workspace, workspace_create(NULL, seat->prev_workspace_name); } - if (!seat->prev_workspace_name || + if (active_ws && (!seat->prev_workspace_name || (strcmp(seat->prev_workspace_name, active_ws->name) - && active_ws != workspace)) { + && active_ws != workspace))) { free(seat->prev_workspace_name); seat->prev_workspace_name = malloc(strlen(active_ws->name) + 1); if (!seat->prev_workspace_name) { diff --git a/swaybar/bar.c b/swaybar/bar.c index a1f7bfdb9..db1c12228 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -403,8 +404,7 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { static void display_in(int fd, short mask, void *data) { struct swaybar *bar = data; if (wl_display_dispatch(bar->display) == -1) { - bar_teardown(bar); - exit(0); + bar->running = false; } } @@ -439,7 +439,7 @@ void bar_run(struct swaybar *bar) { loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); } #endif - while (1) { + while (bar->running) { errno = 0; if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) { break; diff --git a/swaybar/main.c b/swaybar/main.c index 4ef746290..108b16e91 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -11,13 +11,7 @@ static struct swaybar swaybar; void sig_handler(int signal) { - bar_teardown(&swaybar); - exit(0); -} - -void sway_terminate(int code) { - bar_teardown(&swaybar); - exit(code); + swaybar.running = false; } int main(int argc, char **argv) { @@ -93,8 +87,6 @@ int main(int argc, char **argv) { } } - signal(SIGTERM, sig_handler); - if (!bar_setup(&swaybar, socket_path)) { free(socket_path); return 1; @@ -102,6 +94,10 @@ int main(int argc, char **argv) { free(socket_path); + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + swaybar.running = true; bar_run(&swaybar); bar_teardown(&swaybar); return 0; diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index 215e1e722..451b08967 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -189,9 +189,9 @@ bool init_host(struct swaybar_host *host, char *protocol, goto error; } - sd_bus_slot_set_floating(reg_slot, 1); - sd_bus_slot_set_floating(unreg_slot, 1); - sd_bus_slot_set_floating(watcher_slot, 1); + sd_bus_slot_set_floating(reg_slot, 0); + sd_bus_slot_set_floating(unreg_slot, 0); + sd_bus_slot_set_floating(watcher_slot, 0); sway_log(SWAY_DEBUG, "Registered %s", host->service); return true; diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 4262d6877..4fa6c97b5 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -299,6 +299,8 @@ void destroy_sni(struct swaybar_sni *sni) { return; } + cairo_surface_destroy(sni->icon); + sd_bus_slot_unref(sni->new_icon_slot); sd_bus_slot_unref(sni->new_attention_icon_slot); sd_bus_slot_unref(sni->new_status_slot); @@ -308,9 +310,11 @@ void destroy_sni(struct swaybar_sni *sni) { free(sni->path); free(sni->status); free(sni->icon_name); - free(sni->icon_pixmap); + list_free_items_and_destroy(sni->icon_pixmap); free(sni->attention_icon_name); + list_free_items_and_destroy(sni->attention_icon_pixmap); free(sni->menu); + free(sni->icon_theme_path); free(sni); } diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 381510717..951a05890 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -18,10 +18,6 @@ static int cmp_id(const void *item, const void *cmp_to) { return strcmp(item, cmp_to); } -static int cmp_service(const void *item, const void *cmp_to) { - return strncmp(item, cmp_to, strlen(cmp_to)); -} - static int handle_lost_service(sd_bus_message *msg, void *data, sd_bus_error *error) { char *service, *old_owner, *new_owner; @@ -33,18 +29,23 @@ static int handle_lost_service(sd_bus_message *msg, if (!*new_owner) { struct swaybar_watcher *watcher = data; - int idx = list_seq_find(watcher->items, - using_standard_protocol(watcher) ? cmp_id : cmp_service, service); - if (idx != -1) { + for (int idx = 0; idx < watcher->items->length; ++idx) { char *id = watcher->items->items[idx]; - sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); - list_del(watcher->items, idx); - sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, - "StatusNotifierItemUnregistered", "s", id); - free(id); + int cmp_res = using_standard_protocol(watcher) ? + cmp_id(id, service) : strncmp(id, service, strlen(service)); + if (cmp_res == 0) { + sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); + list_del(watcher->items, idx--); + sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, + "StatusNotifierItemUnregistered", "s", id); + free(id); + if (using_standard_protocol(watcher)) { + break; + } + } } - idx = list_seq_find(watcher->hosts, cmp_id, service); + int idx = list_seq_find(watcher->hosts, cmp_id, service); if (idx != -1) { sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); free(watcher->hosts->items[idx]); @@ -185,8 +186,8 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { goto error; } - sd_bus_slot_set_floating(signal_slot, 1); - sd_bus_slot_set_floating(vtable_slot, 1); + sd_bus_slot_set_floating(signal_slot, 0); + sd_bus_slot_set_floating(vtable_slot, 0); watcher->bus = bus; watcher->hosts = create_list(); diff --git a/swaymsg/main.c b/swaymsg/main.c index c84f5671f..716d2d2ed 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -135,8 +135,8 @@ static void pretty_print_input(json_object *i) { json_object_get_int(vendor)); if (json_object_object_get_ex(i, "xkb_active_layout_name", &kbdlayout)) { - printf(" Active Keyboard Layout: %s\n", - json_object_get_string(kbdlayout)); + const char *layout = json_object_get_string(kbdlayout); + printf(" Active Keyboard Layout: %s\n", layout ? layout : "(unnamed)"); } if (json_object_object_get_ex(i, "libinput_send_events", &events)) {