diff options
-rw-r--r-- | client/pool-buffer.c | 46 | ||||
-rw-r--r-- | include/pool-buffer.h | 2 | ||||
-rw-r--r-- | include/sway/commands.h | 1 | ||||
-rw-r--r-- | include/sway/config.h | 1 | ||||
-rw-r--r-- | include/sway/criteria.h | 1 | ||||
-rw-r--r-- | include/sway/tree/layout.h | 2 | ||||
-rw-r--r-- | include/sway/tree/view.h | 2 | ||||
-rw-r--r-- | include/swaylock/swaylock.h | 3 | ||||
-rw-r--r-- | sway/commands.c | 1 | ||||
-rw-r--r-- | sway/commands/bind.c | 48 | ||||
-rw-r--r-- | sway/commands/swap.c | 80 | ||||
-rw-r--r-- | sway/criteria.c | 18 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 12 | ||||
-rw-r--r-- | sway/input/keyboard.c | 25 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/sway.5.scd | 23 | ||||
-rw-r--r-- | sway/tree/container.c | 94 | ||||
-rw-r--r-- | sway/tree/layout.c | 124 | ||||
-rw-r--r-- | sway/tree/view.c | 10 | ||||
-rw-r--r-- | swaylock/main.c | 56 | ||||
-rw-r--r-- | swaylock/password.c | 102 | ||||
-rw-r--r-- | swaylock/render.c | 4 |
22 files changed, 508 insertions, 148 deletions
diff --git a/client/pool-buffer.c b/client/pool-buffer.c index 1f54a77c..52438303 100644 --- a/client/pool-buffer.c +++ b/client/pool-buffer.c @@ -1,37 +1,56 @@ #define _XOPEN_SOURCE 500 #include <assert.h> #include <cairo/cairo.h> +#include <fcntl.h> +#include <pango/pangocairo.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> -#include <pango/pangocairo.h> #include <unistd.h> #include <wayland-client.h> #include "config.h" #include "pool-buffer.h" +static bool set_cloexec(int fd) { + long flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return false; + } + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + return false; + } + + return true; +} + static int create_pool_file(size_t size, char **name) { static const char template[] = "sway-client-XXXXXX"; const char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { + if (path == NULL) { + fprintf(stderr, "XDG_RUNTIME_DIR is not set\n"); return -1; } - int ts = (path[strlen(path) - 1] == '/'); - - *name = malloc( - strlen(template) + - strlen(path) + - (ts ? 0 : 1) + 1); - sprintf(*name, "%s%s%s", path, ts ? "" : "/", template); + size_t name_size = strlen(template) + 1 + strlen(path) + 1; + *name = malloc(name_size); + if (*name == NULL) { + fprintf(stderr, "allocation failed\n"); + return -1; + } + snprintf(*name, name_size, "%s/%s", path, template); int fd = mkstemp(*name); - if (fd < 0) { return -1; } + if (!set_cloexec(fd)) { + close(fd); + return -1; + } + if (ftruncate(fd, size) < 0) { close(fd); return -1; @@ -53,7 +72,7 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, struct pool_buffer *buf, int32_t width, int32_t height, uint32_t format) { uint32_t stride = width * 4; - uint32_t size = stride * height; + size_t size = stride * height; char *name; int fd = create_pool_file(size, &name); @@ -68,8 +87,10 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, free(name); fd = -1; + buf->size = size; buf->width = width; buf->height = height; + buf->data = data; buf->surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride); buf->cairo = cairo_create(buf->surface); @@ -92,6 +113,9 @@ void destroy_buffer(struct pool_buffer *buffer) { if (buffer->pango) { g_object_unref(buffer->pango); } + if (buffer->data) { + munmap(buffer->data, buffer->size); + } memset(buffer, 0, sizeof(struct pool_buffer)); } diff --git a/include/pool-buffer.h b/include/pool-buffer.h index 856f7c8c..54f5be06 100644 --- a/include/pool-buffer.h +++ b/include/pool-buffer.h @@ -12,6 +12,8 @@ struct pool_buffer { cairo_t *cairo; PangoContext *pango; uint32_t width, height; + void *data; + size_t size; bool busy; }; diff --git a/include/sway/commands.h b/include/sway/commands.h index d39ac56c..365068ae 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -144,6 +144,7 @@ sway_cmd cmd_splitt; sway_cmd cmd_splitv; sway_cmd cmd_sticky; sway_cmd cmd_swaybg_command; +sway_cmd cmd_swap; sway_cmd cmd_title_format; sway_cmd cmd_unmark; sway_cmd cmd_workspace; diff --git a/include/sway/config.h b/include/sway/config.h index 33f52156..118981e3 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -28,6 +28,7 @@ struct sway_variable { struct sway_binding { int order; bool release; + bool locked; bool bindcode; list_t *keys; uint32_t modifiers; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 74da132c..bd3ca0ac 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -18,6 +18,7 @@ struct criteria { char *target; // workspace or output name for `assign` criteria pcre *title; + pcre *shell; pcre *app_id; pcre *class; pcre *instance; diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index cc999871..2e0f2abf 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -69,4 +69,6 @@ struct sway_container *container_split(struct sway_container *child, void container_recursive_resize(struct sway_container *container, double amount, enum resize_edge edge); +void container_swap(struct sway_container *con1, struct sway_container *con2); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0fb8f1b3..a8bf4955 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -201,7 +201,7 @@ const char *view_get_window_role(struct sway_view *view); uint32_t view_get_window_type(struct sway_view *view); -const char *view_get_type(struct sway_view *view); +const char *view_get_shell(struct sway_view *view); void view_configure(struct sway_view *view, double ox, double oy, int width, int height); diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index dae823b8..2931fd61 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -56,6 +56,7 @@ struct swaylock_surface { struct zwlr_layer_surface_v1 *layer_surface; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; + bool frame_pending, dirty; uint32_t width, height; int32_t scale; char *output_name; @@ -74,5 +75,7 @@ void swaylock_handle_key(struct swaylock_state *state, xkb_keysym_t keysym, uint32_t codepoint); void render_frame(struct swaylock_surface *surface); void render_frames(struct swaylock_state *state); +void damage_surface(struct swaylock_surface *surface); +void damage_state(struct swaylock_state *state); #endif diff --git a/sway/commands.c b/sway/commands.c index 6cba0a1c..c3728afd 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -186,6 +186,7 @@ static struct cmd_handler command_handlers[] = { { "splith", cmd_splith }, { "splitt", cmd_splitt }, { "splitv", cmd_splitv }, + { "swap", cmd_swap }, { "title_format", cmd_title_format }, { "unmark", cmd_unmark }, }; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index cbabb07b..c6b3368a 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -83,20 +83,26 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { binding->keys = create_list(); binding->modifiers = 0; binding->release = false; + binding->locked = false; binding->bindcode = false; - // Handle --release - if (strcmp("--release", argv[0]) == 0) { - if (argc >= 3) { + // Handle --release and --locked + while (argc > 0) { + if (strcmp("--release", argv[0]) == 0) { binding->release = true; - argv++; - argc--; + } else if (strcmp("--locked", argv[0]) == 0) { + binding->locked = true; } else { - free_sway_binding(binding); - return cmd_results_new(CMD_FAILURE, "bindsym", - "Invalid bindsym command " - "(expected more than 2 arguments, got %d)", argc); + break; } + argv++; + argc--; + } + if (argc < 2) { + free_sway_binding(binding); + return cmd_results_new(CMD_FAILURE, "bindsym", + "Invalid bindsym command " + "(expected at least 2 non-option arguments, got %d)", argc); } binding->command = join_args(argv + 1, argc - 1); @@ -176,20 +182,26 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { binding->keys = create_list(); binding->modifiers = 0; binding->release = false; + binding->locked = false; binding->bindcode = true; - // Handle --release - if (strcmp("--release", argv[0]) == 0) { - if (argc >= 3) { + // Handle --release and --locked + while (argc > 0) { + if (strcmp("--release", argv[0]) == 0) { binding->release = true; - argv++; - argc--; + } else if (strcmp("--locked", argv[0]) == 0) { + binding->locked = true; } else { - free_sway_binding(binding); - return cmd_results_new(CMD_FAILURE, "bindcode", - "Invalid bindcode command " - "(expected more than 2 arguments, got %d)", argc); + break; } + argv++; + argc--; + } + if (argc < 2) { + free_sway_binding(binding); + return cmd_results_new(CMD_FAILURE, "bindcode", + "Invalid bindcode command " + "(expected at least 2 non-option arguments, got %d)", argc); } binding->command = join_args(argv + 1, argc - 1); diff --git a/sway/commands/swap.c b/sway/commands/swap.c new file mode 100644 index 00000000..e925ad33 --- /dev/null +++ b/sway/commands/swap.c @@ -0,0 +1,80 @@ +#include <strings.h> +#include <wlr/util/log.h> +#include "sway/commands.h" +#include "sway/tree/layout.h" +#include "sway/tree/view.h" +#include "stringop.h" + +static const char* EXPECTED_SYNTAX = + "Expected 'swap container with id|con_id|mark <arg>'"; + +static bool test_con_id(struct sway_container *container, void *con_id) { + return container->id == (size_t)con_id; +} + +static bool test_id(struct sway_container *container, void *id) { + xcb_window_t *wid = id; + return (container->type == C_VIEW + && container->sway_view->type == SWAY_VIEW_XWAYLAND + && container->sway_view->wlr_xwayland_surface->window_id == *wid); +} + +static bool test_mark(struct sway_container *container, void *mark) { + if (container->type == C_VIEW && container->sway_view->marks->length) { + return !list_seq_find(container->sway_view->marks, + (int (*)(const void *, const void *))strcmp, mark); + } + return false; +} + +struct cmd_results *cmd_swap(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) { + return error; + } + + if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { + return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); + } + + struct sway_container *current = config->handler_context.current_container; + struct sway_container *other; + + char *value = join_args(argv + 3, argc - 3); + if (strcasecmp(argv[2], "id") == 0) { + xcb_window_t id = strtol(value, NULL, 0); + other = container_find(&root_container, test_id, (void *)&id); + } else if (strcasecmp(argv[2], "con_id") == 0) { + size_t con_id = atoi(value); + other = container_find(&root_container, test_con_id, (void *)con_id); + } else if (strcasecmp(argv[2], "mark") == 0) { + other = container_find(&root_container, test_mark, (void *)value); + } else { + free(value); + return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); + } + + if (!other) { + error = cmd_results_new(CMD_FAILURE, "swap", + "Failed to find %s '%s'", argv[2], value); + } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) { + error = cmd_results_new(CMD_FAILURE, "swap", + "Can only swap with containers and views"); + } else if (container_has_anscestor(current, other) + || container_has_anscestor(other, current)) { + error = cmd_results_new(CMD_FAILURE, "swap", + "Cannot swap ancestor and descendant"); + } else if (current->layout == L_FLOATING || other->layout == L_FLOATING) { + error = cmd_results_new(CMD_FAILURE, "swap", + "Swapping with floating containers is not supported"); + } + + free(value); + + if (error) { + return error; + } + + container_swap(current, other); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/criteria.c b/sway/criteria.c index 4295cacc..dec5fed7 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -13,6 +13,7 @@ bool criteria_is_empty(struct criteria *criteria) { return !criteria->title + && !criteria->shell && !criteria->app_id && !criteria->class && !criteria->instance @@ -29,6 +30,7 @@ bool criteria_is_empty(struct criteria *criteria) { void criteria_destroy(struct criteria *criteria) { pcre_free(criteria->title); + pcre_free(criteria->shell); pcre_free(criteria->app_id); pcre_free(criteria->class); pcre_free(criteria->instance); @@ -53,6 +55,13 @@ static bool criteria_matches_view(struct criteria *criteria, } } + if (criteria->shell) { + const char *shell = view_get_shell(view); + if (!shell || regex_cmp(shell, criteria->shell) != 0) { + return false; + } + } + if (criteria->app_id) { const char *app_id = view_get_app_id(view); if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { @@ -206,6 +215,7 @@ enum criteria_token { T_FLOATING, T_ID, T_INSTANCE, + T_SHELL, T_TILING, T_TITLE, T_URGENT, @@ -229,6 +239,8 @@ static enum criteria_token token_from_name(char *name) { return T_ID; } else if (strcmp(name, "instance") == 0) { return T_INSTANCE; + } else if (strcmp(name, "shell") == 0) { + return T_SHELL; } else if (strcmp(name, "title") == 0) { return T_TITLE; } else if (strcmp(name, "urgent") == 0) { @@ -271,6 +283,9 @@ static char *get_focused_prop(enum criteria_token token) { case T_INSTANCE: value = view_get_instance(view); break; + case T_SHELL: + value = view_get_shell(view); + break; case T_TITLE: value = view_get_class(view); break; @@ -332,6 +347,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_TITLE: generate_regex(&criteria->title, effective_value); break; + case T_SHELL: + generate_regex(&criteria->shell, effective_value); + break; case T_APP_ID: generate_regex(&criteria->app_id, effective_value); break; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 9a0d282b..b2b95fa0 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -3,13 +3,14 @@ #include <stdlib.h> #include <wayland-server.h> #include <wlr/types/wlr_xdg_shell.h> +#include <wlr/util/edges.h> +#include "log.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" -#include "sway/server.h" #include "sway/tree/view.h" -#include "sway/input/seat.h" -#include "sway/input/input-manager.h" -#include "log.h" static const struct sway_view_child_impl popup_impl; @@ -248,7 +249,8 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); wlr_xdg_surface_ping(xdg_surface); - wlr_xdg_toplevel_set_maximized(xdg_surface, true); + wlr_xdg_toplevel_set_tiled(xdg_surface, WLR_EDGE_LEFT | WLR_EDGE_RIGHT | + WLR_EDGE_TOP | WLR_EDGE_BOTTOM); struct sway_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct sway_xdg_shell_view)); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c07557db..e873eea3 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -141,7 +141,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, */ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, xkb_keysym_t *pressed_keysyms, uint32_t modifiers, - enum wlr_key_state key_state) { + enum wlr_key_state key_state, bool locked) { // configured bindings int n = pressed_keysyms_length(pressed_keysyms); list_t *keysym_bindings = config->current_mode->keysym_bindings; @@ -149,7 +149,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, struct sway_binding *binding = keysym_bindings->items[i]; if (!binding_matches_key_state(binding, key_state) || modifiers ^ binding->modifiers || - n != binding->keys->length) { + n != binding->keys->length || locked > binding->locked) { continue; } @@ -174,7 +174,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, } static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, - struct sway_binding *binding, struct wlr_event_keyboard_key *event) { + struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) { assert(binding->bindcode); uint32_t keycode = event->keycode + 8; @@ -183,6 +183,10 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, return false; } + if (locked > binding->locked) { + return false; + } + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); if (modifiers ^ binding->modifiers) { return false; @@ -265,13 +269,13 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, * should be propagated to clients. */ static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, - struct wlr_event_keyboard_key *event) { + struct wlr_event_keyboard_key *event, bool locked) { struct wlr_keyboard *wlr_keyboard = keyboard->seat_device->input_device->wlr_device->keyboard; list_t *keycode_bindings = config->current_mode->keycode_bindings; for (int i = 0; i < keycode_bindings->length; ++i) { struct sway_binding *binding = keycode_bindings->items[i]; - if (binding_matches_keycodes(wlr_keyboard, binding, event)) { + if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) { keyboard_execute_command(keyboard, binding); return true; } @@ -333,19 +337,20 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { keyboard->seat_device->input_device->wlr_device; wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); struct wlr_event_keyboard_key *event = data; + bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; xkb_keycode_t keycode = event->keycode + 8; bool handled = false; // handle keycodes - handled = keyboard_execute_bindcode(keyboard, event); + handled = keyboard_execute_bindcode(keyboard, event, input_inhibited); // handle translated keysyms if (!handled && event->state == WLR_KEY_RELEASED) { handled = keyboard_execute_bindsym(keyboard, keyboard->pressed_keysyms_translated, keyboard->modifiers_translated, - event->state); + event->state, input_inhibited); } const xkb_keysym_t *translated_keysyms; size_t translated_keysyms_len = @@ -357,14 +362,14 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { handled = keyboard_execute_bindsym(keyboard, keyboard->pressed_keysyms_translated, keyboard->modifiers_translated, - event->state); + event->state, input_inhibited); } // Handle raw keysyms if (!handled && event->state == WLR_KEY_RELEASED) { handled = keyboard_execute_bindsym(keyboard, keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, - event->state); + event->state, input_inhibited); } const xkb_keysym_t *raw_keysyms; size_t raw_keysyms_len = @@ -374,7 +379,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { if (!handled && event->state == WLR_KEY_PRESSED) { handled = keyboard_execute_bindsym(keyboard, keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, - event->state); + event->state, input_inhibited); } // Compositor bindings diff --git a/sway/meson.build b/sway/meson.build index 72347d51..9c942e8e 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -63,6 +63,7 @@ sway_sources = files( 'commands/show_marks.c', 'commands/split.c', 'commands/swaybg_command.c', + 'commands/swap.c', 'commands/title_format.c', 'commands/unmark.c', 'commands/workspace.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index ff138562..5d99c9d6 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -167,6 +167,15 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). "Sticks" a floating window to the current output so that it shows up on all workspaces. +*swap* container with id|con\_id|mark <arg> + Swaps the position, geometry, and fullscreen status of two containers. The + first container can be selected either by criteria or focus. The second + container can be selected by _id_, _con\_id_, or _mark_. _id_ can only be + used with xwayland views. If the first container has focus, it will retain + focus unless it is moved to a different workspace or the second container + becomes fullscreen on the same workspace as the first container. In either + of those cases, the second container will gain focus. + The following commands may be used either in the configuration file or at runtime. @@ -177,17 +186,20 @@ runtime. for\_window <criteria> move container to workspace <workspace> -*bindsym* <key combo> <command> +*bindsym* [--release|--locked] <key combo> <command> Binds _key combo_ to execute the sway command _command_ when pressed. You may use XKB key names here (*xev*(1) is a good tool for discovering these). + With the flag _--release_, the command is executed when the key combo is + released. Unless the flag _--locked_ is set, the command will not be run + when a screen locking program is active. Example: # Execute firefox when alt, shift, and f are pressed together bindsym Mod1+Shift+f exec firefox - *bindcode* <code> <command> is also available for binding with key codes - instead of key names. + *bindcode* [--release|--locked] <code> <command> is also available for + binding with key codes instead of key names. *client.<class>* <border> <background> <text> <indicator> <child\_border> Configures the color of window borders and title bars. All 5 colors are @@ -551,6 +563,11 @@ The following attributes may be matched with: value is \_\_focused\_\_, then the window instance must be the same as that of the currently focused window. +*shell* + Compare value against the window shell, such as "xdg\_shell" or "xwayland". + Can be a regular expression. If value is \_\_focused\_\_, then the shell + must be the same as that of the currently focused window. + *tiling* Matches tiling windows. diff --git a/sway/tree/container.c b/sway/tree/container.c index f29a9adc..a4798c7e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -176,45 +176,6 @@ static void _container_destroy(struct sway_container *cont) { free(cont); } -static struct sway_container *container_output_destroy( - struct sway_container *output) { - if (!sway_assert(output, "cannot destroy null output")) { - return NULL; - } - - if (output->children->length > 0) { - // TODO save workspaces when there are no outputs. - // TODO also check if there will ever be no outputs except for exiting - // program - if (root_container.children->length > 1) { - int p = root_container.children->items[0] == output; - // Move workspace from this output to another output - while (output->children->length) { - struct sway_container *child = output->children->items[0]; - container_remove_child(child); - container_add_child(root_container.children->items[p], child); - } - container_sort_workspaces(root_container.children->items[p]); - arrange_output(root_container.children->items[p]); - } - } - - wl_list_remove(&output->sway_output->destroy.link); - wl_list_remove(&output->sway_output->mode.link); - wl_list_remove(&output->sway_output->transform.link); - wl_list_remove(&output->sway_output->scale.link); - - wl_list_remove(&output->sway_output->damage_destroy.link); - wl_list_remove(&output->sway_output->damage_frame.link); - - // clear the wlr_output reference to this container - output->sway_output->wlr_output->data = NULL; - - wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); - _container_destroy(output); - return &root_container; -} - static struct sway_container *container_workspace_destroy( struct sway_container *workspace) { if (!sway_assert(workspace, "cannot destroy null workspace")) { @@ -232,7 +193,7 @@ static struct sway_container *container_workspace_destroy( // destroy the WS if there are no children (TODO check for floating) wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); ipc_event_workspace(workspace, NULL, "empty"); - } else { + } else if (output) { // Move children to a different workspace on this output struct sway_container *new_workspace = NULL; // TODO move floating @@ -253,11 +214,62 @@ static struct sway_container *container_workspace_destroy( free(workspace->sway_workspace); _container_destroy(workspace); - output_damage_whole(output->sway_output); + if (output) { + output_damage_whole(output->sway_output); + } return parent; } +static struct sway_container *container_output_destroy( + struct sway_container *output) { + if (!sway_assert(output, "cannot destroy null output")) { + return NULL; + } + + if (output->children->length > 0) { + // TODO save workspaces when there are no outputs. + // TODO also check if there will ever be no outputs except for exiting + // program + if (root_container.children->length > 1) { + // Move workspace from this output to another output + struct sway_container *other_output = + root_container.children->items[0]; + if (other_output == output) { + other_output = root_container.children->items[1]; + } + + while (output->children->length) { + struct sway_container *workspace = output->children->items[0]; + container_remove_child(workspace); + if (workspace->children->length > 0) { + container_add_child(other_output, workspace); + ipc_event_workspace(workspace, NULL, "move"); + } else { + container_workspace_destroy(workspace); + } + } + container_sort_workspaces(other_output); + arrange_output(other_output); + } + } + + wl_list_remove(&output->sway_output->destroy.link); + wl_list_remove(&output->sway_output->mode.link); + wl_list_remove(&output->sway_output->transform.link); + wl_list_remove(&output->sway_output->scale.link); + + wl_list_remove(&output->sway_output->damage_destroy.link); + wl_list_remove(&output->sway_output->damage_frame.link); + + // clear the wlr_output reference to this container + output->sway_output->wlr_output->data = NULL; + + wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); + _container_destroy(output); + return &root_container; +} + static void container_root_finish(struct sway_container *con) { wlr_log(L_ERROR, "TODO: destroy the root container"); } diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 21cec529..624d5516 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -882,3 +882,127 @@ void container_recursive_resize(struct sway_container *container, } } } + +static void swap_places(struct sway_container *con1, + struct sway_container *con2) { + struct sway_container *temp = malloc(sizeof(struct sway_container)); + temp->x = con1->x; + temp->y = con1->y; + temp->width = con1->width; + temp->height = con1->height; + temp->parent = con1->parent; + + con1->x = con2->x; + con1->y = con2->y; + con1->width = con2->width; + con1->height = con2->height; + + con2->x = temp->x; + con2->y = temp->y; + con2->width = temp->width; + con2->height = temp->height; + + int temp_index = index_child(con1); + container_insert_child(con2->parent, con1, index_child(con2)); + container_insert_child(temp->parent, con2, temp_index); + + free(temp); +} + +static void swap_focus(struct sway_container *con1, + struct sway_container *con2, struct sway_seat *seat, + struct sway_container *focus) { + if (focus == con1 || focus == con2) { + struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); + struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); + if (focus == con1 && (con2->parent->layout == L_TABBED + || con2->parent->layout == L_STACKED)) { + if (workspace_is_visible(ws2)) { + seat_set_focus_warp(seat, con2, false); + } + seat_set_focus(seat, ws1 != ws2 ? con2 : con1); + } else if (focus == con2 && (con1->parent->layout == L_TABBED + || con1->parent->layout == L_STACKED)) { + if (workspace_is_visible(ws1)) { + seat_set_focus_warp(seat, con1, false); + } + seat_set_focus(seat, ws1 != ws2 ? con1 : con2); + } else if (ws1 != ws2) { + seat_set_focus(seat, focus == con1 ? con2 : con1); + } else { + seat_set_focus(seat, focus); + } + } else { + seat_set_focus(seat, focus); + } +} + +void container_swap(struct sway_container *con1, struct sway_container *con2) { + if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { + return; + } + if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER, + "Can only swap containers and views")) { + return; + } + if (!sway_assert(!container_has_anscestor(con1, con2) + && !container_has_anscestor(con2, con1), + "Cannot swap anscestor and descendant")) { + return; + } + if (!sway_assert(con1->layout != L_FLOATING && con2->layout != L_FLOATING, + "Swapping with floating containers is not supported")) { + return; + } + + wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); + + int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; + int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; + if (fs1) { + view_set_fullscreen(con1->sway_view, false); + } + if (fs2) { + view_set_fullscreen(con2->sway_view, false); + } + + struct sway_seat *seat = input_manager_get_default_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *vis1 = container_parent( + seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), + C_WORKSPACE); + struct sway_container *vis2 = container_parent( + seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)), + C_WORKSPACE); + + char *stored_prev_name = NULL; + if (prev_workspace_name) { + stored_prev_name = strdup(prev_workspace_name); + } + + swap_places(con1, con2); + + if (!workspace_is_visible(vis1)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, vis1)); + } + if (!workspace_is_visible(vis2)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, vis2)); + } + + swap_focus(con1, con2, seat, focus); + + if (stored_prev_name) { + free(prev_workspace_name); + prev_workspace_name = stored_prev_name; + } + + arrange_children_of(con1->parent); + arrange_children_of(con2->parent); + + if (fs1 && con2->type == C_VIEW) { + view_set_fullscreen(con2->sway_view, true); + } + if (fs2 && con1->type == C_VIEW) { + view_set_fullscreen(con1->sway_view, true); + } +} diff --git a/sway/tree/view.c b/sway/tree/view.c index 812d7740..d91182ed 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -107,7 +107,7 @@ uint32_t view_get_window_type(struct sway_view *view) { return 0; } -const char *view_get_type(struct sway_view *view) { +const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL_V6: return "xdg_shell_v6"; @@ -654,10 +654,12 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { return title ? strlen(title) : 0; } const char *title = view_get_title(view); + const char *app_id = view_get_app_id(view); const char *class = view_get_class(view); const char *instance = view_get_instance(view); - const char *shell = view_get_type(view); + const char *shell = view_get_shell(view); size_t title_len = title ? strlen(title) : 0; + size_t app_id_len = app_id ? strlen(app_id) : 0; size_t class_len = class ? strlen(class) : 0; size_t instance_len = instance ? strlen(instance) : 0; size_t shell_len = shell ? strlen(shell) : 0; @@ -675,6 +677,10 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { lenient_strcat(buffer, title); len += title_len; format += 6; + } else if (strncmp(next, "%app_id", 7) == 0) { + lenient_strcat(buffer, app_id); + len += app_id_len; + format += 7; } else if (strncmp(next, "%class", 6) == 0) { lenient_strcat(buffer, class); len += class_len; diff --git a/swaylock/main.c b/swaylock/main.c index f89f2849..591df7b4 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -131,14 +131,58 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = layer_surface_closed, }; -static void handle_wl_output_geometry(void *data, struct wl_output *output, int32_t x, - int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, - const char *make, const char *model, int32_t transform) { +static const struct wl_callback_listener surface_frame_listener; + +static void surface_frame_handle_done(void *data, struct wl_callback *callback, + uint32_t time) { + struct swaylock_surface *surface = data; + + wl_callback_destroy(callback); + surface->frame_pending = false; + + if (surface->dirty) { + // Schedule a frame in case the surface is damaged again + struct wl_callback *callback = wl_surface_frame(surface->surface); + wl_callback_add_listener(callback, &surface_frame_listener, surface); + surface->frame_pending = true; + + render_frame(surface); + surface->dirty = false; + } +} + +static const struct wl_callback_listener surface_frame_listener = { + .done = surface_frame_handle_done, +}; + +void damage_surface(struct swaylock_surface *surface) { + surface->dirty = true; + if (surface->frame_pending) { + return; + } + + struct wl_callback *callback = wl_surface_frame(surface->surface); + wl_callback_add_listener(callback, &surface_frame_listener, surface); + surface->frame_pending = true; + wl_surface_commit(surface->surface); +} + +void damage_state(struct swaylock_state *state) { + struct swaylock_surface *surface; + wl_list_for_each(surface, &state->surfaces, link) { + damage_surface(surface); + } +} + +static void handle_wl_output_geometry(void *data, struct wl_output *output, + int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { // Who cares } -static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) { +static void handle_wl_output_mode(void *data, struct wl_output *output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { // Who cares } @@ -151,7 +195,7 @@ static void handle_wl_output_scale(void *data, struct wl_output *output, struct swaylock_surface *surface = data; surface->scale = factor; if (surface->state->run_display) { - render_frames(surface->state); + damage_surface(surface); } } diff --git a/swaylock/password.c b/swaylock/password.c index 1ad5cd81..6d493309 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -93,58 +93,58 @@ static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { void swaylock_handle_key(struct swaylock_state *state, xkb_keysym_t keysym, uint32_t codepoint) { switch (keysym) { - case XKB_KEY_KP_Enter: /* fallthrough */ - case XKB_KEY_Return: - state->auth_state = AUTH_STATE_VALIDATING; - render_frames(state); - wl_display_roundtrip(state->display); - if (attempt_password(&state->password)) { - state->run_display = false; - break; - } - state->auth_state = AUTH_STATE_INVALID; - render_frames(state); + case XKB_KEY_KP_Enter: /* fallthrough */ + case XKB_KEY_Return: + state->auth_state = AUTH_STATE_VALIDATING; + damage_state(state); + wl_display_roundtrip(state->display); + if (attempt_password(&state->password)) { + state->run_display = false; break; - case XKB_KEY_Delete: - case XKB_KEY_BackSpace: - if (backspace(&state->password)) { - state->auth_state = AUTH_STATE_BACKSPACE; - } else { - state->auth_state = AUTH_STATE_CLEAR; - } - render_frames(state); - break; - case XKB_KEY_Escape: - clear_password_buffer(&state->password); + } + state->auth_state = AUTH_STATE_INVALID; + damage_state(state); + break; + case XKB_KEY_Delete: + case XKB_KEY_BackSpace: + if (backspace(&state->password)) { + state->auth_state = AUTH_STATE_BACKSPACE; + } else { state->auth_state = AUTH_STATE_CLEAR; - render_frames(state); - break; - case XKB_KEY_Caps_Lock: - /* The state is getting active after this - * so we need to manually toggle it */ - state->xkb.caps_lock = !state->xkb.caps_lock; - state->auth_state = AUTH_STATE_INPUT_NOP; - render_frames(state); - break; - case XKB_KEY_Shift_L: - case XKB_KEY_Shift_R: - case XKB_KEY_Control_L: - case XKB_KEY_Control_R: - case XKB_KEY_Meta_L: - case XKB_KEY_Meta_R: - case XKB_KEY_Alt_L: - case XKB_KEY_Alt_R: - case XKB_KEY_Super_L: - case XKB_KEY_Super_R: - state->auth_state = AUTH_STATE_INPUT_NOP; - render_frames(state); - break; - default: - if (codepoint) { - append_ch(&state->password, codepoint); - state->auth_state = AUTH_STATE_INPUT; - render_frames(state); - } - break; + } + damage_state(state); + break; + case XKB_KEY_Escape: + clear_password_buffer(&state->password); + state->auth_state = AUTH_STATE_CLEAR; + damage_state(state); + break; + case XKB_KEY_Caps_Lock: + /* The state is getting active after this + * so we need to manually toggle it */ + state->xkb.caps_lock = !state->xkb.caps_lock; + state->auth_state = AUTH_STATE_INPUT_NOP; + damage_state(state); + break; + case XKB_KEY_Shift_L: + case XKB_KEY_Shift_R: + case XKB_KEY_Control_L: + case XKB_KEY_Control_R: + case XKB_KEY_Meta_L: + case XKB_KEY_Meta_R: + case XKB_KEY_Alt_L: + case XKB_KEY_Alt_R: + case XKB_KEY_Super_L: + case XKB_KEY_Super_R: + state->auth_state = AUTH_STATE_INPUT_NOP; + damage_state(state); + break; + default: + if (codepoint) { + append_ch(&state->password, codepoint); + state->auth_state = AUTH_STATE_INPUT; + damage_state(state); + } + break; } } diff --git a/swaylock/render.c b/swaylock/render.c index 05236dea..2032ddcf 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -23,6 +23,10 @@ void render_frame(struct swaylock_surface *surface) { surface->current_buffer = get_next_buffer(state->shm, surface->buffers, buffer_width, buffer_height); + if (surface->current_buffer == NULL) { + return; + } + cairo_t *cairo = surface->current_buffer->cairo; cairo_identity_matrix(cairo); |