diff options
Diffstat (limited to 'sway')
61 files changed, 2797 insertions, 1211 deletions
diff --git a/sway/commands.c b/sway/commands.c index 91e559a1..6e473b97 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -46,6 +46,7 @@ static const struct cmd_handler handlers[] = { { "assign", cmd_assign }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, + { "bindgesture", cmd_bindgesture }, { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "client.background", cmd_client_noop }, @@ -96,6 +97,7 @@ static const struct cmd_handler handlers[] = { { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, { "unbindcode", cmd_unbindcode }, + { "unbindgesture", cmd_unbindgesture }, { "unbindswitch", cmd_unbindswitch }, { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, @@ -412,6 +414,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode && handler->handle != cmd_bindswitch + && handler->handle != cmd_bindgesture && handler->handle != cmd_set && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { @@ -487,13 +490,19 @@ struct cmd_results *cmd_results_new(enum cmd_status status, } results->status = status; if (format) { - char *error = malloc(256); + char *error = NULL; va_list args; va_start(args, format); - if (error) { - vsnprintf(error, 256, format, args); - } + int slen = vsnprintf(NULL, 0, format, args); va_end(args); + if (slen > 0) { + error = malloc(slen + 1); + if (error != NULL) { + va_start(args, format); + vsnprintf(error, slen + 1, format, args); + va_end(args); + } + } results->error = error; } else { results->error = NULL; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 25be415e..c0b383db 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -47,7 +47,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a, if (binding_a->type != binding_b->type) { return false; } - if (binding_a->state != binding_b->state) { + if (binding_a->trigger != binding_b->trigger) { return false; } if ((binding_a->flags & BINDING_LOCKED) != @@ -372,6 +372,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, strlen("--input-device=")) == 0) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); + strip_quotes(binding->input); } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; } else if (strcmp("--no-repeat", argv[0]) == 0) { @@ -551,11 +552,11 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, "unknown switch %s)", bindtype, split->items[0]); } if (strcmp(split->items[1], "on") == 0) { - binding->state = WLR_SWITCH_STATE_ON; + binding->trigger = SWAY_SWITCH_TRIGGER_ON; } else if (strcmp(split->items[1], "off") == 0) { - binding->state = WLR_SWITCH_STATE_OFF; + binding->trigger = SWAY_SWITCH_TRIGGER_OFF; } else if (strcmp(split->items[1], "toggle") == 0) { - binding->state = WLR_SWITCH_STATE_TOGGLE; + binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE; } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index b35065c1..e6b09e64 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -8,6 +8,8 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/server.h" +#include "sway/desktop/launcher.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" @@ -25,11 +27,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { return error; } +static void export_xdga_token(struct launcher_ctx *ctx) { + const char *token = launcher_ctx_get_token_name(ctx); + setenv("XDG_ACTIVATION_TOKEN", token, 1); +} + +static void export_startup_id(struct launcher_ctx *ctx) { + const char *token = launcher_ctx_get_token_name(ctx); + setenv("DESKTOP_STARTUP_ID", token, 1); +} + struct cmd_results *cmd_exec_process(int argc, char **argv) { struct cmd_results *error = NULL; char *cmd = NULL; + bool no_startup_id = false; if (strcmp(argv[0], "--no-startup-id") == 0) { - sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); + no_startup_id = true; --argc; ++argv; if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { return error; @@ -51,6 +64,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { } pid_t pid, child; + struct launcher_ctx *ctx = launcher_ctx_create(); // Fork process if ((pid = fork()) == 0) { // Fork child process again @@ -63,6 +77,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { close(fd[0]); if ((child = fork()) == 0) { close(fd[1]); + if (ctx) { + export_xdga_token(ctx); + } + if (ctx && !no_startup_id) { + export_startup_id(ctx); + } execlp("sh", "sh", "-c", cmd, (void *)NULL); sway_log_errno(SWAY_ERROR, "execlp failed"); _exit(1); @@ -90,8 +110,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { waitpid(pid, NULL, 0); if (child > 0) { sway_log(SWAY_DEBUG, "Child process created with pid %d", child); - root_record_workspace_pid(child); + if (ctx != NULL) { + sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); + ctx->pid = child; + } } else { + launcher_ctx_destroy(ctx); return cmd_results_new(CMD_FAILURE, "Second fork() failed"); } diff --git a/sway/commands/focus.c b/sway/commands/focus.c index b8d28480..facd82de 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container, } else { return false; } - + return true; } @@ -285,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, } } else { return cmd_results_new(CMD_FAILURE, - "Failed to find a %s container in workspace", + "Failed to find a %s container in workspace.", floating ? "floating" : "tiling"); } return cmd_results_new(CMD_SUCCESS, NULL); @@ -295,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, int argc, char **argv) { if (!argc) { return cmd_results_new(CMD_INVALID, - "Expected 'focus output <direction|name>'"); + "Expected 'focus output <direction|name>'."); } char *identifier = join_args(argv, argc); struct sway_output *output = output_by_name_or_id(identifier); @@ -305,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat, if (!parse_direction(identifier, &direction)) { free(identifier); return cmd_results_new(CMD_INVALID, - "There is no output with that name"); + "There is no output with that name."); } struct sway_workspace *ws = seat_get_focused_workspace(seat); if (!ws) { free(identifier); return cmd_results_new(CMD_FAILURE, - "No focused workspace to base directions off of"); + "No focused workspace to base directions off of."); } output = output_get_in_direction(ws->output, direction); @@ -375,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) { struct sway_seat *seat = config->handler_context.seat; if (node->type < N_WORKSPACE) { return cmd_results_new(CMD_FAILURE, - "Command 'focus' cannot be used above the workspace level"); + "Command 'focus' cannot be used above the workspace level."); } - if (argc == 0 && container) { + if (argc == 0) { + if (!container) { + return cmd_results_new(CMD_FAILURE, "No container to focus was specified."); + } + if (container_is_scratchpad_hidden_or_child(container)) { root_scratchpad_show(container); } diff --git a/sway/commands/font.c b/sway/commands/font.c index cea720f5..74bb6b9f 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -4,6 +4,7 @@ #include "sway/config.h" #include "log.h" #include "stringop.h" +#include <pango/pangocairo.h> struct cmd_results *cmd_font(int argc, char **argv) { struct cmd_results *error = NULL; @@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { if (strncmp(font, "pango:", 6) == 0) { config->pango_markup = true; config->font = strdup(font + 6); + free(font); } else { config->pango_markup = false; - config->font = strdup(font); + config->font = font; } - free(font); + // Parse the font early so we can reject it if it's not valid for pango. + // Also avoids re-parsing each time we render text. + PangoFontDescription *font_description = pango_font_description_from_string(config->font); + + const char *family = pango_font_description_get_family(font_description); + if (family == NULL) { + pango_font_description_free(font_description); + return cmd_results_new(CMD_FAILURE, "Invalid font family."); + } + + const gint size = pango_font_description_get_size(font_description); + if (size == 0) { + pango_font_description_free(font_description); + return cmd_results_new(CMD_FAILURE, "Invalid font size."); + } + + if (config->font_description != NULL) { + pango_font_description_free(config->font_description); + } + + config->font_description = font_description; config_update_font_height(); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c new file mode 100644 index 00000000..d4442cc3 --- /dev/null +++ b/sway/commands/gesture.c @@ -0,0 +1,166 @@ +#define _POSIX_C_SOURCE 200809L +#include "sway/config.h" + +#include "gesture.h" +#include "log.h" +#include "stringop.h" +#include "sway/commands.h" + +void free_gesture_binding(struct sway_gesture_binding *binding) { + if (!binding) { + return; + } + free(binding->input); + free(binding->command); + free(binding); +} + +/** + * Returns true if the bindings have the same gesture type, direction, etc + */ +static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, + struct sway_gesture_binding *binding_b) { + if (strcmp(binding_a->input, binding_b->input) != 0) { + return false; + } + + if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { + return false; + } + + if ((binding_a->flags & BINDING_EXACT) != + (binding_b->flags & BINDING_EXACT)) { + return false; + } + return true; +} + +/** + * Add gesture binding to config + */ +static struct cmd_results *gesture_binding_add( + struct sway_gesture_binding *binding, + const char *gesturecombo, bool warn) { + list_t *mode_bindings = config->current_mode->gesture_bindings; + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_gesture_binding *config_binding = mode_bindings->items[i]; + if (binding_gesture_equal(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", + gesturecombo, binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' to `%s` from `%s`", + gesturecombo, binding->command, + config_binding->command); + } + free_gesture_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", + gesturecombo, binding->command); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +/** + * Remove gesture binding from config + */ +static struct cmd_results *gesture_binding_remove( + struct sway_gesture_binding *binding, const char *gesturecombo) { + list_t *mode_bindings = config->current_mode->gesture_bindings; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_gesture_binding *config_binding = mode_bindings->items[i]; + if (binding_gesture_equal(binding, config_binding)) { + free_gesture_binding(config_binding); + free_gesture_binding(binding); + list_del(mode_bindings, i); + sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", + gesturecombo); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + free_gesture_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", + gesturecombo); +} + +/** + * Parse and execute bindgesture or unbindgesture command. + */ +static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { + int minargs = 2; + char *bindtype = "bindgesture"; + if (unbind) { + minargs--; + bindtype = "unbindgesture"; + } + + struct cmd_results *error = NULL; + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { + return error; + } + struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); + if (!binding) { + return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); + } + binding->input = strdup("*"); + + bool warn = true; + + // Handle flags + while (argc > 0) { + if (strcmp("--exact", argv[0]) == 0) { + binding->flags |= BINDING_EXACT; + } else if (strcmp("--no-warn", argv[0]) == 0) { + warn = false; + } else if (strncmp("--input-device=", argv[0], + strlen("--input-device=")) == 0) { + free(binding->input); + binding->input = strdup(argv[0] + strlen("--input-device=")); + } else { + break; + } + argv++; + argc--; + } + + if (argc < minargs) { + free(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid %s command (expected at least %d " + "non-option arguments, got %d)", bindtype, minargs, argc); + } + + char* errmsg = NULL; + if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { + free(binding); + struct cmd_results *final = cmd_results_new(CMD_FAILURE, + "Invalid %s command (%s)", + bindtype, errmsg); + free(errmsg); + return final; + } + + if (unbind) { + return gesture_binding_remove(binding, argv[0]); + } + binding->command = join_args(argv + 1, argc - 1); + return gesture_binding_add(binding, argv[0], warn); +} + +struct cmd_results *cmd_bindgesture(int argc, char **argv) { + return cmd_bind_or_unbind_gesture(argc, argv, false); +} + +struct cmd_results *cmd_unbindgesture(int argc, char **argv) { + return cmd_bind_or_unbind_gesture(argc, argv, true); +} diff --git a/sway/commands/input.c b/sway/commands/input.c index 77acb671..ea531659 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = { { "drag", input_cmd_drag }, { "drag_lock", input_cmd_drag_lock }, { "dwt", input_cmd_dwt }, + { "dwtp", input_cmd_dwtp }, { "events", input_cmd_events }, { "left_handed", input_cmd_left_handed }, { "map_from_region", input_cmd_map_from_region }, diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c new file mode 100644 index 00000000..232e2b26 --- /dev/null +++ b/sway/commands/input/dwtp.c @@ -0,0 +1,25 @@ +#include <string.h> +#include <strings.h> +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_dwtp(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *ic = config->handler_context.input_config; + if (!ic) { + return cmd_results_new(CMD_FAILURE, "No input device defined."); + } + + if (parse_boolean(argv[0], true)) { + ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED; + } else { + ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED; + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index d6548a68..3cce4ec8 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -1,10 +1,16 @@ #define _POSIX_C_SOURCE 200809L #include <assert.h> +#include <wlr/interfaces/wlr_keyboard.h> #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" #include "log.h" +struct xkb_switch_layout_action { + struct wlr_keyboard *keyboard; + xkb_layout_index_t layout; +}; + static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); if (idx >= num_layouts) { @@ -28,10 +34,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { return layout_idx; } -static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { +static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) { xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); xkb_layout_index_t idx = get_current_layout_index(kbd); - switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); + return (idx + num_layouts + dir) % num_layouts; } struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { @@ -66,6 +72,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { relative = 0; } + struct xkb_switch_layout_action *actions = calloc( + wl_list_length(&server.input->devices), + sizeof(struct xkb_switch_layout_action)); + size_t actions_len = 0; + + if (!actions) { + return cmd_results_new(CMD_FAILURE, "Unable to allocate actions"); + } + + /* Calculate new indexes first because switching a layout in one + keyboard may result in a change on other keyboards as well because + of keyboard groups. */ struct sway_input_device *dev; wl_list_for_each(dev, &server.input->devices, link) { if (strcmp(ic->identifier, "*") != 0 && @@ -76,12 +94,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { continue; } + + struct xkb_switch_layout_action *action = + &actions[actions_len++]; + + action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); if (relative) { - switch_layout_relative(dev->wlr_device->keyboard, relative); + action->layout = get_layout_relative(action->keyboard, relative); } else { - switch_layout(dev->wlr_device->keyboard, layout); + action->layout = layout; } } + for (size_t i = 0; i < actions_len; i++) { + switch_layout(actions[i].keyboard, actions[i].layout); + } + free(actions); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/mode.c b/sway/commands/mode.c index e23e4ee4..7263efcb 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -11,10 +11,12 @@ // Must be in order for the bsearch static const struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, + { "bindgesture", cmd_bindgesture }, { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "set", cmd_set }, { "unbindcode", cmd_unbindcode }, + { "unbindgesture", cmd_unbindgesture }, { "unbindswitch", cmd_unbindswitch }, { "unbindsym", cmd_unbindsym }, }; @@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->keycode_bindings = create_list(); mode->mouse_bindings = create_list(); mode->switch_bindings = create_list(); + mode->gesture_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 1a05a7a6..7bd1fe3e 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -686,6 +686,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { arrange_output(old_output); arrange_output(new_output); + struct sway_seat *seat = config->handler_context.seat; + seat_consider_warp_to_focus(seat); + return cmd_results_new(CMD_SUCCESS, NULL); } @@ -788,15 +791,15 @@ static struct cmd_results *cmd_move_to_position_pointer( struct wlr_output *output = wlr_output_layout_output_at( root->output_layout, cursor->x, cursor->y); if (output) { - struct wlr_box *box = - wlr_output_layout_get_box(root->output_layout, output); - lx = fmax(lx, box->x); - ly = fmax(ly, box->y); - if (lx + container->pending.width > box->x + box->width) { - lx = box->x + box->width - container->pending.width; + struct wlr_box box; + wlr_output_layout_get_box(root->output_layout, output, &box); + lx = fmax(lx, box.x); + ly = fmax(ly, box.y); + if (lx + container->pending.width > box.x + box.width) { + lx = box.x + box.width - container->pending.width; } - if (ly + container->pending.height > box->y + box->height) { - ly = box->y + box->height - container->pending.height; + if (ly + container->pending.height > box.y + box.height) { + ly = box.y + box.height - container->pending.height; } } diff --git a/sway/commands/output.c b/sway/commands/output.c index 125df5a7..df32c673 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = { { "modeline", output_cmd_modeline }, { "pos", output_cmd_position }, { "position", output_cmd_position }, + { "power", output_cmd_power }, { "render_bit_depth", output_cmd_render_bit_depth }, { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, @@ -26,6 +27,7 @@ static const struct cmd_handler output_handlers[] = { { "subpixel", output_cmd_subpixel }, { "toggle", output_cmd_toggle }, { "transform", output_cmd_transform }, + { "unplug", output_cmd_unplug }, }; struct cmd_results *cmd_output(int argc, char **argv) { diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 68ee9fe1..67f212ff 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -102,19 +102,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } char *conf_path = dirname(conf); - char *rel_path = src; - src = malloc(strlen(conf_path) + strlen(src) + 2); - if (!src) { - free(rel_path); + char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); + if (!real_src) { + free(src); free(conf); sway_log(SWAY_ERROR, "Unable to allocate memory"); return cmd_results_new(CMD_FAILURE, "Unable to allocate resources"); } - sprintf(src, "%s/%s", conf_path, rel_path); - free(rel_path); + snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); + free(src); free(conf); + src = real_src; } bool can_access = access(src, F_OK) != -1; diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 638c0ade..c7adbd58 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c @@ -1,45 +1,8 @@ +#include "log.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" -#include "util.h" -#include <strings.h> struct cmd_results *output_cmd_dpms(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - if (!argc) { - return cmd_results_new(CMD_INVALID, "Missing dpms argument."); - } - - enum config_dpms current_dpms = DPMS_ON; - - if (strcasecmp(argv[0], "toggle") == 0) { - - const char *oc_name = config->handler_context.output_config->name; - if (strcmp(oc_name, "*") == 0) { - return cmd_results_new(CMD_INVALID, - "Cannot apply toggle to all outputs."); - } - - struct sway_output *sway_output = all_output_by_name_or_id(oc_name); - if (!sway_output || !sway_output->wlr_output) { - return cmd_results_new(CMD_FAILURE, - "Cannot apply toggle to unknown output %s", oc_name); - } - - if (sway_output->enabled && !sway_output->wlr_output->enabled) { - current_dpms = DPMS_OFF; - } - } - - if (parse_boolean(argv[0], current_dpms == DPMS_ON)) { - config->handler_context.output_config->dpms_state = DPMS_ON; - } else { - config->handler_context.output_config->dpms_state = DPMS_OFF; - } - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; + sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, " + "use \"output power\" instead"); + return output_cmd_power(argc, argv); } diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c new file mode 100644 index 00000000..e6ae2852 --- /dev/null +++ b/sway/commands/output/power.c @@ -0,0 +1,43 @@ +#include <strings.h> +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" +#include "util.h" + +struct cmd_results *output_cmd_power(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (argc == 0) { + return cmd_results_new(CMD_INVALID, "Missing power argument"); + } + + bool current = true; + if (strcasecmp(argv[0], "toggle") == 0) { + const char *oc_name = config->handler_context.output_config->name; + if (strcmp(oc_name, "*") == 0) { + return cmd_results_new(CMD_INVALID, + "Cannot apply toggle to all outputs"); + } + + struct sway_output *sway_output = all_output_by_name_or_id(oc_name); + if (!sway_output || !sway_output->wlr_output) { + return cmd_results_new(CMD_FAILURE, + "Cannot apply toggle to unknown output %s", oc_name); + } + + if (sway_output->enabled && !sway_output->wlr_output->enabled) { + current = false; + } + } + + if (parse_boolean(argv[0], current)) { + config->handler_context.output_config->power = 1; + } else { + config->handler_context.output_config->power = 0; + } + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; +} diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c new file mode 100644 index 00000000..dfef626f --- /dev/null +++ b/sway/commands/output/unplug.c @@ -0,0 +1,54 @@ +#include <strings.h> +#include <wlr/config.h> +#include <wlr/backend/headless.h> +#include <wlr/backend/wayland.h> +#if WLR_HAS_X11_BACKEND +#include <wlr/backend/x11.h> +#endif +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +static bool is_backend_allowed(struct wlr_backend *backend) { + if (wlr_backend_is_headless(backend)) { + return true; + } + if (wlr_backend_is_wl(backend)) { + return true; + } +#if WLR_HAS_X11_BACKEND + if (wlr_backend_is_x11(backend)) { + return true; + } +#endif + return false; +} + +/** + * This command is intended for developer use only. + */ +struct cmd_results *output_cmd_unplug(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + + const char *oc_name = config->handler_context.output_config->name; + if (strcmp(oc_name, "*") == 0) { + return cmd_results_new(CMD_INVALID, "Won't unplug all outputs"); + } + + struct sway_output *sway_output = all_output_by_name_or_id(oc_name); + if (!sway_output) { + return cmd_results_new(CMD_INVALID, + "Cannot unplug unknown output %s", oc_name); + } + + if (!is_backend_allowed(sway_output->wlr_output->backend)) { + return cmd_results_new(CMD_INVALID, + "Can only unplug outputs with headless, wayland or x11 backend"); + } + + wlr_output_destroy(sway_output->wlr_output); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 3b855fdf..60a66d58 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -7,6 +7,7 @@ #include "sway/config.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/desktop/launcher.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" #include "sway/tree/root.h" @@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) { sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); - root_rename_pid_workspaces(workspace->name, new_name); - free(workspace->name); workspace->name = new_name; diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 749235eb..504a9f5e 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -111,8 +111,8 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, : WLR_AXIS_ORIENTATION_HORIZONTAL; double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) ? -1 : 1; - struct wlr_event_pointer_axis event = { - .device = NULL, + struct wlr_pointer_axis_event event = { + .pointer = NULL, .time_msec = 0, .source = WLR_AXIS_SOURCE_WHEEL, .orientation = orientation, diff --git a/sway/config.c b/sway/config.c index f7ff49b3..e99a3458 100644 --- a/sway/config.c +++ b/sway/config.c @@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) { } list_free(mode->switch_bindings); } + if (mode->gesture_bindings) { + for (int i = 0; i < mode->gesture_bindings->length; i++) { + free_gesture_binding(mode->gesture_bindings->items[i]); + } + list_free(mode->gesture_bindings); + } free(mode); } @@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; + if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; list_add(config->modes, config->current_mode); config->floating_mod = 0; @@ -236,6 +243,7 @@ static void config_defaults(struct sway_config *config) { config->default_layout = L_NONE; config->default_orientation = L_NONE; if (!(config->font = strdup("monospace 10"))) goto cleanup; + config->font_description = pango_font_description_from_string(config->font); config->urgent_timeout = 500; config->focus_on_window_activation = FOWA_URGENT; config->popup_during_fullscreen = POPUP_SMART; @@ -1005,7 +1013,7 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { void config_update_font_height(void) { int prev_max_height = config->font_height; - get_text_metrics(config->font, &config->font_height, &config->font_baseline); + get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); if (config->font_height != prev_max_height) { arrange_root(); diff --git a/sway/config/input.c b/sway/config/input.c index a998e170..a98204df 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -25,6 +25,7 @@ struct input_config *new_input_config(const char* identifier) { input->drag = INT_MIN; input->drag_lock = INT_MIN; input->dwt = INT_MIN; + input->dwtp = INT_MIN; input->send_events = INT_MIN; input->click_method = INT_MIN; input->middle_emulation = INT_MIN; @@ -61,6 +62,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->dwt != INT_MIN) { dst->dwt = src->dwt; } + if (src->dwtp != INT_MIN) { + dst->dwtp = src->dwtp; + } if (src->left_handed != INT_MIN) { dst->left_handed = src->left_handed; } diff --git a/sway/config/output.c b/sway/config/output.c index fa509252..9c7082d0 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -27,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) { void output_get_identifier(char *identifier, size_t len, struct sway_output *output) { struct wlr_output *wlr_output = output->wlr_output; - snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, - wlr_output->serial); + snprintf(identifier, len, "%s %s %s", + wlr_output->make ? wlr_output->make : "Unknown", + wlr_output->model ? wlr_output->model : "Unknown", + wlr_output->serial ? wlr_output->serial : "Unknown"); } const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { @@ -69,6 +71,7 @@ struct output_config *new_output_config(const char *name) { oc->max_render_time = -1; oc->adaptive_sync = -1; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; + oc->power = -1; return oc; } @@ -130,8 +133,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { free(dst->background_fallback); dst->background_fallback = strdup(src->background_fallback); } - if (src->dpms_state != 0) { - dst->dpms_state = src->dpms_state; + if (src->power != -1) { + dst->power = src->power; } } @@ -190,11 +193,11 @@ static void merge_id_on_name(struct output_config *oc) { list_add(config->output_configs, ion_oc); sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " - "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", + "transform %d) (bg %s %s) (power %d) (max render time: %d)", ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, ion_oc->transform, ion_oc->background, - ion_oc->background_option, ion_oc->dpms_state, + ion_oc->background_option, ion_oc->power, ion_oc->max_render_time); } } @@ -235,18 +238,18 @@ struct output_config *store_output_config(struct output_config *oc) { } sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " - "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " + "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " "(max render time: %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), - oc->transform, oc->background, oc->background_option, oc->dpms_state, + oc->transform, oc->background, oc->background_option, oc->power, oc->max_render_time); return oc; } -static void set_mode(struct wlr_output *output, int width, int height, - float refresh_rate, bool custom) { +static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, + int width, int height, float refresh_rate, bool custom) { // Not all floating point integers can be represented exactly // as (int)(1000 * mHz / 1000.f) // round() the result to avoid any error @@ -254,7 +257,7 @@ static void set_mode(struct wlr_output *output, int width, int height, if (wl_list_empty(&output->modes) || custom) { sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); - wlr_output_set_custom_mode(output, width, height, + wlr_output_state_set_custom_mode(pending, width, height, refresh_rate > 0 ? mhz : 0); return; } @@ -278,10 +281,11 @@ static void set_mode(struct wlr_output *output, int width, int height, } else { sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); } - wlr_output_set_mode(output, best); + wlr_output_state_set_mode(pending, best); } -static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) { +static void set_modeline(struct wlr_output *output, + struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { if (!wlr_output_is_drm(output)) { sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; @@ -289,7 +293,7 @@ static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) { sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); if (mode) { - wlr_output_set_mode(output, mode); + wlr_output_state_set_mode(pending, mode); } } @@ -311,23 +315,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { // 1 inch = 25.4 mm #define MM_PER_INCH 25.4 -static int compute_default_scale(struct wlr_output *output) { +static int compute_default_scale(struct wlr_output *output, + struct wlr_output_state *pending) { struct wlr_box box = { .width = output->width, .height = output->height }; - if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { - switch (output->pending.mode_type) { + if (pending->committed & WLR_OUTPUT_STATE_MODE) { + switch (pending->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED: - box.width = output->pending.mode->width; - box.height = output->pending.mode->height; + box.width = pending->mode->width; + box.height = pending->mode->height; break; case WLR_OUTPUT_STATE_MODE_CUSTOM: - box.width = output->pending.custom_mode.width; - box.height = output->pending.custom_mode.height; + box.width = pending->custom_mode.width; + box.height = pending->custom_mode.height; break; } } enum wl_output_transform transform = output->transform; - if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { - transform = output->pending.transform; + if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { + transform = pending->transform; } wlr_box_transform(&box, &box, transform, box.width, box.height); @@ -374,38 +379,38 @@ static const uint32_t *bit_depth_preferences[] = { }; static void queue_output_config(struct output_config *oc, - struct sway_output *output) { + struct sway_output *output, struct wlr_output_state *pending) { if (output == root->fallback_output) { return; } struct wlr_output *wlr_output = output->wlr_output; - if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { + if (oc && (!oc->enabled || oc->power == 0)) { sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); - wlr_output_enable(wlr_output, false); + wlr_output_state_set_enabled(pending, false); return; } sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); - wlr_output_enable(wlr_output, true); + wlr_output_state_set_enabled(pending, true); if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { sway_log(SWAY_DEBUG, "Set %s modeline", wlr_output->name); - set_modeline(wlr_output, &oc->drm_mode); + set_modeline(wlr_output, pending, &oc->drm_mode); } else if (oc && oc->width > 0 && oc->height > 0) { sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", wlr_output->name, oc->width, oc->height, oc->refresh_rate); - set_mode(wlr_output, oc->width, oc->height, + set_mode(wlr_output, pending, oc->width, oc->height, oc->refresh_rate, oc->custom_mode == 1); } else if (!wl_list_empty(&wlr_output->modes)) { sway_log(SWAY_DEBUG, "Set preferred mode"); struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); - wlr_output_set_mode(wlr_output, preferred_mode); + wlr_output_state_set_mode(pending, preferred_mode); - if (!wlr_output_test(wlr_output)) { + if (!wlr_output_test_state(wlr_output, pending)) { sway_log(SWAY_DEBUG, "Preferred mode rejected, " "falling back to another mode"); struct wlr_output_mode *mode; @@ -414,8 +419,8 @@ static void queue_output_config(struct output_config *oc, continue; } - wlr_output_set_mode(wlr_output, mode); - if (wlr_output_test(wlr_output)) { + wlr_output_state_set_mode(pending, mode); + if (wlr_output_test_state(wlr_output, pending)) { break; } } @@ -425,7 +430,7 @@ static void queue_output_config(struct output_config *oc, if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, sway_wl_output_subpixel_to_string(oc->subpixel)); - wlr_output_set_subpixel(wlr_output, oc->subpixel); + wlr_output_state_set_subpixel(pending, oc->subpixel); } enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; @@ -437,7 +442,7 @@ static void queue_output_config(struct output_config *oc, } if (wlr_output->transform != tr) { sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); - wlr_output_set_transform(wlr_output, tr); + wlr_output_state_set_transform(pending, tr); } // Apply the scale last before the commit, because the scale auto-detection @@ -446,18 +451,22 @@ static void queue_output_config(struct output_config *oc, if (oc && oc->scale > 0) { scale = oc->scale; } else { - scale = compute_default_scale(wlr_output); + scale = compute_default_scale(wlr_output, pending); sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); } if (scale != wlr_output->scale) { sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); - wlr_output_set_scale(wlr_output, scale); + wlr_output_state_set_scale(pending, scale); } if (oc && oc->adaptive_sync != -1) { sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, oc->adaptive_sync); - wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); + wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { + sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); + wlr_output_state_set_adaptive_sync_enabled(pending, false); + } } if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { @@ -465,8 +474,8 @@ static void queue_output_config(struct output_config *oc, assert(fmts); for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { - wlr_output_set_render_format(wlr_output, fmts[i]); - if (wlr_output_test(wlr_output)) { + wlr_output_state_set_render_format(pending, fmts[i]); + if (wlr_output_test_state(wlr_output, pending)) { break; } @@ -487,14 +496,15 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { // Flag to prevent the output mode event handler from calling us output->enabling = (!oc || oc->enabled); - queue_output_config(oc, output); + struct wlr_output_state pending = {0}; + queue_output_config(oc, output, &pending); - if (!oc || oc->dpms_state != DPMS_OFF) { - output->current_mode = wlr_output->pending.mode; + if (!oc || oc->power != 0) { + output->current_mode = pending.mode; } sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); - if (!wlr_output_commit(wlr_output)) { + if (!wlr_output_commit_state(wlr_output, &pending)) { // Failed to commit output changes, maybe the output is missing a CRTC. // Leave the output disabled for now and try again when the output gets // the mode we asked for. @@ -546,12 +556,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { } // Update output->{lx, ly, width, height} - struct wlr_box *output_box = - wlr_output_layout_get_box(root->output_layout, wlr_output); - output->lx = output_box->x; - output->ly = output_box->y; - output->width = output_box->width; - output->height = output_box->height; + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; if (!output->enabled) { output_enable(output); @@ -577,15 +587,15 @@ bool test_output_config(struct output_config *oc, struct sway_output *output) { return false; } - queue_output_config(oc, output); - bool ok = wlr_output_test(output->wlr_output); - wlr_output_rollback(output->wlr_output); - return ok; + struct wlr_output_state pending = {0}; + queue_output_config(oc, output, &pending); + return wlr_output_test_state(output->wlr_output, &pending); } static void default_output_config(struct output_config *oc, struct wlr_output *wlr_output) { oc->enabled = 1; + oc->power = 1; struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { oc->width = mode->width; @@ -598,7 +608,6 @@ static void default_output_config(struct output_config *oc, struct sway_output *output = wlr_output->data; oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; - oc->dpms_state = DPMS_ON; oc->max_render_time = 0; } @@ -653,10 +662,10 @@ static struct output_config *get_output_config(char *identifier, sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" - " (dpms %d) (max render time: %d)", result->name, result->enabled, + " (power %d) (max render time: %d)", result->name, result->enabled, result->width, result->height, result->refresh_rate, result->x, result->y, result->scale, result->transform, - result->background, result->background_option, result->dpms_state, + result->background, result->background_option, result->power, result->max_render_time); } else if (oc_name) { // No identifier config, just return a copy of the name config diff --git a/sway/criteria.c b/sway/criteria.c index d2a5566f..d7326bea 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -3,7 +3,8 @@ #include <stdio.h> #include <stdbool.h> #include <strings.h> -#include <pcre.h> +#define PCRE2_CODE_UNIT_WIDTH 8 +#include <pcre2.h> #include "sway/criteria.h" #include "sway/tree/container.h" #include "sway/config.h" @@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) { char *error = NULL; // Returns error string on failure or NULL otherwise. -static bool generate_regex(pcre **regex, char *value) { - const char *reg_err; - int offset; - - *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); +static bool generate_regex(pcre2_code **regex, char *value) { + int errorcode; + PCRE2_SIZE offset; + *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL); if (!*regex) { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); + const char *fmt = "Regex compilation for '%s' failed: %s"; - int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; + int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3; error = malloc(len); - snprintf(error, len, fmt, value, reg_err); + snprintf(error, len, fmt, value, buffer); return false; } @@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { if (strcmp(value, "__focused__") == 0) { (*pattern)->match_type = PATTERN_FOCUSED; } else { - (*pattern)->match_type = PATTERN_PCRE; + (*pattern)->match_type = PATTERN_PCRE2; if (!generate_regex(&(*pattern)->regex, value)) { return false; }; @@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { static void pattern_destroy(struct pattern *pattern) { if (pattern) { if (pattern->regex) { - pcre_free(pattern->regex); + pcre2_code_free(pattern->regex); } free(pattern); } @@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) { pattern_destroy(criteria->window_role); #endif pattern_destroy(criteria->con_mark); - free(criteria->workspace); + pattern_destroy(criteria->workspace); + free(criteria->target); free(criteria->cmdlist); free(criteria->raw); free(criteria); } -static int regex_cmp(const char *item, const pcre *regex) { - return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); +static int regex_cmp(const char *item, const pcre2_code *regex) { + pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL); + int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL); + pcre2_match_data_free(match_data); + return result; } #if HAVE_XWAYLAND @@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria, bool exists = false; struct sway_container *con = container; for (int i = 0; i < con->marks->length; ++i) { - if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { + if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) { exists = true; break; } @@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->title) { const char *title = view_get_title(view); if (!title) { - return false; + title = ""; } switch (criteria->title->match_type) { @@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(title, criteria->title->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(title, criteria->title->regex) < 0) { return false; } break; @@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->shell) { const char *shell = view_get_shell(view); if (!shell) { - return false; + shell = ""; } switch (criteria->shell->match_type) { @@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(shell, criteria->shell->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(shell, criteria->shell->regex) < 0) { return false; } break; @@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->app_id) { const char *app_id = view_get_app_id(view); if (!app_id) { - return false; + app_id = ""; } switch (criteria->app_id->match_type) { @@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(app_id, criteria->app_id->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(app_id, criteria->app_id->regex) < 0) { return false; } break; @@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->class) { const char *class = view_get_class(view); if (!class) { - return false; + class = ""; } switch (criteria->class->match_type) { @@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(class, criteria->class->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(class, criteria->class->regex) < 0) { return false; } break; @@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->instance) { const char *instance = view_get_instance(view); if (!instance) { - return false; + instance = ""; } switch (criteria->instance->match_type) { case PATTERN_FOCUSED: - if (focused && strcmp(instance, view_get_instance(focused))) { + if (focused && lenient_strcmp(instance, view_get_instance(focused))) { return false; } break; - case PATTERN_PCRE: - if (regex_cmp(instance, criteria->instance->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(instance, criteria->instance->regex) < 0) { return false; } break; @@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->window_role) { const char *window_role = view_get_window_role(view); if (!window_role) { - return false; + window_role = ""; } switch (criteria->window_role->match_type) { case PATTERN_FOCUSED: - if (focused && strcmp(window_role, view_get_window_role(focused))) { + if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { return false; } break; - case PATTERN_PCRE: - if (regex_cmp(window_role, criteria->window_role->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(window_role, criteria->window_role->regex) < 0) { return false; } break; @@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { return false; } break; @@ -676,7 +683,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } name = calloc(head - namestart + 1, 1); if (head != namestart) { - strncpy(name, namestart, head - namestart); + memcpy(name, namestart, head - namestart); } // Parse token value skip_spaces(&head); @@ -703,7 +710,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } } value = calloc(head - valuestart + 1, 1); - strncpy(value, valuestart, head - valuestart); + memcpy(value, valuestart, head - valuestart); if (in_quotes) { ++head; in_quotes = false; @@ -734,7 +741,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; int len = head - raw; criteria->raw = calloc(len + 1, 1); - strncpy(criteria->raw, raw, len); + memcpy(criteria->raw, raw, len); return criteria; cleanup: diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index ec45d80a..c8d4502c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - output_damage_surface(output, lx - output_box->x, - ly - output_box->y, surface, whole); + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + output_damage_surface(output, lx - output_box.x, + ly - output_box.y, surface, whole); } } diff --git a/sway/desktop/fx_renderer.c b/sway/desktop/fx_renderer.c index 3b4279fe..782ceb88 100644 --- a/sway/desktop/fx_renderer.c +++ b/sway/desktop/fx_renderer.c @@ -35,6 +35,71 @@ static const GLfloat verts[] = { 0, 1, // bottom left }; +static const float transforms[][9] = { + [WL_OUTPUT_TRANSFORM_NORMAL] = { + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_90] = { + 0.0f, 1.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_180] = { + -1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_270] = { + 0.0f, -1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED] = { + -1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED_90] = { + 0.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED_180] = { + 1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED_270] = { + 0.0f, -1.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, +}; + +static void matrix_projection(float mat[static 9], int width, int height, + enum wl_output_transform transform) { + memset(mat, 0, sizeof(*mat) * 9); + + const float *t = transforms[transform]; + float x = 2.0f / width; + float y = 2.0f / height; + + // Rotation + reflection + mat[0] = x * t[0]; + mat[1] = x * t[1]; + mat[3] = y * -t[3]; + mat[4] = y * -t[4]; + + // Translation + mat[2] = -copysign(1.0f, mat[0] + mat[1]); + mat[5] = -copysign(1.0f, mat[3] + mat[4]); + + // Identity + mat[8] = 1.0f; +} + static GLuint compile_shader(GLuint type, const GLchar *src) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); @@ -156,7 +221,8 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { // TODO: wlr_egl_make_current or eglMakeCurrent? // TODO: assert instead of conditional statement? - if (!wlr_egl_make_current(egl)) { + if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE, + wlr_egl_get_context(egl))) { sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not make EGL current"); return NULL; } @@ -242,7 +308,11 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { goto error; } - wlr_egl_unset_current(renderer->egl); + if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl), + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL"); + goto error; + } sway_log(SWAY_INFO, "GLES2 RENDERER: Shaders Initialized Successfully"); return renderer; @@ -257,7 +327,10 @@ error: glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_ext.program); - wlr_egl_unset_current(renderer->egl); + if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl), + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL"); + } // TODO: more freeing? free(renderer); @@ -270,7 +343,7 @@ void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t he glViewport(0, 0, width, height); // refresh projection matrix - wlr_matrix_projection(renderer->projection, width, height, + matrix_projection(renderer->projection, width, height, WL_OUTPUT_TRANSFORM_FLIPPED_180); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 82353038..3a4d0b87 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_idle_notify_v1.h> #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/seat.h" @@ -140,6 +141,7 @@ void sway_idle_inhibit_v1_check_active( } } wlr_idle_set_enabled(manager->idle, NULL, !inhibited); + wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); } struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c new file mode 100644 index 00000000..48e5d24c --- /dev/null +++ b/sway/desktop/launcher.c @@ -0,0 +1,211 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdlib.h> +#include <string.h> +#include <wlr/types/wlr_xdg_activation_v1.h> +#include "sway/input/seat.h" +#include "sway/output.h" +#include "sway/desktop/launcher.h" +#include "sway/tree/node.h" +#include "sway/tree/container.h" +#include "sway/tree/workspace.h" +#include "sway/tree/root.h" +#include "log.h" + +/** + * Get the pid of a parent process given the pid of a child process. + * + * Returns the parent pid or NULL if the parent pid cannot be determined. + */ +static pid_t get_parent_pid(pid_t child) { + pid_t parent = -1; + char file_name[100]; + char *buffer = NULL; + const char *sep = " "; + FILE *stat = NULL; + size_t buf_size = 0; + + snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); + + if ((stat = fopen(file_name, "r"))) { + if (getline(&buffer, &buf_size, stat) != -1) { + strtok(buffer, sep); // pid + strtok(NULL, sep); // executable name + strtok(NULL, sep); // state + char *token = strtok(NULL, sep); // parent pid + parent = strtol(token, NULL, 10); + } + free(buffer); + fclose(stat); + } + + if (parent) { + return (parent == child) ? -1 : parent; + } + + return -1; +} + +void launcher_ctx_consume(struct launcher_ctx *ctx) { + // The view is now responsible for destroying this ctx + wl_list_remove(&ctx->token_destroy.link); + wl_list_init(&ctx->token_destroy.link); + + if (!ctx->activated) { + // An unactivated token hasn't been destroyed yet + wlr_xdg_activation_token_v1_destroy(ctx->token); + } + ctx->token = NULL; + + // Prevent additional matches + wl_list_remove(&ctx->link); + wl_list_init(&ctx->link); +} + +void launcher_ctx_destroy(struct launcher_ctx *ctx) { + if (ctx == NULL) { + return; + } + wl_list_remove(&ctx->node_destroy.link); + wl_list_remove(&ctx->token_destroy.link); + wl_list_remove(&ctx->link); + wlr_xdg_activation_token_v1_destroy(ctx->token); + free(ctx->name); + free(ctx); +} + +struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) { + if (wl_list_empty(&server.pending_launcher_ctxs)) { + return NULL; + } + + struct launcher_ctx *ctx = NULL; + sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); + + do { + struct launcher_ctx *_ctx = NULL; + wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) { + if (pid == _ctx->pid) { + ctx = _ctx; + sway_log(SWAY_DEBUG, + "found %s match for pid %d: %s", + node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node)); + break; + } + } + pid = get_parent_pid(pid); + } while (pid > 1); + + return ctx; +} + +struct sway_workspace *launcher_ctx_get_workspace( + struct launcher_ctx *ctx) { + struct sway_workspace *ws = NULL; + struct sway_output *output = NULL; + + switch (ctx->node->type) { + case N_CONTAINER: + // Unimplemented + // TODO: add container matching? + ws = ctx->node->sway_container->pending.workspace; + break; + case N_WORKSPACE: + ws = ctx->node->sway_workspace; + break; + case N_OUTPUT: + output = ctx->node->sway_output; + ws = workspace_by_name(ctx->name); + if (!ws) { + sway_log(SWAY_DEBUG, + "Creating workspace %s for pid %d because it disappeared", + ctx->name, ctx->pid); + if (!output->enabled) { + sway_log(SWAY_DEBUG, + "Workspace output %s is disabled, trying another one", + output->wlr_output->name); + output = NULL; + } + ws = workspace_create(output, ctx->name); + } + break; + case N_ROOT: + ws = workspace_create(NULL, ctx->name); + break; + } + + return ws; +} + +static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy); + switch (ctx->node->type) { + case N_CONTAINER: + // Unimplemented + break; + case N_WORKSPACE:; + struct sway_workspace *ws = ctx->node->sway_workspace; + wl_list_remove(&ctx->node_destroy.link); + wl_list_init(&ctx->node_destroy.link); + // We want to save this ws name to recreate later, hopefully on the + // same output + free(ctx->name); + ctx->name = strdup(ws->name); + if (!ws->output || ws->output->node.destroying) { + // If the output is being destroyed it would be pointless to track + // If the output is being disabled, we'll find out if it's still + // disabled when we try to match it. + ctx->node = &root->node; + break; + } + ctx->node = &ws->output->node; + wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); + break; + case N_OUTPUT: + wl_list_remove(&ctx->node_destroy.link); + wl_list_init(&ctx->node_destroy.link); + // We'll make the ws ctx->name somewhere else + ctx->node = &root->node; + break; + case N_ROOT: + // Unreachable + break; + } +} + +static void token_handle_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy); + ctx->token = NULL; + launcher_ctx_destroy(ctx); +} + +struct launcher_ctx *launcher_ctx_create() { + struct sway_seat *seat = input_manager_current_seat(); + struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); + return NULL; + } + + struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); + struct wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); + token->data = ctx; + ctx->name = strdup(ws->name); + ctx->token = token; + ctx->node = &ws->node; + + ctx->node_destroy.notify = ctx_handle_node_destroy; + wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); + + ctx->token_destroy.notify = token_handle_destroy; + wl_signal_add(&token->events.destroy, &ctx->token_destroy); + + wl_list_init(&ctx->link); + wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); + return ctx; +} + +const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) { + const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token); + return token; +} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 27e457f1..6e3cc0e2 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -3,8 +3,8 @@ #include <string.h> #include <wayland-server-core.h> #include <wlr/types/wlr_layer_shell_v1.h> -#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_subcompositor.h> #include "log.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -270,10 +270,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { wl_resource_get_client(sway_layer->layer_surface->resource); bool set_focus = seat->exclusive_client == client; - wl_list_remove(&sway_layer->output_destroy.link); - wl_list_remove(&sway_layer->link); - wl_list_init(&sway_layer->link); - if (set_focus) { struct sway_layer_surface *layer = find_mapped_layer_by_client(client, sway_layer->layer_surface->output); @@ -282,7 +278,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { } } - sway_layer->layer_surface->output = NULL; wlr_layer_surface_v1_destroy(sway_layer->layer_surface); } @@ -291,10 +286,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, layer, surface_commit); struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; struct wlr_output *wlr_output = layer_surface->output; - if (wlr_output == NULL) { - return; - } - + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; struct wlr_box old_extent = layer->extent; @@ -341,13 +333,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { cursor_rebase_all(); struct wlr_output *wlr_output = sway_layer->layer_surface->output; - if (wlr_output == NULL) { - return; - } + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; - if (output == NULL) { - return; - } output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, sway_layer->layer_surface->surface, true); } @@ -375,22 +362,24 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&sway_layer->surface_commit.link); wl_list_remove(&sway_layer->new_popup.link); wl_list_remove(&sway_layer->new_subsurface.link); - if (sway_layer->layer_surface->output != NULL) { - struct sway_output *output = sway_layer->layer_surface->output->data; - if (output != NULL) { - arrange_layers(output); - transaction_commit_dirty(); - } - wl_list_remove(&sway_layer->output_destroy.link); - sway_layer->layer_surface->output = NULL; - } + + struct wlr_output *wlr_output = sway_layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); + struct sway_output *output = wlr_output->data; + arrange_layers(output); + transaction_commit_dirty(); + wl_list_remove(&sway_layer->output_destroy.link); + sway_layer->layer_surface->output = NULL; + free(sway_layer); } static void handle_map(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, sway_layer, map); - struct sway_output *output = sway_layer->layer_surface->output->data; + struct wlr_output *wlr_output = sway_layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); + struct sway_output *output = wlr_output->data; output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, sway_layer->layer_surface->surface, true); wlr_surface_send_enter(sway_layer->layer_surface->surface, @@ -408,9 +397,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface, bool whole) { struct sway_layer_surface *layer = subsurface->layer_surface; struct wlr_output *wlr_output = layer->layer_surface->output; - if (!wlr_output) { - return; - } + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; @@ -496,15 +483,15 @@ static struct sway_layer_surface *popup_get_layer( static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { struct wlr_xdg_popup *popup = layer_popup->wlr_popup; struct wlr_surface *surface = popup->base->surface; - int popup_sx = popup->geometry.x - popup->base->current.geometry.x; - int popup_sy = popup->geometry.y - popup->base->current.geometry.y; + int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; + int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; int ox = popup_sx, oy = popup_sy; struct sway_layer_surface *layer; while (true) { if (layer_popup->parent_type == LAYER_PARENT_POPUP) { layer_popup = layer_popup->parent_popup; - ox += layer_popup->wlr_popup->geometry.x; - oy += layer_popup->wlr_popup->geometry.y; + ox += layer_popup->wlr_popup->current.geometry.x; + oy += layer_popup->wlr_popup->current.geometry.y; } else { layer = layer_popup->parent_layer; ox += layer->geo.x; @@ -513,6 +500,7 @@ static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { } } struct wlr_output *wlr_output = layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; output_damage_surface(output, ox, oy, surface, whole); } @@ -521,6 +509,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) { struct sway_layer_popup *popup = wl_container_of(listener, popup, map); struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_output *wlr_output = layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); popup_damage(popup, true); } @@ -550,7 +539,9 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; - struct sway_output *output = layer->layer_surface->output->data; + struct wlr_output *wlr_output = layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); + struct sway_output *output = wlr_output->data; // the output box expressed in the coordinate system of the toplevel parent // of the popup @@ -642,6 +633,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", layer_surface->namespace); + // Note that layer_surface->output can be NULL + // here, but none of our destroy callbacks are + // registered yet so we don't have to make them + // handle that case. wlr_layer_surface_v1_destroy(layer_surface); return; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index b6d19dd6..182ca428 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,11 +10,10 @@ #include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_drm_lease_v1.h> #include <wlr/types/wlr_matrix.h> -#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> #include <wlr/types/wlr_presentation_time.h> -#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_compositor.h> #include <wlr/util/region.h> #include "config.h" #include "log.h" @@ -276,6 +275,25 @@ static void for_each_surface_container_iterator(struct sway_container *con, static void output_for_each_surface(struct sway_output *output, sway_surface_iterator_func_t iterator, void *user_data) { + if (server.session_lock.locked) { + if (server.session_lock.lock == NULL) { + return; + } + struct wlr_session_lock_surface_v1 *lock_surface; + wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { + if (lock_surface->output != output->wlr_output) { + continue; + } + if (!lock_surface->mapped) { + continue; + } + + output_surface_for_each_surface(output, lock_surface->surface, + 0.0, 0.0, iterator, user_data); + } + return; + } + if (output_has_opaque_overlay_layer_surface(output)) { goto overlay; } @@ -435,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } + if (server.session_lock.locked) { + return false; + } + if (!wl_list_empty(&view->saved_buffers)) { return false; } @@ -533,31 +555,43 @@ static int output_repaint_timer_handler(void *data) { } } - bool needs_frame; + int buffer_age; + if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) { + return 0; + } + pixman_region32_t damage; pixman_region32_init(&damage); - if (!wlr_output_damage_attach_render(output->damage, - &needs_frame, &damage)) { + wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage); + if (!output->wlr_output->needs_frame && + !pixman_region32_not_empty(&output->damage_ring.current)) { + pixman_region32_fini(&damage); + wlr_output_rollback(output->wlr_output); return 0; } - if (needs_frame) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - output_render(output, &now, &damage); - } else { - wlr_output_rollback(output->wlr_output); - } + output_render(output, &now, &damage); pixman_region32_fini(&damage); return 0; } -static void damage_handle_frame(struct wl_listener *listener, void *user_data) { +static void handle_damage(struct wl_listener *listener, void *user_data) { + struct sway_output *output = + wl_container_of(listener, output, damage); + struct wlr_output_event_damage *event = user_data; + if (wlr_damage_ring_add(&output->damage_ring, event->damage)) { + wlr_output_schedule_frame(output->wlr_output); + } +} + +static void handle_frame(struct wl_listener *listener, void *user_data) { struct sway_output *output = - wl_container_of(listener, output, damage_frame); + wl_container_of(listener, output, frame); if (!output->enabled || !output->wlr_output->enabled) { return; } @@ -620,11 +654,18 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { send_frame_done(output, &data); } +static void handle_needs_frame(struct wl_listener *listener, void *user_data) { + struct sway_output *output = + wl_container_of(listener, output, needs_frame); + wlr_output_schedule_frame(output->wlr_output); +} + void output_damage_whole(struct sway_output *output) { // The output can exist with no wlr_output if it's just been disconnected // and the transaction to evacuate it has't completed yet. - if (output && output->wlr_output && output->damage) { - wlr_output_damage_add_whole(output->damage); + if (output != NULL && output->wlr_output != NULL) { + wlr_damage_ring_add_whole(&output->damage_ring); + wlr_output_schedule_frame(output->wlr_output); } } @@ -648,11 +689,15 @@ static void damage_surface_iterator(struct sway_output *output, ceil(output->wlr_output->scale) - surface->current.scale); } pixman_region32_translate(&damage, box.x, box.y); - wlr_output_damage_add(output->damage, &damage); + if (wlr_damage_ring_add(&output->damage_ring, &damage)) { + wlr_output_schedule_frame(output->wlr_output); + } pixman_region32_fini(&damage); if (whole) { - wlr_output_damage_add_box(output->damage, &box); + if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { + wlr_output_schedule_frame(output->wlr_output); + } } if (!wl_list_empty(&surface->current.frame_callback_list)) { @@ -682,7 +727,9 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) { box.x -= output->lx; box.y -= output->ly; scale_box(&box, output->wlr_output->scale); - wlr_output_damage_add_box(output->damage, &box); + if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { + wlr_output_schedule_frame(output->wlr_output); + } } static void damage_child_views_iterator(struct sway_container *con, @@ -706,7 +753,9 @@ void output_damage_whole_container(struct sway_output *output, .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); - wlr_output_damage_add_box(output->damage, &box); + if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { + wlr_output_schedule_frame(output->wlr_output); + } // Damage subsurfaces as well, which may extend outside the box if (con->view) { damage_child_views_iterator(con, output); @@ -715,20 +764,6 @@ void output_damage_whole_container(struct sway_output *output, } } -static void damage_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = - wl_container_of(listener, output, damage_destroy); - if (!output->enabled) { - return; - } - output_disable(output); - - wl_list_remove(&output->damage_destroy.link); - wl_list_remove(&output->damage_frame.link); - - transaction_commit_dirty(); -} - static void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -740,14 +775,15 @@ static void update_output_manager_config(struct sway_server *server) { } struct wlr_output_configuration_head_v1 *config_head = wlr_output_configuration_head_v1_create(config, output->wlr_output); - struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - // We mark the output enabled even if it is switched off by DPMS + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + // We mark the output enabled when it's switched off but not disabled config_head->state.enabled = output->current_mode != NULL && output->enabled; config_head->state.mode = output->current_mode; - if (output_box) { - config_head->state.x = output_box->x; - config_head->state.y = output_box->y; + if (!wlr_box_empty(&output_box)) { + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; } } @@ -757,18 +793,24 @@ static void update_output_manager_config(struct sway_server *server) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_server *server = output->server; - output_begin_destroy(output); if (output->enabled) { output_disable(output); } + output_begin_destroy(output); + wl_list_remove(&output->link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + wl_list_remove(&output->damage.link); + wl_list_remove(&output->frame.link); + wl_list_remove(&output->needs_frame.link); + + wlr_damage_ring_finish(&output->damage_ring); output->wlr_output->data = NULL; output->wlr_output = NULL; @@ -796,10 +838,16 @@ static void handle_mode(struct wl_listener *listener, void *data) { if (!output->enabled) { return; } + arrange_layers(output); arrange_output(output); transaction_commit_dirty(); + int width, height; + wlr_output_transformed_resolution(output->wlr_output, &width, &height); + wlr_damage_ring_set_bounds(&output->damage_ring, width, height); + wlr_output_schedule_frame(output->wlr_output); + update_output_manager_config(output->server); } @@ -827,6 +875,13 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } + + if (event->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM)) { + int width, height; + wlr_output_transformed_resolution(output->wlr_output, &width, &height); + wlr_damage_ring_set_bounds(&output->damage_ring, width, height); + wlr_output_schedule_frame(output->wlr_output); + } } static void handle_present(struct wl_listener *listener, void *data) { @@ -862,10 +917,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { if (wlr_output->non_desktop) { sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); + struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); if (server->drm_lease_manager) { wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_output); } + list_add(root->non_desktop_outputs, non_desktop); return; } @@ -880,7 +937,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } output->server = server; - output->damage = wlr_output_damage_create(wlr_output); + wlr_damage_ring_init(&output->damage_ring); wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; @@ -890,10 +947,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - 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_signal_add(&wlr_output->events.damage, &output->damage); + output->damage.notify = handle_damage; + wl_signal_add(&wlr_output->events.frame, &output->frame); + output->frame.notify = handle_frame; + wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame); + output->needs_frame.notify = handle_needs_frame; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); @@ -1007,10 +1066,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct output_config *oc = new_output_config(output->wlr_output->name); switch (event->mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: - oc->dpms_state = DPMS_OFF; + oc->power = 0; break; case ZWLR_OUTPUT_POWER_V1_MODE_ON: - oc->dpms_state = DPMS_ON; + oc->power = 1; break; } oc = store_output_config(oc); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 6247d937..35e2150e 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -9,11 +9,11 @@ #include <wlr/render/gles2.h> #include <wlr/render/wlr_renderer.h> #include <wlr/types/wlr_buffer.h> +#include <wlr/types/wlr_damage_ring.h> #include <wlr/types/wlr_matrix.h> -#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> -#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_compositor.h> #include <wlr/util/region.h> #include "log.h" #include "config.h" @@ -1301,6 +1301,41 @@ void output_render(struct sway_output *output, struct timespec *when, fx_renderer_clear((float[]){1, 1, 0, 1}); } + if (server.session_lock.locked) { + float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + if (server.session_lock.lock == NULL) { + // abandoned lock -> red BG + clear_color[0] = 1.f; + } + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + fx_renderer_clear(clear_color); + } + + if (server.session_lock.lock != NULL) { + struct render_data data = { + .damage = damage, + .deco_data = get_undecorated_decoration_data(), + }; + + struct wlr_session_lock_surface_v1 *lock_surface; + wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { + if (lock_surface->output != wlr_output) { + continue; + } + if (!lock_surface->mapped) { + continue; + } + + output_surface_for_each_surface(output, lock_surface->surface, + 0.0, 0.0, render_surface_iterator, &data); + } + } + goto renderer_end; + } + if (output_has_opaque_overlay_layer_surface(output)) { goto render_overlay; } @@ -1409,7 +1444,7 @@ renderer_end: enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, &output->damage->current, + wlr_region_transform(&frame_damage, &output->damage_ring.current, transform, width, height); if (debug.damage != DAMAGE_DEFAULT) { @@ -1423,5 +1458,7 @@ renderer_end: if (!wlr_output_commit(wlr_output)) { return; } + + wlr_damage_ring_rotate(&output->damage_ring); output->last_frame = *when; } diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c index 767b2045..1d7b536d 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200112L #include <stdlib.h> #include <time.h> -#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_compositor.h> #include "sway/server.h" #include "sway/surface.h" diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 5fae8296..8da922d5 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -24,11 +24,11 @@ static const struct sway_view_child_impl popup_impl; static void popup_get_view_coords(struct sway_view_child *child, int *sx, int *sy) { struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; + struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; - wlr_xdg_popup_get_toplevel_coords(surface->popup, - surface->popup->geometry.x - surface->current.geometry.x, - surface->popup->geometry.y - surface->current.geometry.y, + wlr_xdg_popup_get_toplevel_coords(wlr_popup, + wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, + wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, sx, sy); } @@ -65,7 +65,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { static void popup_unconstrain(struct sway_xdg_popup *popup) { struct sway_view *view = popup->child.view; - struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; + struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; struct sway_output *output = view->container->pending.workspace->output; @@ -91,7 +91,7 @@ static struct sway_xdg_popup *popup_create( return NULL; } view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); - popup->wlr_xdg_surface = xdg_surface; + popup->wlr_xdg_popup = xdg_surface->popup; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; @@ -119,7 +119,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( static void get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height) { struct wlr_xdg_toplevel_state *state = - &view->wlr_xdg_surface->toplevel->current; + &view->wlr_xdg_toplevel->current; *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; @@ -133,9 +133,9 @@ static const char *get_string_prop(struct sway_view *view, } switch (prop) { case VIEW_PROP_TITLE: - return view->wlr_xdg_surface->toplevel->title; + return view->wlr_xdg_toplevel->title; case VIEW_PROP_APP_ID: - return view->wlr_xdg_surface->toplevel->app_id; + return view->wlr_xdg_toplevel->app_id; default: return NULL; } @@ -148,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, if (xdg_shell_view == NULL) { return 0; } - return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); + return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, + width, height); } static void set_activated(struct sway_view *view, bool activated) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - wlr_xdg_toplevel_set_activated(surface, activated); - } + wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); } static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; enum wlr_edges edges = WLR_EDGE_NONE; if (tiled) { edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; } - wlr_xdg_toplevel_set_tiled(surface, edges); + wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); + wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); } static void set_resizing(struct sway_view *view, bool resizing) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - wlr_xdg_toplevel_set_resizing(surface, resizing); + wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); } static bool wants_floating(struct sway_view *view) { - struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; struct wlr_xdg_toplevel_state *state = &toplevel->current; return (state->min_width != 0 && state->min_height != 0 && (state->min_width == state->max_width @@ -204,7 +199,7 @@ static void for_each_surface(struct sway_view *view, if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, + wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, user_data); } @@ -213,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view, if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, - user_data); + wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, + iterator, user_data); } static bool is_transient_for(struct sway_view *child, @@ -222,12 +217,12 @@ static bool is_transient_for(struct sway_view *child, if (xdg_shell_view_from_view(child) == NULL) { return false; } - struct wlr_xdg_surface *surface = child->wlr_xdg_surface; - while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { + struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; + while (toplevel) { + if (toplevel->parent == ancestor->wlr_xdg_toplevel) { return true; } - surface = surface->toplevel->parent; + toplevel = toplevel->parent; } return false; } @@ -236,17 +231,13 @@ static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL - && surface->toplevel) { - wlr_xdg_toplevel_send_close(surface); - } + wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); } static void close_popups(struct sway_view *view) { struct wlr_xdg_popup *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { - wlr_xdg_popup_destroy(popup->base); + wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { + wlr_xdg_popup_destroy(popup); } } @@ -280,7 +271,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit); struct sway_view *view = &xdg_shell_view->view; - struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; struct wlr_box new_geo; wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); @@ -334,26 +325,27 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { popup_create(wlr_popup, &xdg_shell_view->view); } +static void handle_request_maximize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_maximize); + struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; + wlr_xdg_surface_schedule_configure(toplevel->base); +} + static void handle_request_fullscreen(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen); - struct wlr_xdg_toplevel_set_fullscreen_event *e = data; - struct wlr_xdg_surface *xdg_surface = - xdg_shell_view->view.wlr_xdg_surface; + struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; struct sway_view *view = &xdg_shell_view->view; - if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, - "xdg_shell requested fullscreen of surface with role %i", - xdg_surface->role)) { - return; - } - if (!xdg_surface->mapped) { + if (!toplevel->base->mapped) { return; } struct sway_container *container = view->container; - if (e->fullscreen && e->output && e->output->data) { - struct sway_output *output = e->output->data; + struct wlr_xdg_toplevel_requested *req = &toplevel->requested; + if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { + struct sway_output *output = req->fullscreen_output->data; struct sway_workspace *ws = output_get_active_workspace(output); if (ws && !container_is_scratchpad_hidden(container) && container->pending.workspace != ws) { @@ -365,7 +357,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) } } - container_set_fullscreen(container, e->fullscreen); + container_set_fullscreen(container, req->fullscreen); arrange_root(); transaction_commit_dirty(); @@ -375,7 +367,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_move); struct sway_view *view = &xdg_shell_view->view; - if (!container_is_floating(view->container)) { + if (!container_is_floating(view->container) || + view->container->pending.fullscreen_mode) { return; } struct wlr_xdg_toplevel_move_event *e = data; @@ -412,6 +405,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); + wl_list_remove(&xdg_shell_view->request_maximize.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); wl_list_remove(&xdg_shell_view->request_move.link); wl_list_remove(&xdg_shell_view->request_resize.link); @@ -423,13 +417,13 @@ static void handle_map(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map); struct sway_view *view = &xdg_shell_view->view; - struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; - view->natural_width = view->wlr_xdg_surface->current.geometry.width; - view->natural_height = view->wlr_xdg_surface->current.geometry.height; + view->natural_width = toplevel->base->current.geometry.width; + view->natural_height = toplevel->base->current.geometry.height; if (!view->natural_width && !view->natural_height) { - view->natural_width = view->wlr_xdg_surface->surface->current.width; - view->natural_height = view->wlr_xdg_surface->surface->current.height; + view->natural_width = toplevel->base->surface->current.width; + view->natural_height = toplevel->base->surface->current.height; } bool csd = false; @@ -440,44 +434,48 @@ static void handle_map(struct wl_listener *listener, void *data) { csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; } else { struct sway_server_decoration *deco = - decoration_from_surface(xdg_surface->surface); + decoration_from_surface(toplevel->base->surface); csd = !deco || deco->wlr_server_decoration->mode == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; } - view_map(view, view->wlr_xdg_surface->surface, - xdg_surface->toplevel->requested.fullscreen, - xdg_surface->toplevel->requested.fullscreen_output, + view_map(view, toplevel->base->surface, + toplevel->requested.fullscreen, + toplevel->requested.fullscreen_output, csd); transaction_commit_dirty(); xdg_shell_view->commit.notify = handle_commit; - wl_signal_add(&xdg_surface->surface->events.commit, + wl_signal_add(&toplevel->base->surface->events.commit, &xdg_shell_view->commit); xdg_shell_view->new_popup.notify = handle_new_popup; - wl_signal_add(&xdg_surface->events.new_popup, + wl_signal_add(&toplevel->base->events.new_popup, &xdg_shell_view->new_popup); + xdg_shell_view->request_maximize.notify = handle_request_maximize; + wl_signal_add(&toplevel->events.request_maximize, + &xdg_shell_view->request_maximize); + xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; - wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, + wl_signal_add(&toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); xdg_shell_view->request_move.notify = handle_request_move; - wl_signal_add(&xdg_surface->toplevel->events.request_move, + wl_signal_add(&toplevel->events.request_move, &xdg_shell_view->request_move); xdg_shell_view->request_resize.notify = handle_request_resize; - wl_signal_add(&xdg_surface->toplevel->events.request_resize, + wl_signal_add(&toplevel->events.request_resize, &xdg_shell_view->request_resize); xdg_shell_view->set_title.notify = handle_set_title; - wl_signal_add(&xdg_surface->toplevel->events.set_title, + wl_signal_add(&toplevel->events.set_title, &xdg_shell_view->set_title); xdg_shell_view->set_app_id.notify = handle_set_app_id; - wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + wl_signal_add(&toplevel->events.set_app_id, &xdg_shell_view->set_app_id); } @@ -491,7 +489,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->destroy.link); wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); - view->wlr_xdg_surface = NULL; + view->wlr_xdg_toplevel = NULL; if (view->xdg_decoration) { view->xdg_decoration->view = NULL; } @@ -522,7 +520,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { } view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); - xdg_shell_view->view.wlr_xdg_surface = xdg_surface; + xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; xdg_shell_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 40288f97..e15a3341 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -5,7 +5,9 @@ #include <wayland-server-core.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_xdg_activation_v1.h> #include <wlr/xwayland.h> +#include <xcb/xcb_icccm.h> #include "log.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" @@ -15,6 +17,7 @@ #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/server.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -121,6 +124,20 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { } } +static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { + struct wlr_xwayland_surface *xsurface = data; + if (!xsurface->mapped) { + return; + } + struct sway_seat *seat = input_manager_current_seat(); + struct sway_container *focus = seat_get_focused_container(seat); + if (focus && focus->view && focus->view->pid != xsurface->pid) { + return; + } + + seat_set_focus_surface(seat, xsurface->surface, false); +} + static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, destroy); @@ -129,6 +146,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&surface->unmap.link); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->override_redirect.link); + wl_list_remove(&surface->request_activate.link); free(surface); } @@ -176,6 +194,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged( surface->destroy.notify = unmanaged_handle_destroy; wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); surface->override_redirect.notify = unmanaged_handle_override_redirect; + wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); + surface->request_activate.notify = unmanaged_handle_request_activate; return surface; } @@ -294,7 +314,7 @@ static bool wants_floating(struct sway_view *view) { } } - struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; + xcb_size_hints_t *size_hints = surface->size_hints; if (size_hints != NULL && size_hints->min_width > 0 && size_hints->min_height > 0 && (size_hints->max_width == size_hints->min_width || @@ -348,7 +368,7 @@ static void destroy(struct sway_view *view) { static void get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height) { struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; + xcb_size_hints_t *size_hints = surface->size_hints; if (size_hints == NULL) { *min_width = DBL_MIN; @@ -448,6 +468,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->set_title.link); wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_role.link); + wl_list_remove(&xwayland_view->set_startup_id.link); wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->set_decorations.link); @@ -577,7 +598,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!container_is_floating(view->container)) { + if (!container_is_floating(view->container) || + view->container->pending.fullscreen_mode) { return; } struct sway_seat *seat = input_manager_current_seat(); @@ -647,6 +669,31 @@ static void handle_set_role(struct wl_listener *listener, void *data) { view_execute_criteria(view); } +static void handle_set_startup_id(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_startup_id); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (xsurface->startup_id == NULL) { + return; + } + + struct wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_v1_find_token( + server.xdg_activation_v1, xsurface->startup_id); + if (token == NULL) { + // Tried to activate with an unknown or expired token + return; + } + + struct launcher_ctx *ctx = token->data; + if (token->data == NULL) { + // TODO: support external launchers in X + return; + } + view_assign_ctx(view, ctx); +} + static void handle_set_window_type(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_window_type); @@ -666,14 +713,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!xsurface->hints_urgency && view->urgent_timer) { + const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); + if (!hints_urgency && view->urgent_timer) { // The view is in the timeout period. We'll ignore the request to // unset urgency so that the view remains urgent until the timer clears // it. return; } if (view->allow_request_urgent) { - view_set_urgent(view, (bool)xsurface->hints_urgency); + view_set_urgent(view, hints_urgency); } } @@ -731,6 +779,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); xwayland_view->set_role.notify = handle_set_role; + wl_signal_add(&xsurface->events.set_startup_id, + &xwayland_view->set_startup_id); + xwayland_view->set_startup_id.notify = handle_set_startup_id; + wl_signal_add(&xsurface->events.set_window_type, &xwayland_view->set_window_type); xwayland_view->set_window_type.notify = handle_set_window_type; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 6fddee90..449aa430 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -386,28 +386,29 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, static void handle_pointer_motion_relative( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - struct wlr_event_pointer_motion *e = data; - cursor_handle_activity_from_device(cursor, e->device); + struct wlr_pointer_motion_event *e = data; + cursor_handle_activity_from_device(cursor, &e->pointer->base); - pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, - e->unaccel_dx, e->unaccel_dy); + pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, + e->delta_y, e->unaccel_dx, e->unaccel_dy); } static void handle_pointer_motion_absolute( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion_absolute); - struct wlr_event_pointer_motion_absolute *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_pointer_motion_absolute_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base, event->x, event->y, &lx, &ly); double dx = lx - cursor->cursor->x; double dy = ly - cursor->cursor->y; - pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); + pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy, + dx, dy); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -422,7 +423,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, static void handle_pointer_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); - struct wlr_event_pointer_button *event = data; + struct wlr_pointer_button_event *event = data; if (event->state == WLR_BUTTON_PRESSED) { cursor->pressed_button_count++; @@ -434,20 +435,20 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { } } - cursor_handle_activity_from_device(cursor, event->device); - dispatch_cursor_button(cursor, event->device, + cursor_handle_activity_from_device(cursor, &event->pointer->base); + dispatch_cursor_button(cursor, &event->pointer->base, event->time_msec, event->button, event->state); } void dispatch_cursor_axis(struct sway_cursor *cursor, - struct wlr_event_pointer_axis *event) { + struct wlr_pointer_axis_event *event) { seatop_pointer_axis(cursor->seat, event); } static void handle_pointer_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); - struct wlr_event_pointer_axis *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_pointer_axis_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); dispatch_cursor_axis(cursor, event); } @@ -458,8 +459,8 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) { static void handle_touch_down(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); - struct wlr_event_touch_down *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_touch_down_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); cursor_hide(cursor); struct sway_seat *seat = cursor->seat; @@ -467,7 +468,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { struct wlr_surface *surface = NULL; double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, event->x, event->y, &lx, &ly); double sx, sy; struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); @@ -495,24 +496,25 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { double dx, dy; dx = lx - cursor->cursor->x; dy = ly - cursor->cursor->y; - pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); - dispatch_cursor_button(cursor, event->device, event->time_msec, + pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, + dx, dy); + dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); } } static void handle_touch_up(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); - struct wlr_event_touch_up *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_touch_up_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; if (cursor->simulating_pointer_from_touch) { if (cursor->pointer_touch_id == cursor->seat->touch_id) { cursor->pointer_touch_up = true; - dispatch_cursor_button(cursor, event->device, event->time_msec, - BTN_LEFT, WLR_BUTTON_RELEASED); + dispatch_cursor_button(cursor, &event->touch->base, + event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); } } else { wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); @@ -522,15 +524,15 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { static void handle_touch_motion(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_motion); - struct wlr_event_touch_motion *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_touch_motion_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); struct sway_seat *seat = cursor->seat; struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_surface *surface = NULL; double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, event->x, event->y, &lx, &ly); double sx, sy; node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); @@ -552,7 +554,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { double dx, dy; dx = lx - cursor->cursor->x; dy = ly - cursor->cursor->y; - pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); + pointer_motion(cursor, event->time_msec, &event->touch->base, + dx, dy, dx, dy); } } else if (surface) { wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, @@ -591,14 +594,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device, double x1 = region->x1, x2 = region->x2; double y1 = region->y1, y2 = region->y2; - if (region->mm) { - if (device->width_mm == 0 || device->height_mm == 0) { + if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); + if (tablet->width_mm == 0 || tablet->height_mm == 0) { return; } - x1 /= device->width_mm; - x2 /= device->width_mm; - y1 /= device->height_mm; - y2 /= device->height_mm; + x1 /= tablet->width_mm; + x2 /= tablet->width_mm; + y1 /= tablet->height_mm; + y2 /= tablet->height_mm; } *x = apply_mapping_from_coord(x1, x2, *x); @@ -660,8 +664,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, static void handle_tool_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); - struct wlr_event_tablet_tool_axis *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_axis_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; if (!sway_tool) { @@ -716,8 +720,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { static void handle_tool_tip(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); - struct wlr_event_tablet_tool_tip *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_tip_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; @@ -732,7 +736,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { if (cursor->simulating_pointer_from_tool_tip && event->state == WLR_TABLET_TOOL_TIP_UP) { cursor->simulating_pointer_from_tool_tip = false; - dispatch_cursor_button(cursor, event->device, event->time_msec, + dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { @@ -744,8 +748,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { WLR_TABLET_TOOL_TIP_UP); } else { cursor->simulating_pointer_from_tool_tip = true; - dispatch_cursor_button(cursor, event->device, event->time_msec, - BTN_LEFT, WLR_BUTTON_PRESSED); + dispatch_cursor_button(cursor, &event->tablet->base, + event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } } else { @@ -767,12 +771,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, static void handle_tool_proximity(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); - struct wlr_event_tablet_tool_proximity *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_proximity_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct wlr_tablet_tool *tool = event->tool; if (!tool->data) { - struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); + struct sway_tablet *tablet = get_tablet_for_device(cursor, + &event->tablet->base); if (!tablet) { sway_log(SWAY_ERROR, "no tablet for tablet tool"); return; @@ -797,8 +802,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { static void handle_tool_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); - struct wlr_event_tablet_tool_button *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_button_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; if (!sway_tool) { @@ -819,14 +824,14 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { switch (event->state) { case WLR_BUTTON_PRESSED: if (cursor->tool_buttons == 0) { - dispatch_cursor_button(cursor, event->device, + dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_RIGHT, event->state); } cursor->tool_buttons++; break; case WLR_BUTTON_RELEASED: if (cursor->tool_buttons == 1) { - dispatch_cursor_button(cursor, event->device, + dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_RIGHT, event->state); } cursor->tool_buttons--; @@ -923,65 +928,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, event->hotspot_y, focused_client); } +static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of( + listener, cursor, hold_begin); + struct wlr_pointer_hold_begin_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_hold_begin(cursor->seat, event); +} + +static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of( + listener, cursor, hold_end); + struct wlr_pointer_hold_end_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_hold_end(cursor->seat, event); +} + static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_begin); - struct wlr_event_pointer_pinch_begin *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_pinch_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); + struct wlr_pointer_pinch_begin_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_pinch_begin(cursor->seat, event); } static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_update); - struct wlr_event_pointer_pinch_update *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_pinch_update( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->dx, event->dy, - event->scale, event->rotation); + struct wlr_pointer_pinch_update_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_pinch_update(cursor->seat, event); } static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_end); - struct wlr_event_pointer_pinch_end *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_pinch_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); + struct wlr_pointer_pinch_end_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_pinch_end(cursor->seat, event); } static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_begin); - struct wlr_event_pointer_swipe_begin *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_swipe_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); + struct wlr_pointer_swipe_begin_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_swipe_begin(cursor->seat, event); } static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_update); - struct wlr_event_pointer_swipe_update *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_swipe_update( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->dx, event->dy); + struct wlr_pointer_swipe_update_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_swipe_update(cursor->seat, event); } static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_end); - struct wlr_event_pointer_swipe_end *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_swipe_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); + struct wlr_pointer_swipe_end_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_swipe_end(cursor->seat, event); } static void handle_image_surface_destroy(struct wl_listener *listener, @@ -1055,6 +1063,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_event_source_remove(cursor->hide_source); wl_list_remove(&cursor->image_surface_destroy.link); + wl_list_remove(&cursor->hold_begin.link); + wl_list_remove(&cursor->hold_end.link); wl_list_remove(&cursor->pinch_begin.link); wl_list_remove(&cursor->pinch_update.link); wl_list_remove(&cursor->pinch_end.link); @@ -1104,19 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_list_init(&cursor->image_surface_destroy.link); cursor->image_surface_destroy.notify = handle_image_surface_destroy; + // gesture events cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); - cursor->pinch_begin.notify = handle_pointer_pinch_begin; + + wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); + cursor->hold_begin.notify = handle_pointer_hold_begin; + wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); + cursor->hold_end.notify = handle_pointer_hold_end; + wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); - cursor->pinch_update.notify = handle_pointer_pinch_update; + cursor->pinch_begin.notify = handle_pointer_pinch_begin; wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); - cursor->pinch_end.notify = handle_pointer_pinch_end; + cursor->pinch_update.notify = handle_pointer_pinch_update; wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); - cursor->swipe_begin.notify = handle_pointer_swipe_begin; + cursor->pinch_end.notify = handle_pointer_pinch_end; + wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); - cursor->swipe_update.notify = handle_pointer_swipe_update; + cursor->swipe_begin.notify = handle_pointer_swipe_begin; wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); - cursor->swipe_end.notify = handle_pointer_swipe_end; + cursor->swipe_update.notify = handle_pointer_swipe_update; wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); + cursor->swipe_end.notify = handle_pointer_swipe_end; // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); @@ -1354,12 +1372,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { sway_constraint->destroy.notify = handle_constraint_destroy; wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); - struct sway_node *focus = seat_get_focus(seat); - if (focus && node_is_view(focus)) { - struct wlr_surface *surface = focus->sway_container->view->surface; - if (surface == constraint->surface) { - sway_cursor_constrain(seat->cursor, constraint); - } + struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; + if (surface && surface == constraint->surface) { + sway_cursor_constrain(seat->cursor, constraint); } } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f04a8ce0..39f4b795 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -236,7 +236,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) { apply_input_type_config(input_device); - sway_input_configure_libinput_device(input_device); + bool config_changed = sway_input_configure_libinput_device(input_device); wl_signal_add(&device->events.destroy, &input_device->device_destroy); input_device->device_destroy.notify = handle_device_destroy; @@ -274,6 +274,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) { } ipc_event_input("added", input_device); + + if (config_changed) { + ipc_event_input("libinput_config", input_device); + } } static void handle_inhibit_activate(struct wl_listener *listener, void *data) { @@ -289,6 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) struct sway_input_manager *input_manager = wl_container_of( listener, input_manager, inhibit_deactivate); struct sway_seat *seat; + if (server.session_lock.locked) { + // Don't deactivate the grab of a screenlocker + return; + } wl_list_for_each(seat, &input_manager->seats, link) { seat_set_exclusive_client(seat, NULL); struct sway_node *previous = seat_get_focus(seat); @@ -377,7 +385,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { struct sway_input_manager *input_manager = wl_container_of(listener, input_manager, virtual_keyboard_new); struct wlr_virtual_keyboard_v1 *keyboard = data; - struct wlr_input_device *device = &keyboard->input_device; + struct wlr_input_device *device = &keyboard->keyboard.base; // TODO: Amend protocol to allow NULL seat struct sway_seat *seat = keyboard->seat ? @@ -410,7 +418,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { wl_container_of(listener, input_manager, virtual_pointer_new); struct wlr_virtual_pointer_v1_new_pointer_event *event = data; struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; - struct wlr_input_device *device = &pointer->input_device; + struct wlr_input_device *device = &pointer->pointer.base; struct sway_seat *seat = event->suggested_seat ? input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : @@ -524,11 +532,14 @@ static void retranslate_keysyms(struct input_config *input_config) { static void input_manager_configure_input( struct sway_input_device *input_device) { - sway_input_configure_libinput_device(input_device); + bool config_changed = sway_input_configure_libinput_device(input_device); struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { seat_configure_device(seat, input_device); } + if (config_changed) { + ipc_event_input("libinput_config", input_device); + } } void input_manager_configure_all_inputs(void) { @@ -564,6 +575,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) { } void input_manager_reset_all_inputs(void) { + // Set the active keyboard to NULL to avoid spamming configuration updates + // for all keyboard devices. + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + wlr_seat_set_keyboard(seat->wlr_seat, NULL); + } + struct sway_input_device *input_device = NULL; wl_list_for_each(input_device, &server.input->devices, link) { input_manager_reset_input(input_device); @@ -572,7 +590,6 @@ void input_manager_reset_all_inputs(void) { // If there is at least one keyboard using the default keymap, repeat delay, // and repeat rate, then it is possible that there is a keyboard group that // need their keyboard disarmed. - struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { struct sway_keyboard_group *group; wl_list_for_each(group, &seat->keyboard_groups, link) { diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index f258ac7d..c5a646c4 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -291,14 +291,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, xkb_keycode_t keycode, const xkb_keysym_t **keysyms, uint32_t *modifiers) { - struct wlr_input_device *device = - keyboard->seat_device->input_device->wlr_device; - *modifiers = wlr_keyboard_get_modifiers(device->keyboard); + *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( - device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); + keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); *modifiers = *modifiers & ~consumed; - return xkb_state_key_get_syms(device->keyboard->xkb_state, + return xkb_state_key_get_syms(keyboard->wlr->xkb_state, keycode, keysyms); } @@ -314,13 +312,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, xkb_keycode_t keycode, const xkb_keysym_t **keysyms, uint32_t *modifiers) { - struct wlr_input_device *device = - keyboard->seat_device->input_device->wlr_device; - *modifiers = wlr_keyboard_get_modifiers(device->keyboard); + *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); xkb_layout_index_t layout_index = xkb_state_key_get_layout( - device->keyboard->xkb_state, keycode); - return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, + keyboard->wlr->xkb_state, keycode); + return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, keycode, layout_index, 0, keysyms); } @@ -360,8 +356,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, keyinfo->keycode, &keyinfo->translated_keysyms, &keyinfo->translated_modifiers); - keyinfo->code_modifiers = wlr_keyboard_get_modifiers( - keyboard->seat_device->input_device->wlr_device->keyboard); + keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); // Update shortcut model keyinfo update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, @@ -401,15 +396,16 @@ static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab( } static void handle_key_event(struct sway_keyboard *keyboard, - struct wlr_event_keyboard_key *event) { + struct wlr_keyboard_key_event *event) { struct sway_seat *seat = keyboard->seat_device->sway_seat; struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->wlr_device; char *device_identifier = input_device_get_identifier(wlr_device); - bool exact_identifier = wlr_device->keyboard->group != NULL; + bool exact_identifier = keyboard->wlr->group != NULL; seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); - bool input_inhibited = seat->exclusive_client != NULL; + bool input_inhibited = seat->exclusive_client != NULL || + server.session_lock.locked; struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; @@ -477,10 +473,10 @@ static void handle_key_event(struct sway_keyboard *keyboard, // Set up (or clear) keyboard repeat for a pressed binding. Since the // binding may remove the keyboard, the timer needs to be updated first if (binding && !(binding->flags & BINDING_NOREPEAT) && - wlr_device->keyboard->repeat_info.delay > 0) { + keyboard->wlr->repeat_info.delay > 0) { keyboard->repeat_binding = binding; if (wl_event_source_timer_update(keyboard->key_repeat_source, - wlr_device->keyboard->repeat_info.delay) < 0) { + keyboard->wlr->repeat_info.delay) < 0) { sway_log(SWAY_DEBUG, "failed to set key repeat timer"); } } else if (keyboard->repeat_binding) { @@ -492,7 +488,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, handled = true; } - if (!handled && wlr_device->keyboard->group) { + if (!handled && keyboard->wlr->group) { // Only handle device specific bindings for keyboards in a group free(device_identifier); return; @@ -517,7 +513,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, &keyboard->state_pressed_sent, event->keycode, event->state, keyinfo.keycode, 0); if (pressed_sent) { - wlr_seat_set_keyboard(wlr_seat, wlr_device); + wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); handled = true; @@ -528,8 +524,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); if (kb_grab) { - wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, - wlr_device->keyboard); + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); wlr_input_method_keyboard_grab_v2_send_key(kb_grab, event->time_msec, event->keycode, event->state); handled = true; @@ -542,7 +537,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, update_shortcut_state( &keyboard->state_pressed_sent, event->keycode, event->state, keyinfo.keycode, 0); - wlr_seat_set_keyboard(wlr_seat, wlr_device); + wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); } @@ -618,14 +613,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener, } static int handle_keyboard_repeat(void *data) { - struct sway_keyboard *keyboard = (struct sway_keyboard *)data; - struct wlr_keyboard *wlr_device = - keyboard->seat_device->input_device->wlr_device->keyboard; + struct sway_keyboard *keyboard = data; if (keyboard->repeat_binding) { - if (wlr_device->repeat_info.rate > 0) { + if (keyboard->wlr->repeat_info.rate > 0) { // We queue the next event first, as the command might cancel it if (wl_event_source_timer_update(keyboard->key_repeat_source, - 1000 / wlr_device->repeat_info.rate) < 0) { + 1000 / keyboard->wlr->repeat_info.rate) < 0) { sway_log(SWAY_DEBUG, "failed to update key repeat timer"); } } @@ -658,31 +651,28 @@ static void determine_bar_visibility(uint32_t modifiers) { } static void handle_modifier_event(struct sway_keyboard *keyboard) { - struct wlr_input_device *wlr_device = - keyboard->seat_device->input_device->wlr_device; - if (!wlr_device->keyboard->group) { + if (!keyboard->wlr->group) { struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); if (kb_grab) { - wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, - wlr_device->keyboard); + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, - &wlr_device->keyboard->modifiers); + &keyboard->wlr->modifiers); } else { struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; - wlr_seat_set_keyboard(wlr_seat, wlr_device); + wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); wlr_seat_keyboard_notify_modifiers(wlr_seat, - &wlr_device->keyboard->modifiers); + &keyboard->wlr->modifiers); } - uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); determine_bar_visibility(modifiers); } - if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { - keyboard->effective_layout = wlr_device->keyboard->modifiers.group; + if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { + keyboard->effective_layout = keyboard->wlr->modifiers.group; - if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { + if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) { ipc_event_input("xkb_layout", keyboard->seat_device->input_device); } } @@ -711,6 +701,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, } keyboard->seat_device = device; + keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); device->keyboard = keyboard; wl_list_init(&keyboard->keyboard_key.link); @@ -819,13 +810,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) { static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { struct sway_input_device *device = keyboard->seat_device->input_device; - struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; - struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; + struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", device->identifier, wlr_group); - wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); + wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr); if (wl_list_empty(&wlr_group->devices)) { sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", @@ -850,9 +840,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { } static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { - struct sway_input_device *device = keyboard->seat_device->input_device; - struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; - if (!wlr_keyboard->group) { + if (!keyboard->wlr->group) { return; } @@ -868,7 +856,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { break; case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ case KEYBOARD_GROUP_SMART:; - struct wlr_keyboard_group *group = wlr_keyboard->group; + struct wlr_keyboard_group *group = keyboard->wlr->group; if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || !repeat_info_match(keyboard, &group->keyboard)) { sway_keyboard_group_remove(keyboard); @@ -879,7 +867,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { struct sway_input_device *device = keyboard->seat_device->input_device; - struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; struct sway_seat *seat = keyboard->seat_device->sway_seat; struct seat_config *sc = seat_get_config(seat); @@ -911,7 +898,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { repeat_info_match(keyboard, &wlr_group->keyboard)) { sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", device->identifier, wlr_group); - wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); + wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); return; } break; @@ -950,7 +937,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { goto cleanup; } sway_group->seat_device->input_device->wlr_device = - sway_group->wlr_group->input_device; + &sway_group->wlr_group->keyboard.base; if (!sway_keyboard_create(seat, sway_group->seat_device)) { sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); @@ -959,7 +946,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", device->identifier, sway_group->wlr_group); - wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); + wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr); wl_list_insert(&seat->keyboard_groups, &sway_group->link); @@ -991,10 +978,8 @@ cleanup: void sway_keyboard_configure(struct sway_keyboard *keyboard) { struct input_config *input_config = input_device_get_config(keyboard->seat_device->input_device); - struct wlr_input_device *wlr_device = - keyboard->seat_device->input_device->wlr_device; - if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), + if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), "sway_keyboard_configure should not be called with a " "keyboard group's keyboard")) { return; @@ -1036,11 +1021,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { sway_keyboard_group_remove_invalid(keyboard); - wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); - wlr_keyboard_set_repeat_info(wlr_device->keyboard, + wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); + wlr_keyboard_set_repeat_info(keyboard->wlr, keyboard->repeat_rate, keyboard->repeat_delay); - if (!wlr_device->keyboard->group) { + if (!keyboard->wlr->group) { sway_keyboard_group_add(keyboard); } @@ -1060,40 +1045,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { } } if (locked_mods) { - wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, + wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, locked_mods, 0); uint32_t leds = 0; for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { - if (xkb_state_led_index_is_active( - wlr_device->keyboard->xkb_state, - wlr_device->keyboard->led_indexes[i])) { + if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, + keyboard->wlr->led_indexes[i])) { leds |= (1 << i); } } - if (wlr_device->keyboard->group) { - wlr_keyboard_led_update( - &wlr_device->keyboard->group->keyboard, leds); + if (keyboard->wlr->group) { + wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); } else { - wlr_keyboard_led_update(wlr_device->keyboard, leds); + wlr_keyboard_led_update(keyboard->wlr, leds); } } } else { xkb_keymap_unref(keymap); sway_keyboard_group_remove_invalid(keyboard); - if (!wlr_device->keyboard->group) { + if (!keyboard->wlr->group) { sway_keyboard_group_add(keyboard); } } + // If the seat has no active keyboard, set this one struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; - wlr_seat_set_keyboard(seat, wlr_device); + struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; + if (current_keyboard == NULL) { + wlr_seat_set_keyboard(seat, keyboard->wlr); + } wl_list_remove(&keyboard->keyboard_key.link); - wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); + wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); keyboard->keyboard_key.notify = handle_keyboard_key; wl_list_remove(&keyboard->keyboard_modifiers.link); - wl_signal_add(&wlr_device->keyboard->events.modifiers, + wl_signal_add(&keyboard->wlr->events.modifiers, &keyboard->keyboard_modifiers); keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; @@ -1110,12 +1097,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { if (!keyboard) { return; } - if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { + if (keyboard->wlr->group) { sway_keyboard_group_remove(keyboard); } struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; - struct sway_input_device *device = keyboard->seat_device->input_device; - if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) { + if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) { wlr_seat_set_keyboard(wlr_seat, NULL); } if (keyboard->keymap) { diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 3c0f359d..53019301 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -166,6 +166,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) { return true; } +static bool set_dwtp(struct libinput_device *device, bool dwtp) { + if (!libinput_device_config_dwtp_is_available(device) || + libinput_device_config_dwtp_get_enabled(device) == dwtp) { + return false; + } + sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp); + log_status(libinput_device_config_dwtp_set_enabled(device, dwtp)); + return true; +} + static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { if (!libinput_device_config_calibration_has_matrix(dev)) { return false; @@ -187,10 +197,10 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { return changed; } -void sway_input_configure_libinput_device(struct sway_input_device *input_device) { +bool sway_input_configure_libinput_device(struct sway_input_device *input_device) { struct input_config *ic = input_device_get_config(input_device); if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { - return; + return false; } struct libinput_device *device = @@ -255,13 +265,14 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->dwt != INT_MIN) { changed |= set_dwt(device, ic->dwt); } + if (ic->dwtp != INT_MIN) { + changed |= set_dwtp(device, ic->dwtp); + } if (ic->calibration_matrix.configured) { changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); } - if (changed) { - ipc_event_input("libinput_config", input_device); - } + return changed; } void sway_input_reset_libinput_device(struct sway_input_device *input_device) { @@ -304,6 +315,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_scroll_get_default_button(device)); changed |= set_dwt(device, libinput_device_config_dwt_get_default_enabled(device)); + changed |= set_dwtp(device, + libinput_device_config_dwtp_get_default_enabled(device)); float matrix[6]; libinput_device_config_calibration_get_default_matrix(device, matrix); diff --git a/sway/input/seat.c b/sway/input/seat.c index ce933b66..646f3866 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -7,10 +7,12 @@ #include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_idle_notify_v1.h> #include <wlr/types/wlr_keyboard_group.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_primary_selection.h> #include <wlr/types/wlr_tablet_v2.h> +#include <wlr/types/wlr_touch.h> #include <wlr/types/wlr_xcursor_manager.h> #include "config.h" #include "list.h" @@ -42,6 +44,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { sway_keyboard_destroy(seat_device->keyboard); sway_tablet_destroy(seat_device->tablet); sway_tablet_pad_destroy(seat_device->tablet_pad); + sway_switch_destroy(seat_device->switch_device); wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, seat_device->input_device->wlr_device); wl_list_remove(&seat_device->link); @@ -110,6 +113,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, } if ((source & mask) > 0) { wlr_idle_notify_activity(server.idle, seat->wlr_seat); + wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); } } @@ -140,7 +144,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { continue; } - if (input_device->wlr_device->keyboard == wlr_keyboard) { + if (input_device->wlr_device == &wlr_keyboard->base) { return seat_device->keyboard; } } @@ -148,7 +152,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( wl_list_for_each(group, &seat->keyboard_groups, link) { struct sway_input_device *input_device = group->seat_device->input_device; - if (input_device->wlr_device->keyboard == wlr_keyboard) { + if (input_device->wlr_device == &wlr_keyboard->base) { return group->seat_device->keyboard; } } @@ -245,7 +249,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { seat_node_destroy(seat_node); // If an unmanaged or layer surface is focused when an output gets // disabled and an empty workspace on the output was focused by the - // seat, the seat needs to refocus it's focus inactive to update the + // seat, the seat needs to refocus its focus inactive to update the // value of seat->workspace. if (seat->workspace == node->sway_workspace) { struct sway_node *node = seat_get_focus_inactive(seat, &root->node); @@ -378,8 +382,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { case WLR_DRAG_GRAB_KEYBOARD: return; case WLR_DRAG_GRAB_KEYBOARD_POINTER: - icon->x = cursor->x; - icon->y = cursor->y; + icon->x = cursor->x + wlr_icon->surface->sx; + icon->y = cursor->y + wlr_icon->surface->sy; break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -387,8 +391,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x; - icon->y = seat->touch_y; + icon->x = seat->touch_x + wlr_icon->surface->sx; + icon->y = seat->touch_y + wlr_icon->surface->sy; } drag_icon_damage_whole(icon); @@ -724,14 +728,25 @@ static void seat_apply_input_config(struct sway_seat *seat, ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; switch (mapped_to) { - case MAPPED_TO_DEFAULT: + case MAPPED_TO_DEFAULT:; /* * If the wlroots backend provides an output name, use that. * - * Otherwise, try to map built-in touch and tablet tool devices to the + * Otherwise, try to map built-in touch and pointer devices to the * built-in output. */ - mapped_to_output = sway_device->input_device->wlr_device->output_name; + struct wlr_input_device *dev = sway_device->input_device->wlr_device; + switch (dev->type) { + case WLR_INPUT_DEVICE_POINTER: + mapped_to_output = wlr_pointer_from_input_device(dev)->output_name; + break; + case WLR_INPUT_DEVICE_TOUCH: + mapped_to_output = wlr_touch_from_input_device(dev)->output_name; + break; + default: + mapped_to_output = NULL; + break; + } if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && sway_libinput_device_is_builtin(sway_device->input_device)) { mapped_to_output = get_builtin_output_name(); @@ -799,13 +814,22 @@ static void seat_configure_keyboard(struct sway_seat *seat, sway_keyboard_create(seat, seat_device); } sway_keyboard_configure(seat_device->keyboard); - wlr_seat_set_keyboard(seat->wlr_seat, - seat_device->input_device->wlr_device); - struct sway_node *focus = seat_get_focus(seat); - if (focus && node_is_view(focus)) { - // force notify reenter to pick up the new configuration + + // We only need to update the current keyboard, as the rest will be updated + // as they are activated. + struct wlr_keyboard *wlr_keyboard = + wlr_keyboard_from_input_device(seat_device->input_device->wlr_device); + struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard; + if (wlr_keyboard != current_keyboard) { + return; + } + + // force notify reenter to pick up the new configuration. This reuses + // the current focused surface to avoid breaking input grabs. + struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; + if (surface) { wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); - seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); + seat_keyboard_notify_enter(seat, surface); } } @@ -1057,7 +1081,8 @@ void seat_configure_xcursor(struct sway_seat *seat) { bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); - return !seat->exclusive_client || seat->exclusive_client == client; + return seat->exclusive_client == client || + (seat->exclusive_client == NULL && !server.session_lock.locked); } static void send_unfocus(struct sway_container *con, void *data) { @@ -1116,15 +1141,7 @@ 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; - } - +static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) { struct sway_node *last_focus = seat_get_focus(seat); if (last_focus == node) { return; @@ -1257,6 +1274,20 @@ void seat_set_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_workspace_focus(seat, node); + seat_set_focus_layer(seat, layer); + } else { + seat_set_workspace_focus(seat, node); + } + if (server.session_lock.locked) { + seat_set_focus_surface(seat, server.session_lock.focused, false); + } +} + void seat_set_focus_container(struct sway_seat *seat, struct sway_container *con) { seat_set_focus(seat, con ? &con->node : NULL); @@ -1561,7 +1592,7 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { } void seatop_pointer_axis(struct sway_seat *seat, - struct wlr_event_pointer_axis *event) { + struct wlr_pointer_axis_event *event) { if (seat->seatop_impl->pointer_axis) { seat->seatop_impl->pointer_axis(seat, event); } @@ -1584,6 +1615,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat, } } +void seatop_hold_begin(struct sway_seat *seat, + struct wlr_pointer_hold_begin_event *event) { + if (seat->seatop_impl->hold_begin) { + seat->seatop_impl->hold_begin(seat, event); + } +} + +void seatop_hold_end(struct sway_seat *seat, + struct wlr_pointer_hold_end_event *event) { + if (seat->seatop_impl->hold_end) { + seat->seatop_impl->hold_end(seat, event); + } +} + +void seatop_pinch_begin(struct sway_seat *seat, + struct wlr_pointer_pinch_begin_event *event) { + if (seat->seatop_impl->pinch_begin) { + seat->seatop_impl->pinch_begin(seat, event); + } +} + +void seatop_pinch_update(struct sway_seat *seat, + struct wlr_pointer_pinch_update_event *event) { + if (seat->seatop_impl->pinch_update) { + seat->seatop_impl->pinch_update(seat, event); + } +} + +void seatop_pinch_end(struct sway_seat *seat, + struct wlr_pointer_pinch_end_event *event) { + if (seat->seatop_impl->pinch_end) { + seat->seatop_impl->pinch_end(seat, event); + } +} + +void seatop_swipe_begin(struct sway_seat *seat, + struct wlr_pointer_swipe_begin_event *event) { + if (seat->seatop_impl->swipe_begin) { + seat->seatop_impl->swipe_begin(seat, event); + } +} + +void seatop_swipe_update(struct sway_seat *seat, + struct wlr_pointer_swipe_update_event *event) { + if (seat->seatop_impl->swipe_update) { + seat->seatop_impl->swipe_update(seat, event); + } +} + +void seatop_swipe_end(struct sway_seat *seat, + struct wlr_pointer_swipe_end_event *event) { + if (seat->seatop_impl->swipe_end) { + seat->seatop_impl->swipe_end(seat, event); + } +} + void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { if (seat->seatop_impl->rebase) { seat->seatop_impl->rebase(seat, time_msec); diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 4320a3b4..84acefdf 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -4,6 +4,7 @@ #include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_xcursor_manager.h> +#include "gesture.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -20,6 +21,7 @@ struct seatop_default_event { struct sway_node *previous_node; uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; size_t pressed_button_count; + struct gesture_tracker gestures; }; /*-----------------------------------------\ @@ -427,13 +429,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } } + // Handle changing focus when clicking on a container + if (cont && state == WLR_BUTTON_PRESSED) { + // Default case: focus the container that was just clicked. + node = &cont->node; + + // If the container is a tab/stacked container and the click happened + // on a tab, switch to the tab. If the tab contents were already + // focused, focus the tab container itself. If the tab container was + // already focused, cycle back to focusing the tab contents. + if (on_titlebar) { + struct sway_container *focus = seat_get_focused_container(seat); + if (focus == cont || !container_has_ancestor(focus, cont)) { + node = seat_get_focus_inactive(seat, &cont->node); + } + } + + seat_set_focus(seat, node); + transaction_commit_dirty(); + } + // Handle beginning floating move if (cont && is_floating_or_child && !is_fullscreen_or_child && state == WLR_BUTTON_PRESSED) { uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && (mod_pressed || on_titlebar)) { - seat_set_focus_container(seat, - seat_get_focus_inactive_view(seat, &cont->node)); seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); return; } @@ -444,6 +464,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seat_set_focus_container(seat, cont); seatop_begin_resize_floating(seat, cont, resize_edge); return; } @@ -458,6 +479,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seat_set_focus_container(seat, floater); seatop_begin_resize_floating(seat, floater, edge); return; } @@ -467,25 +489,18 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, if (config->tiling_drag && (mod_pressed || on_titlebar) && state == WLR_BUTTON_PRESSED && !is_floating_or_child && cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { - struct sway_container *focus = seat_get_focused_container(seat); - bool focused = focus == cont || container_has_ancestor(focus, cont); - if (on_titlebar && !focused) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - } - - // If moving a container by it's title bar, use a threshold for the drag + // If moving a container by its title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont); } else { seatop_begin_move_tiling(seat, cont); } + return; } // Handle mousedown on a container surface if (surface && cont && state == WLR_BUTTON_PRESSED) { - seat_set_focus_container(seat, cont); seatop_begin_down(seat, cont, time_msec, sx, sy); seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); return; @@ -493,9 +508,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle clicking a container surface or decorations if (cont && state == WLR_BUTTON_PRESSED) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - transaction_commit_dirty(); seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -645,7 +657,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, * Functions used by handle_pointer_axis / *--------------------------------------*/ -static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { +static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { switch (event->orientation) { case WLR_AXIS_ORIENTATION_VERTICAL: return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; @@ -658,9 +670,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { } static void handle_pointer_axis(struct sway_seat *seat, - struct wlr_event_pointer_axis *event) { + struct wlr_pointer_axis_event *event) { struct sway_input_device *input_device = - event->device ? event->device->data : NULL; + event->pointer ? event->pointer->base.data : NULL; struct input_config *ic = input_device ? input_device_get_config(input_device) : NULL; struct sway_cursor *cursor = seat->cursor; @@ -706,6 +718,7 @@ static void handle_pointer_axis(struct sway_seat *seat, // Scrolling on a tabbed or stacked title bar (handled as press event) if (!handled && (on_titlebar || on_titlebar_border)) { + struct sway_node *new_focus; enum sway_container_layout layout = container_parent_layout(cont); if (layout == L_TABBED || layout == L_STACKED) { struct sway_node *tabcontainer = node_get_parent(node); @@ -713,7 +726,7 @@ static void handle_pointer_axis(struct sway_seat *seat, seat_get_active_tiling_child(seat, tabcontainer); list_t *siblings = container_get_siblings(cont); int desired = list_find(siblings, active->sway_container) + - round(scroll_factor * event->delta_discrete); + round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); if (desired < 0) { desired = 0; } else if (desired >= siblings->length) { @@ -722,14 +735,16 @@ static void handle_pointer_axis(struct sway_seat *seat, struct sway_container *new_sibling_con = siblings->items[desired]; struct sway_node *new_sibling = &new_sibling_con->node; - struct sway_node *new_focus = - seat_get_focus_inactive(seat, new_sibling); // Use the focused child of the tabbed/stacked container, not the // container the user scrolled on. - seat_set_focus(seat, new_focus); - transaction_commit_dirty(); - handled = true; + new_focus = seat_get_focus_inactive(seat, new_sibling); + } else { + new_focus = seat_get_focus_inactive(seat, &cont->node); } + + seat_set_focus(seat, new_focus); + transaction_commit_dirty(); + handled = true; } // Handle mouse bindings - x11 mouse buttons 4-7 - release event @@ -750,6 +765,304 @@ static void handle_pointer_axis(struct sway_seat *seat, } } +/*------------------------------------\ + * Functions used by gesture support / + *----------------------------------*/ + +/** + * Check gesture binding for a specific gesture type and finger count. + * Returns true if binding is present, false otherwise + */ +static bool gesture_binding_check(list_t *bindings, enum gesture_type type, + uint8_t fingers, struct sway_input_device *device) { + char *input = + device ? input_device_get_identifier(device->wlr_device) : strdup("*"); + + for (int i = 0; i < bindings->length; ++i) { + struct sway_gesture_binding *binding = bindings->items[i]; + + // Check type and finger count + if (!gesture_check(&binding->gesture, type, fingers)) { + continue; + } + + // Check that input matches + if (strcmp(binding->input, "*") != 0 && + strcmp(binding->input, input) != 0) { + continue; + } + + free(input); + + return true; + } + + free(input); + + return false; +} + +/** + * Return the gesture binding which matches gesture type, finger count + * and direction, otherwise return null. + */ +static struct sway_gesture_binding* gesture_binding_match( + list_t *bindings, struct gesture *gesture, const char *input) { + struct sway_gesture_binding *current = NULL; + + // Find best matching binding + for (int i = 0; i < bindings->length; ++i) { + struct sway_gesture_binding *binding = bindings->items[i]; + bool exact = binding->flags & BINDING_EXACT; + + // Check gesture matching + if (!gesture_match(&binding->gesture, gesture, exact)) { + continue; + } + + // Check input matching + if (strcmp(binding->input, "*") != 0 && + strcmp(binding->input, input) != 0) { + continue; + } + + // If we already have a match ... + if (current) { + // ... check if input matching is equivalent + if (strcmp(current->input, binding->input) == 0) { + + // ... - do not override an exact binding + if (!exact && current->flags & BINDING_EXACT) { + continue; + } + + // ... - and ensure direction matching is better or equal + if (gesture_compare(¤t->gesture, &binding->gesture) > 0) { + continue; + } + } else if (strcmp(binding->input, "*") == 0) { + // ... do not accept worse input match + continue; + } + } + + // Accept newer or better match + current = binding; + + // If exact binding and input is found, quit search + if (strcmp(current->input, input) == 0 && + gesture_compare(¤t->gesture, gesture) == 0) { + break; + } + } // for all gesture bindings + + return current; +} + +// Wrapper around gesture_tracker_end to use tracker with sway bindings +static struct sway_gesture_binding* gesture_tracker_end_and_match( + struct gesture_tracker *tracker, struct sway_input_device* device) { + // Determine name of input that received gesture + char *input = device + ? input_device_get_identifier(device->wlr_device) + : strdup("*"); + + // Match tracking result to binding + struct gesture *gesture = gesture_tracker_end(tracker); + struct sway_gesture_binding *binding = gesture_binding_match( + config->current_mode->gesture_bindings, gesture, input); + free(gesture); + free(input); + + return binding; +} + +// Small wrapper around seat_execute_command to work on gesture bindings +static void gesture_binding_execute(struct sway_seat *seat, + struct sway_gesture_binding *binding) { + struct sway_binding *dummy_binding = + calloc(1, sizeof(struct sway_binding)); + dummy_binding->type = BINDING_GESTURE; + dummy_binding->command = binding->command; + + char *description = gesture_to_string(&binding->gesture); + sway_log(SWAY_DEBUG, "executing gesture binding: %s", description); + free(description); + + seat_execute_command(seat, dummy_binding); + + free(dummy_binding); +} + +static void handle_hold_begin(struct sway_seat *seat, + struct wlr_pointer_hold_begin_event *event) { + // Start tracking gesture if there is a matching binding ... + struct sway_input_device *device = + event->pointer ? event->pointer->base.data : NULL; + list_t *bindings = config->current_mode->gesture_bindings; + if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) { + struct seatop_default_event *seatop = seat->seatop_data; + gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers); + } else { + // ... otherwise forward to client + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_hold_begin( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->fingers); + } +} + +static void handle_hold_end(struct sway_seat *seat, + struct wlr_pointer_hold_end_event *event) { + // Ensure that gesture is being tracked and was not cancelled + struct seatop_default_event *seatop = seat->seatop_data; + if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_hold_end( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->cancelled); + return; + } + if (event->cancelled) { + gesture_tracker_cancel(&seatop->gestures); + return; + } + + // End gesture tracking and execute matched binding + struct sway_input_device *device = + event->pointer ? event->pointer->base.data : NULL; + struct sway_gesture_binding *binding = gesture_tracker_end_and_match( + &seatop->gestures, device); + + if (binding) { + gesture_binding_execute(seat, binding); + } +} + +static void handle_pinch_begin(struct sway_seat *seat, + struct wlr_pointer_pinch_begin_event *event) { + // Start tracking gesture if there is a matching binding ... + struct sway_input_device *device = + event->pointer ? event->pointer->base.data : NULL; + list_t *bindings = config->current_mode->gesture_bindings; + if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) { + struct seatop_default_event *seatop = seat->seatop_data; + gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers); + } else { + // ... otherwise forward to client + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_pinch_begin( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->fingers); + } +} + +static void handle_pinch_update(struct sway_seat *seat, + struct wlr_pointer_pinch_update_event *event) { + // Update any ongoing tracking ... + struct seatop_default_event *seatop = seat->seatop_data; + if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { + gesture_tracker_update(&seatop->gestures, event->dx, event->dy, + event->scale, event->rotation); + } else { + // ... otherwise forward to client + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_pinch_update( + cursor->pointer_gestures, + cursor->seat->wlr_seat, + event->time_msec, event->dx, event->dy, + event->scale, event->rotation); + } +} + +static void handle_pinch_end(struct sway_seat *seat, + struct wlr_pointer_pinch_end_event *event) { + // Ensure that gesture is being tracked and was not cancelled + struct seatop_default_event *seatop = seat->seatop_data; + if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_pinch_end( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->cancelled); + return; + } + if (event->cancelled) { + gesture_tracker_cancel(&seatop->gestures); + return; + } + + // End gesture tracking and execute matched binding + struct sway_input_device *device = + event->pointer ? event->pointer->base.data : NULL; + struct sway_gesture_binding *binding = gesture_tracker_end_and_match( + &seatop->gestures, device); + + if (binding) { + gesture_binding_execute(seat, binding); + } +} + +static void handle_swipe_begin(struct sway_seat *seat, + struct wlr_pointer_swipe_begin_event *event) { + // Start tracking gesture if there is a matching binding ... + struct sway_input_device *device = + event->pointer ? event->pointer->base.data : NULL; + list_t *bindings = config->current_mode->gesture_bindings; + if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { + struct seatop_default_event *seatop = seat->seatop_data; + gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); + } else { + // ... otherwise forward to client + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_swipe_begin( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->fingers); + } +} + +static void handle_swipe_update(struct sway_seat *seat, + struct wlr_pointer_swipe_update_event *event) { + + // Update any ongoing tracking ... + struct seatop_default_event *seatop = seat->seatop_data; + if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { + gesture_tracker_update(&seatop->gestures, + event->dx, event->dy, NAN, NAN); + } else { + // ... otherwise forward to client + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_swipe_update( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->dx, event->dy); + } +} + +static void handle_swipe_end(struct sway_seat *seat, + struct wlr_pointer_swipe_end_event *event) { + // Ensure gesture is being tracked and was not cancelled + struct seatop_default_event *seatop = seat->seatop_data; + if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { + struct sway_cursor *cursor = seat->cursor; + wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures, + cursor->seat->wlr_seat, event->time_msec, event->cancelled); + return; + } + if (event->cancelled) { + gesture_tracker_cancel(&seatop->gestures); + return; + } + + // End gesture tracking and execute matched binding + struct sway_input_device *device = + event->pointer ? event->pointer->base.data : NULL; + struct sway_gesture_binding *binding = gesture_tracker_end_and_match( + &seatop->gestures, device); + + if (binding) { + gesture_binding_execute(seat, binding); + } +} + /*----------------------------------\ * Functions used by handle_rebase / *--------------------------------*/ @@ -779,6 +1092,14 @@ static const struct sway_seatop_impl seatop_impl = { .pointer_axis = handle_pointer_axis, .tablet_tool_tip = handle_tablet_tool_tip, .tablet_tool_motion = handle_tablet_tool_motion, + .hold_begin = handle_hold_begin, + .hold_end = handle_hold_end, + .pinch_begin = handle_pinch_begin, + .pinch_update = handle_pinch_update, + .pinch_end = handle_pinch_end, + .swipe_begin = handle_swipe_begin, + .swipe_update = handle_swipe_update, + .swipe_end = handle_swipe_end, .rebase = handle_rebase, .allow_set_cursor = true, }; @@ -789,8 +1110,8 @@ void seatop_begin_default(struct sway_seat *seat) { struct seatop_default_event *e = calloc(1, sizeof(struct seatop_default_event)); sway_assert(e, "Unable to allocate seatop_default_event"); + seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seatop_rebase(seat, 0); } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index ecc34fea..b40773d0 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -18,9 +18,9 @@ struct seatop_down_event { }; static void handle_pointer_axis(struct sway_seat *seat, - struct wlr_event_pointer_axis *event) { + struct wlr_pointer_axis_event *event) { struct sway_input_device *input_device = - event->device ? event->device->data : NULL; + event->pointer ? event->pointer->base.data : NULL; struct input_config *ic = input_device ? input_device_get_config(input_device) : NULL; float scroll_factor = diff --git a/sway/input/switch.c b/sway/input/switch.c index 9ea87a1a..fc7dfaff 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -11,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, return NULL; } device->switch_device = switch_device; + switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device); switch_device->seat_device = device; switch_device->state = WLR_SWITCH_STATE_OFF; wl_list_init(&switch_device->switch_toggle.link); @@ -19,9 +20,23 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, return switch_device; } +static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, + enum wlr_switch_state state) { + switch (trigger) { + case SWAY_SWITCH_TRIGGER_ON: + return state == WLR_SWITCH_STATE_ON; + case SWAY_SWITCH_TRIGGER_OFF: + return state == WLR_SWITCH_STATE_OFF; + case SWAY_SWITCH_TRIGGER_TOGGLE: + return true; + } + abort(); // unreachable +} + static void execute_binding(struct sway_switch *sway_switch) { struct sway_seat* seat = sway_switch->seat_device->sway_seat; - bool input_inhibited = seat->exclusive_client != NULL; + bool input_inhibited = seat->exclusive_client != NULL || + server.session_lock.locked; list_t *bindings = config->current_mode->switch_bindings; struct sway_switch_binding *matched_binding = NULL; @@ -30,11 +45,10 @@ static void execute_binding(struct sway_switch *sway_switch) { if (binding->type != sway_switch->type) { continue; } - if (binding->state != WLR_SWITCH_STATE_TOGGLE && - binding->state != sway_switch->state) { + if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { continue; } - if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE + if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE || (binding->flags & BINDING_RELOAD) == 0)) { continue; } @@ -65,7 +79,7 @@ static void execute_binding(struct sway_switch *sway_switch) { static void handle_switch_toggle(struct wl_listener *listener, void *data) { struct sway_switch *sway_switch = wl_container_of(listener, sway_switch, switch_toggle); - struct wlr_event_switch_toggle *event = data; + struct wlr_switch_toggle_event *event = data; struct sway_seat *seat = sway_switch->seat_device->sway_seat; seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); @@ -82,10 +96,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) { } void sway_switch_configure(struct sway_switch *sway_switch) { - struct wlr_input_device *wlr_device = - sway_switch->seat_device->input_device->wlr_device; wl_list_remove(&sway_switch->switch_toggle.link); - wl_signal_add(&wlr_device->switch_device->events.toggle, + wl_signal_add(&sway_switch->wlr->events.toggle, &sway_switch->switch_toggle); sway_switch->switch_toggle.notify = handle_switch_toggle; sway_log(SWAY_DEBUG, "Configured switch for device"); diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 26e86e36..92ede3fa 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -196,7 +196,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener, static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); - struct wlr_event_tablet_pad_ring *event = data; + struct wlr_tablet_pad_ring_event *event = data; if (!pad->current_surface) { return; @@ -210,7 +210,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); - struct wlr_event_tablet_pad_strip *event = data; + struct wlr_tablet_pad_strip_event *event = data; if (!pad->current_surface) { return; @@ -224,7 +224,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); - struct wlr_event_tablet_pad_button *event = data; + struct wlr_tablet_pad_button_event *event = data; if (!pad->current_surface) { return; @@ -246,6 +246,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, return NULL; } + tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); tablet_pad->seat_device = device; wl_list_init(&tablet_pad->attach.link); wl_list_init(&tablet_pad->button.link); @@ -260,40 +261,40 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, } void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { - struct wlr_input_device *device = + struct wlr_input_device *wlr_device = tablet_pad->seat_device->input_device->wlr_device; struct sway_seat *seat = tablet_pad->seat_device->sway_seat; if (!tablet_pad->tablet_v2_pad) { tablet_pad->tablet_v2_pad = - wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); + wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device); } wl_list_remove(&tablet_pad->attach.link); tablet_pad->attach.notify = handle_tablet_pad_attach; - wl_signal_add(&device->tablet_pad->events.attach_tablet, + wl_signal_add(&tablet_pad->wlr->events.attach_tablet, &tablet_pad->attach); wl_list_remove(&tablet_pad->button.link); tablet_pad->button.notify = handle_tablet_pad_button; - wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); + wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button); wl_list_remove(&tablet_pad->strip.link); tablet_pad->strip.notify = handle_tablet_pad_strip; - wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); + wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip); wl_list_remove(&tablet_pad->ring.link); tablet_pad->ring.notify = handle_tablet_pad_ring; - wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); + wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); /* Search for a sibling tablet */ - if (!wlr_input_device_is_libinput(device)) { + if (!wlr_input_device_is_libinput(wlr_device)) { /* We can only do this on libinput devices */ return; } struct libinput_device_group *group = - libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); + libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device)); struct sway_tablet *tool; wl_list_for_each(tool, &seat->cursor->tablets, link) { struct wlr_input_device *tablet = diff --git a/sway/input/text_input.c b/sway/input/text_input.c index b8c19c17..58911c2d 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -77,8 +77,6 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, active_keyboard); - wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, - &active_keyboard->modifiers); wl_signal_add(&keyboard_grab->events.destroy, &relay->input_method_keyboard_grab_destroy); diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 1b64f86e..d757f21f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -112,12 +112,43 @@ static const char *ipc_json_output_adaptive_sync_status_description( return "disabled"; case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: return "enabled"; - case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN: - return "unknown"; } return NULL; } +static const char *ipc_json_output_mode_aspect_ratio_description( + enum wlr_output_mode_aspect_ratio aspect_ratio) { + switch (aspect_ratio) { + case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE: + return "none"; + case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3: + return "4:3"; + case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9: + return "16:9"; + case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27: + return "64:27"; + case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135: + return "256:135"; + } + return NULL; +} + +static json_object *ipc_json_output_mode_description( + const struct wlr_output_mode *mode) { + const char *pic_ar = + ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio); + json_object *mode_object = json_object_new_object(); + json_object_object_add(mode_object, "width", + json_object_new_int(mode->width)); + json_object_object_add(mode_object, "height", + json_object_new_int(mode->height)); + json_object_object_add(mode_object, "refresh", + json_object_new_int(mode->refresh)); + json_object_object_add(mode_object, "picture_aspect_ratio", + json_object_new_string(pic_ar)); + return mode_object; +} + #if HAVE_XWAYLAND static const char *ipc_json_xwindow_type_description(struct sway_view *view) { struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; @@ -242,23 +273,50 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name, return object; } +static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { + json_object_object_add(object, "primary", json_object_new_boolean(false)); + json_object_object_add(object, "make", + json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); + json_object_object_add(object, "model", + json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown")); + json_object_object_add(object, "serial", + json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown")); + + json_object *modes_array = json_object_new_array(); + struct wlr_output_mode *mode; + wl_list_for_each(mode, &wlr_output->modes, link) { + json_object *mode_object = json_object_new_object(); + json_object_object_add(mode_object, "width", + json_object_new_int(mode->width)); + json_object_object_add(mode_object, "height", + json_object_new_int(mode->height)); + json_object_object_add(mode_object, "refresh", + json_object_new_int(mode->refresh)); + json_object_array_add(modes_array, mode_object); + } + json_object_object_add(object, "modes", modes_array); +} + static void ipc_json_describe_output(struct sway_output *output, json_object *object) { + ipc_json_describe_wlr_output(output->wlr_output, object); +} + +static void ipc_json_describe_enabled_output(struct sway_output *output, + json_object *object) { + ipc_json_describe_output(output, object); + struct wlr_output *wlr_output = output->wlr_output; + json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); json_object_object_add(object, "active", json_object_new_boolean(true)); json_object_object_add(object, "dpms", json_object_new_boolean(wlr_output->enabled)); - json_object_object_add(object, "primary", json_object_new_boolean(false)); + json_object_object_add(object, "power", + json_object_new_boolean(wlr_output->enabled)); json_object_object_add(object, "layout", json_object_new_string("output")); json_object_object_add(object, "orientation", json_object_new_string( ipc_json_orientation_description(L_NONE))); - json_object_object_add(object, "make", - json_object_new_string(wlr_output->make)); - json_object_object_add(object, "model", - json_object_new_string(wlr_output->model)); - json_object_object_add(object, "serial", - json_object_new_string(wlr_output->serial)); json_object_object_add(object, "scale", json_object_new_double(wlr_output->scale)); json_object_object_add(object, "scale_filter", @@ -283,25 +341,26 @@ static void ipc_json_describe_output(struct sway_output *output, json_object *modes_array = json_object_new_array(); struct wlr_output_mode *mode; wl_list_for_each(mode, &wlr_output->modes, link) { - json_object *mode_object = json_object_new_object(); - json_object_object_add(mode_object, "width", - json_object_new_int(mode->width)); - json_object_object_add(mode_object, "height", - json_object_new_int(mode->height)); - json_object_object_add(mode_object, "refresh", - json_object_new_int(mode->refresh)); + json_object *mode_object = + ipc_json_output_mode_description(mode); json_object_array_add(modes_array, mode_object); } json_object_object_add(object, "modes", modes_array); - json_object *current_mode_object = json_object_new_object(); - json_object_object_add(current_mode_object, "width", - json_object_new_int(wlr_output->width)); - json_object_object_add(current_mode_object, "height", - json_object_new_int(wlr_output->height)); - json_object_object_add(current_mode_object, "refresh", - json_object_new_int(wlr_output->refresh)); + json_object *current_mode_object; + if (wlr_output->current_mode != NULL) { + current_mode_object = + ipc_json_output_mode_description(wlr_output->current_mode); + } else { + current_mode_object = json_object_new_object(); + json_object_object_add(current_mode_object, "width", + json_object_new_int(wlr_output->width)); + json_object_object_add(current_mode_object, "height", + json_object_new_int(wlr_output->height)); + json_object_object_add(current_mode_object, "refresh", + json_object_new_int(wlr_output->refresh)); + } json_object_object_add(object, "current_mode", current_mode_object); struct sway_node *parent = node_get_parent(&output->node); @@ -325,33 +384,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object *object = json_object_new_object(); + ipc_json_describe_output(output, object); + + json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); json_object_object_add(object, "type", json_object_new_string("output")); json_object_object_add(object, "name", json_object_new_string(wlr_output->name)); json_object_object_add(object, "active", json_object_new_boolean(false)); json_object_object_add(object, "dpms", json_object_new_boolean(false)); - json_object_object_add(object, "primary", json_object_new_boolean(false)); - json_object_object_add(object, "make", - json_object_new_string(wlr_output->make)); - json_object_object_add(object, "model", - json_object_new_string(wlr_output->model)); - json_object_object_add(object, "serial", - json_object_new_string(wlr_output->serial)); - - json_object *modes_array = json_object_new_array(); - struct wlr_output_mode *mode; - wl_list_for_each(mode, &wlr_output->modes, link) { - json_object *mode_object = json_object_new_object(); - json_object_object_add(mode_object, "width", - json_object_new_int(mode->width)); - json_object_object_add(mode_object, "height", - json_object_new_int(mode->height)); - json_object_object_add(mode_object, "refresh", - json_object_new_int(mode->refresh)); - json_object_array_add(modes_array, mode_object); - } - - json_object_object_add(object, "modes", modes_array); + json_object_object_add(object, "power", json_object_new_boolean(false)); json_object_object_add(object, "current_workspace", NULL); @@ -367,6 +408,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { return object; } +json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) { + struct wlr_output *wlr_output = output->wlr_output; + + json_object *object = json_object_new_object(); + + ipc_json_describe_wlr_output(wlr_output, object); + + json_object_object_add(object, "non_desktop", json_object_new_boolean(true)); + json_object_object_add(object, "type", json_object_new_string("output")); + json_object_object_add(object, "name", + json_object_new_string(wlr_output->name)); + + return object; +} + static json_object *ipc_json_describe_scratchpad_output(void) { struct wlr_box box; root_get_box(root, &box); @@ -453,7 +509,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); - bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; + list_t *siblings = container_get_siblings(c); + bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED) + && ((siblings && siblings->length > 1) || !config->hide_lone_tab); if (((!tab_or_stack || container_is_floating(c)) && c->current.border != B_NORMAL) || c->pending.fullscreen_mode != FULLSCREEN_NONE || @@ -706,7 +764,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) { case N_ROOT: break; case N_OUTPUT: - ipc_json_describe_output(node->sway_output, object); + ipc_json_describe_enabled_output(node->sway_output, object); break; case N_CONTAINER: ipc_json_describe_container(node->sway_container, object); @@ -942,6 +1000,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) { json_object_object_add(object, "dwt", json_object_new_string(dwt)); } + if (libinput_device_config_dwtp_is_available(device)) { + const char *dwtp = "unknown"; + switch (libinput_device_config_dwtp_get_enabled(device)) { + case LIBINPUT_CONFIG_DWTP_ENABLED: + dwtp = "enabled"; + break; + case LIBINPUT_CONFIG_DWTP_DISABLED: + dwtp = "disabled"; + break; + } + json_object_object_add(object, "dwtp", json_object_new_string(dwtp)); + } + if (libinput_device_config_calibration_has_matrix(device)) { float matrix[6]; libinput_device_config_calibration_get_matrix(device, matrix); @@ -977,10 +1048,16 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { input_device_get_type(device))); if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { - struct wlr_keyboard *keyboard = device->wlr_device->keyboard; + struct wlr_keyboard *keyboard = + wlr_keyboard_from_input_device(device->wlr_device); struct xkb_keymap *keymap = keyboard->keymap; struct xkb_state *state = keyboard->xkb_state; + json_object_object_add(object, "repeat_delay", + json_object_new_int(keyboard->repeat_info.delay)); + json_object_object_add(object, "repeat_rate", + json_object_new_int(keyboard->repeat_info.rate)); + json_object *layouts_arr = json_object_new_array(); json_object_object_add(object, "xkb_layout_names", layouts_arr); @@ -1005,11 +1082,11 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { struct input_config *ic = input_device_get_config(device); float scroll_factor = 1.0f; - if (ic != NULL && !isnan(ic->scroll_factor) && + if (ic != NULL && !isnan(ic->scroll_factor) && ic->scroll_factor != FLT_MIN) { scroll_factor = ic->scroll_factor; } - json_object_object_add(object, "scroll_factor", + json_object_object_add(object, "scroll_factor", json_object_new_double(scroll_factor)); } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 1bf5a05f..00b01d7d 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -150,7 +150,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { int ipc_handle_connection(int fd, uint32_t mask, void *data) { (void) fd; struct sway_server *server = data; - sway_log(SWAY_DEBUG, "Event on IPC listening socket"); assert(mask == WL_EVENT_READABLE); int client_fd = accept(ipc_socket, NULL, NULL); @@ -211,13 +210,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } if (mask & WL_EVENT_HANGUP) { - sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } - sway_log(SWAY_DEBUG, "Client %d readable", client->fd); - int read_available; if (ioctl(client_fd, FIONREAD, &read_available) == -1) { sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); @@ -523,7 +519,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { } if (mask & WL_EVENT_HANGUP) { - sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } @@ -532,8 +527,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { return 0; } - sway_log(SWAY_DEBUG, "Client %d writable", client->fd); - ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); if (written == -1 && errno == EAGAIN) { @@ -692,6 +685,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt ipc_json_describe_disabled_output(output)); } } + + for (int i = 0; i < root->non_desktop_outputs->length; i++) { + struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i]; + json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output)); + } + const char *json_string = json_object_to_json_string(outputs); ipc_send_reply(client, payload_type, json_string, (uint32_t)strlen(json_string)); @@ -955,7 +954,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ ipc_client_handle_writable, client); } - sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", - payload_type, client->fd, payload); return true; } diff --git a/sway/lock.c b/sway/lock.c new file mode 100644 index 00000000..bf7d5de8 --- /dev/null +++ b/sway/lock.c @@ -0,0 +1,225 @@ +#define _POSIX_C_SOURCE 200809L +#include <assert.h> +#include "log.h" +#include "sway/input/keyboard.h" +#include "sway/input/seat.h" +#include "sway/output.h" +#include "sway/server.h" + +struct sway_session_lock_surface { + struct wlr_session_lock_surface_v1 *lock_surface; + struct sway_output *output; + struct wlr_surface *surface; + struct wl_listener map; + struct wl_listener destroy; + struct wl_listener surface_commit; + struct wl_listener output_mode; + struct wl_listener output_commit; + struct wl_listener output_destroy; +}; + +static void set_lock_focused_surface(struct wlr_surface *focused) { + server.session_lock.focused = focused; + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat_set_focus_surface(seat, focused, false); + } +} + +static void handle_surface_map(struct wl_listener *listener, void *data) { + struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); + if (server.session_lock.focused == NULL) { + set_lock_focused_surface(surf->surface); + } + output_damage_whole(surf->output); +} + +static void handle_surface_commit(struct wl_listener *listener, void *data) { + struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); + output_damage_surface(surf->output, 0, 0, surf->surface, false); +} + +static void handle_output_mode(struct wl_listener *listener, void *data) { + struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); + wlr_session_lock_surface_v1_configure(surf->lock_surface, + surf->output->width, surf->output->height); +} + +static void handle_output_commit(struct wl_listener *listener, void *data) { + struct wlr_output_event_commit *event = data; + struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); + if (event->committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_TRANSFORM)) { + wlr_session_lock_surface_v1_configure(surf->lock_surface, + surf->output->width, surf->output->height); + } +} + +static void destroy_lock_surface(struct sway_session_lock_surface *surf) { + // Move the seat focus to another surface if one is available + if (server.session_lock.focused == surf->surface) { + struct wlr_surface *next_focus = NULL; + + struct wlr_session_lock_surface_v1 *other; + wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { + if (other != surf->lock_surface && other->mapped) { + next_focus = other->surface; + break; + } + } + set_lock_focused_surface(next_focus); + } + + wl_list_remove(&surf->map.link); + wl_list_remove(&surf->destroy.link); + wl_list_remove(&surf->surface_commit.link); + wl_list_remove(&surf->output_mode.link); + wl_list_remove(&surf->output_commit.link); + wl_list_remove(&surf->output_destroy.link); + output_damage_whole(surf->output); + free(surf); +} + +static void handle_surface_destroy(struct wl_listener *listener, void *data) { + struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); + destroy_lock_surface(surf); +} + +static void handle_output_destroy(struct wl_listener *listener, void *data) { + struct sway_session_lock_surface *surf = + wl_container_of(listener, surf, output_destroy); + destroy_lock_surface(surf); +} + +static void handle_new_surface(struct wl_listener *listener, void *data) { + struct wlr_session_lock_surface_v1 *lock_surface = data; + struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); + if (surf == NULL) { + return; + } + + sway_log(SWAY_DEBUG, "new lock layer surface"); + + struct sway_output *output = lock_surface->output->data; + wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); + + surf->lock_surface = lock_surface; + surf->surface = lock_surface->surface; + surf->output = output; + surf->map.notify = handle_surface_map; + wl_signal_add(&lock_surface->events.map, &surf->map); + surf->destroy.notify = handle_surface_destroy; + wl_signal_add(&lock_surface->events.destroy, &surf->destroy); + surf->surface_commit.notify = handle_surface_commit; + wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); + surf->output_mode.notify = handle_output_mode; + wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode); + surf->output_commit.notify = handle_output_commit; + wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); + surf->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->node.events.destroy, &surf->output_destroy); +} + +static void handle_unlock(struct wl_listener *listener, void *data) { + sway_log(SWAY_DEBUG, "session unlocked"); + server.session_lock.locked = false; + server.session_lock.lock = NULL; + server.session_lock.focused = NULL; + + wl_list_remove(&server.session_lock.lock_new_surface.link); + wl_list_remove(&server.session_lock.lock_unlock.link); + wl_list_remove(&server.session_lock.lock_destroy.link); + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat_set_exclusive_client(seat, NULL); + // copied from seat_set_focus_layer -- deduplicate? + struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); + if (previous) { + // Hack to get seat to re-focus the return value of get_focus + seat_set_focus(seat, NULL); + seat_set_focus(seat, previous); + } + } + + // redraw everything + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); + } +} + +static void handle_abandon(struct wl_listener *listener, void *data) { + sway_log(SWAY_INFO, "session lock abandoned"); + server.session_lock.lock = NULL; + server.session_lock.focused = NULL; + + wl_list_remove(&server.session_lock.lock_new_surface.link); + wl_list_remove(&server.session_lock.lock_unlock.link); + wl_list_remove(&server.session_lock.lock_destroy.link); + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat->exclusive_client = NULL; + } + + // redraw everything + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); + } +} + +static void handle_session_lock(struct wl_listener *listener, void *data) { + struct wlr_session_lock_v1 *lock = data; + struct wl_client *client = wl_resource_get_client(lock->resource); + + if (server.session_lock.lock) { + wlr_session_lock_v1_destroy(lock); + return; + } + + sway_log(SWAY_DEBUG, "session locked"); + server.session_lock.locked = true; + server.session_lock.lock = lock; + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat_set_exclusive_client(seat, client); + } + + wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); + wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); + wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); + + wlr_session_lock_v1_send_locked(lock); + + // redraw everything + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); + } +} + +static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { + assert(server.session_lock.lock == NULL); + wl_list_remove(&server.session_lock.new_lock.link); + wl_list_remove(&server.session_lock.manager_destroy.link); +} + +void sway_session_lock_init(void) { + server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); + + server.session_lock.lock_new_surface.notify = handle_new_surface; + server.session_lock.lock_unlock.notify = handle_unlock; + server.session_lock.lock_destroy.notify = handle_abandon; + server.session_lock.new_lock.notify = handle_session_lock; + server.session_lock.manager_destroy.notify = handle_session_lock_destroy; + wl_signal_add(&server.session_lock.manager->events.new_lock, + &server.session_lock.new_lock); + wl_signal_add(&server.session_lock.manager->events.destroy, + &server.session_lock.manager_destroy); +} diff --git a/sway/main.c b/sway/main.c index b6f8a8bf..85bc2f1c 100644 --- a/sway/main.c +++ b/sway/main.c @@ -150,27 +150,17 @@ static void log_kernel(void) { pclose(f); } - -static bool drop_permissions(void) { - if (getuid() != geteuid() || getgid() != getegid()) { - sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: " - "SUID privilege drop will be removed in a future release, please migrate to seatd-launch"); - - // Set the gid and uid in the correct order. - if (setgid(getgid()) != 0) { - sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); - return false; - } - if (setuid(getuid()) != 0) { - sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start"); - return false; - } +static bool detect_suid(void) { + if (geteuid() != 0 && getegid() != 0) { + return false; } - if (setgid(0) != -1 || setuid(0) != -1) { - sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " - "restore it after setuid), refusing to start"); + + if (getuid() == geteuid() && getgid() == getegid()) { return false; } + + sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. " + "This check will be removed in a future release."); return true; } @@ -240,35 +230,35 @@ static void handle_wlr_log(enum wlr_log_importance importance, _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); } +static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"config", required_argument, NULL, 'c'}, + {"validate", no_argument, NULL, 'C'}, + {"debug", no_argument, NULL, 'd'}, + {"version", no_argument, NULL, 'v'}, + {"verbose", no_argument, NULL, 'V'}, + {"get-socketpath", no_argument, NULL, 'p'}, + {"unsupported-gpu", no_argument, NULL, 'u'}, + {0, 0, 0, 0} +}; + +static const char usage[] = + "Usage: sway [options] [command]\n" + "\n" + " -h, --help Show help message and quit.\n" + " -c, --config <config> Specify a config file.\n" + " -C, --validate Check the validity of the config file, then exit.\n" + " -d, --debug Enables full logging, including debug information.\n" + " -v, --version Show the version number and quit.\n" + " -V, --verbose Enables more verbose logging.\n" + " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" + "\n"; + int main(int argc, char **argv) { - static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; - - static const struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"config", required_argument, NULL, 'c'}, - {"validate", no_argument, NULL, 'C'}, - {"debug", no_argument, NULL, 'd'}, - {"version", no_argument, NULL, 'v'}, - {"verbose", no_argument, NULL, 'V'}, - {"get-socketpath", no_argument, NULL, 'p'}, - {"unsupported-gpu", no_argument, NULL, 'u'}, - {0, 0, 0, 0} - }; + static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; char *config_path = NULL; - const char* usage = - "Usage: sway [options] [command]\n" - "\n" - " -h, --help Show help message and quit.\n" - " -c, --config <config> Specify a config file.\n" - " -C, --validate Check the validity of the config file, then exit.\n" - " -d, --debug Enables full logging, including debug information.\n" - " -v, --version Show the version number and quit.\n" - " -V, --verbose Enables more verbose logging.\n" - " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" - "\n"; - int c; while (1) { int option_index = 0; @@ -286,25 +276,25 @@ int main(int argc, char **argv) { config_path = strdup(optarg); break; case 'C': // validate - validate = 1; + validate = true; break; case 'd': // debug - debug = 1; + debug = true; break; case 'D': // extended debug options enable_debug_flag(optarg); break; case 'u': - allow_unsupported_gpu = 1; + allow_unsupported_gpu = true; break; case 'v': // version printf("sway version " SWAY_VERSION "\n"); exit(EXIT_SUCCESS); break; case 'V': // verbose - verbose = 1; + verbose = true; break; - case 'p': ; // --get-socketpath + case 'p': // --get-socketpath if (getenv("SWAYSOCK")) { printf("%s\n", getenv("SWAYSOCK")); exit(EXIT_SUCCESS); @@ -319,6 +309,11 @@ int main(int argc, char **argv) { } } + // SUID operation is deprecated, so block it for now. + if (detect_suid()) { + exit(EXIT_FAILURE); + } + // 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) { @@ -357,9 +352,6 @@ int main(int argc, char **argv) { "`sway -d 2>sway.log`."); exit(EXIT_FAILURE); } - if (!drop_permissions()) { - exit(EXIT_FAILURE); - } char *socket_path = getenv("SWAYSOCK"); if (!socket_path) { sway_log(SWAY_ERROR, "Unable to retrieve socket path"); @@ -372,16 +364,6 @@ int main(int argc, char **argv) { } detect_proprietary(allow_unsupported_gpu); - - if (!server_privileged_prepare(&server)) { - return 1; - } - - if (!drop_permissions()) { - server_fini(&server); - exit(EXIT_FAILURE); - } - increase_nofile_limit(); // handle SIGTERM signals @@ -413,6 +395,8 @@ int main(int argc, char **argv) { goto shutdown; } + set_rr_scheduling(); + if (!server_start(&server)) { sway_terminate(EXIT_FAILURE); goto shutdown; diff --git a/sway/meson.build b/sway/meson.build index ac3d408d..12362fcd 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -1,245 +1,252 @@ sway_sources = files( - 'commands.c', - 'config.c', - 'criteria.c', - 'decoration.c', - 'ipc-json.c', - 'ipc-server.c', - 'main.c', - 'server.c', - 'swaynag.c', - 'xdg_activation_v1.c', - 'xdg_decoration.c', + 'commands.c', + 'config.c', + 'criteria.c', + 'decoration.c', + 'ipc-json.c', + 'ipc-server.c', + 'lock.c', + 'main.c', + 'realtime.c', + 'server.c', + 'swaynag.c', + 'xdg_activation_v1.c', + 'xdg_decoration.c', - 'desktop/desktop.c', - 'desktop/fx_renderer.c', - 'desktop/idle_inhibit_v1.c', - 'desktop/layer_shell.c', - 'desktop/output.c', - 'desktop/render.c', - 'desktop/surface.c', - 'desktop/transaction.c', - 'desktop/xdg_shell.c', + 'desktop/desktop.c', + 'desktop/fx_renderer.c', + 'desktop/idle_inhibit_v1.c', + 'desktop/layer_shell.c', + 'desktop/output.c', + 'desktop/render.c', + 'desktop/surface.c', + 'desktop/transaction.c', + 'desktop/xdg_shell.c', + 'desktop/launcher.c', + 'input/input-manager.c', + 'input/cursor.c', + 'input/keyboard.c', + 'input/libinput.c', + 'input/seat.c', + 'input/seatop_default.c', + 'input/seatop_down.c', + 'input/seatop_move_floating.c', + 'input/seatop_move_tiling.c', + 'input/seatop_resize_floating.c', + 'input/seatop_resize_tiling.c', + 'input/switch.c', + 'input/tablet.c', + 'input/text_input.c', - 'input/input-manager.c', - 'input/cursor.c', - 'input/keyboard.c', - 'input/libinput.c', - 'input/seat.c', - 'input/seatop_default.c', - 'input/seatop_down.c', - 'input/seatop_move_floating.c', - 'input/seatop_move_tiling.c', - 'input/seatop_resize_floating.c', - 'input/seatop_resize_tiling.c', - 'input/switch.c', - 'input/tablet.c', - 'input/text_input.c', + 'config/bar.c', + 'config/output.c', + 'config/seat.c', + 'config/input.c', - 'config/bar.c', - 'config/output.c', - 'config/seat.c', - 'config/input.c', + 'commands/assign.c', + 'commands/bar.c', + 'commands/bind.c', + 'commands/border.c', + 'commands/client.c', + 'commands/corner_radius.c', + 'commands/create_output.c', + 'commands/default_border.c', + 'commands/default_floating_border.c', + 'commands/default_orientation.c', + 'commands/dim_inactive.c', + 'commands/dim_inactive_colors.c', + 'commands/exit.c', + 'commands/exec.c', + 'commands/exec_always.c', + 'commands/floating.c', + 'commands/floating_minmax_size.c', + 'commands/floating_modifier.c', + 'commands/focus.c', + 'commands/focus_follows_mouse.c', + 'commands/focus_on_window_activation.c', + 'commands/focus_wrapping.c', + 'commands/font.c', + 'commands/for_window.c', + 'commands/force_display_urgency_hint.c', + 'commands/force_focus_wrapping.c', + 'commands/fullscreen.c', + 'commands/gaps.c', + 'commands/gesture.c', + 'commands/hide_edge_borders.c', + 'commands/inhibit_idle.c', + 'commands/kill.c', + 'commands/mark.c', + 'commands/max_render_time.c', + 'commands/opacity.c', + 'commands/include.c', + 'commands/input.c', + 'commands/layout.c', + 'commands/mode.c', + 'commands/mouse_warping.c', + 'commands/move.c', + 'commands/new_float.c', + 'commands/new_window.c', + 'commands/no_focus.c', + 'commands/nop.c', + 'commands/output.c', + 'commands/popup_during_fullscreen.c', + 'commands/reload.c', + 'commands/rename.c', + 'commands/resize.c', + 'commands/saturation.c', + 'commands/scratchpad.c', + 'commands/seat.c', + 'commands/seat/attach.c', + 'commands/seat/cursor.c', + 'commands/seat/fallback.c', + 'commands/seat/hide_cursor.c', + 'commands/seat/idle.c', + 'commands/seat/keyboard_grouping.c', + 'commands/seat/pointer_constraint.c', + 'commands/seat/shortcuts_inhibitor.c', + 'commands/seat/xcursor_theme.c', + 'commands/set.c', + 'commands/show_marks.c', + 'commands/shortcuts_inhibitor.c', + 'commands/smart_borders.c', + 'commands/smart_gaps.c', + 'commands/split.c', + 'commands/sticky.c', + 'commands/swaybg_command.c', + 'commands/swaynag_command.c', + 'commands/swap.c', + 'commands/tiling_drag.c', + 'commands/tiling_drag_threshold.c', + 'commands/title_align.c', + 'commands/title_format.c', + 'commands/titlebar_border_thickness.c', + 'commands/titlebar_padding.c', + 'commands/unmark.c', + 'commands/urgent.c', + 'commands/workspace.c', + 'commands/workspace_layout.c', + 'commands/ws_auto_back_and_forth.c', + 'commands/xwayland.c', - 'commands/assign.c', - 'commands/bar.c', - 'commands/bind.c', - 'commands/border.c', - 'commands/client.c', - 'commands/corner_radius.c', - 'commands/create_output.c', - 'commands/default_border.c', - 'commands/default_floating_border.c', - 'commands/default_orientation.c', - 'commands/dim_inactive.c', - 'commands/dim_inactive_colors.c', - 'commands/exit.c', - 'commands/exec.c', - 'commands/exec_always.c', - 'commands/floating.c', - 'commands/floating_minmax_size.c', - 'commands/floating_modifier.c', - 'commands/focus.c', - 'commands/focus_follows_mouse.c', - 'commands/focus_on_window_activation.c', - 'commands/focus_wrapping.c', - 'commands/font.c', - 'commands/for_window.c', - 'commands/force_display_urgency_hint.c', - 'commands/force_focus_wrapping.c', - 'commands/fullscreen.c', - 'commands/gaps.c', - 'commands/hide_edge_borders.c', - 'commands/inhibit_idle.c', - 'commands/kill.c', - 'commands/mark.c', - 'commands/max_render_time.c', - 'commands/opacity.c', - 'commands/saturation.c', - 'commands/include.c', - 'commands/input.c', - 'commands/layout.c', - 'commands/mode.c', - 'commands/mouse_warping.c', - 'commands/move.c', - 'commands/new_float.c', - 'commands/new_window.c', - 'commands/no_focus.c', - 'commands/nop.c', - 'commands/output.c', - 'commands/popup_during_fullscreen.c', - 'commands/reload.c', - 'commands/rename.c', - 'commands/resize.c', - 'commands/scratchpad.c', - 'commands/seat.c', - 'commands/seat/attach.c', - 'commands/seat/cursor.c', - 'commands/seat/fallback.c', - 'commands/seat/hide_cursor.c', - 'commands/seat/idle.c', - 'commands/seat/keyboard_grouping.c', - 'commands/seat/pointer_constraint.c', - 'commands/seat/shortcuts_inhibitor.c', - 'commands/seat/xcursor_theme.c', - 'commands/set.c', - 'commands/show_marks.c', - 'commands/shortcuts_inhibitor.c', - 'commands/smart_borders.c', - 'commands/smart_gaps.c', - 'commands/split.c', - 'commands/sticky.c', - 'commands/swaybg_command.c', - 'commands/swaynag_command.c', - 'commands/swap.c', - 'commands/tiling_drag.c', - 'commands/tiling_drag_threshold.c', - 'commands/title_align.c', - 'commands/title_format.c', - 'commands/titlebar_border_thickness.c', - 'commands/titlebar_padding.c', - 'commands/unmark.c', - 'commands/urgent.c', - 'commands/workspace.c', - 'commands/workspace_layout.c', - 'commands/ws_auto_back_and_forth.c', - 'commands/xwayland.c', + 'commands/bar/bind.c', + 'commands/bar/binding_mode_indicator.c', + 'commands/bar/colors.c', + 'commands/bar/font.c', + 'commands/bar/gaps.c', + 'commands/bar/height.c', + 'commands/bar/hidden_state.c', + 'commands/bar/icon_theme.c', + 'commands/bar/id.c', + 'commands/bar/mode.c', + 'commands/bar/modifier.c', + 'commands/bar/output.c', + 'commands/bar/pango_markup.c', + 'commands/bar/position.c', + 'commands/bar/separator_symbol.c', + 'commands/bar/status_command.c', + 'commands/bar/status_edge_padding.c', + 'commands/bar/status_padding.c', + 'commands/bar/strip_workspace_numbers.c', + 'commands/bar/strip_workspace_name.c', + 'commands/bar/swaybar_command.c', + 'commands/bar/tray_bind.c', + 'commands/bar/tray_output.c', + 'commands/bar/tray_padding.c', + 'commands/bar/workspace_buttons.c', + 'commands/bar/workspace_min_width.c', + 'commands/bar/wrap_scroll.c', - 'commands/bar/bind.c', - 'commands/bar/binding_mode_indicator.c', - 'commands/bar/colors.c', - 'commands/bar/font.c', - 'commands/bar/gaps.c', - 'commands/bar/height.c', - 'commands/bar/hidden_state.c', - 'commands/bar/icon_theme.c', - 'commands/bar/id.c', - 'commands/bar/mode.c', - 'commands/bar/modifier.c', - 'commands/bar/output.c', - 'commands/bar/pango_markup.c', - 'commands/bar/position.c', - 'commands/bar/separator_symbol.c', - 'commands/bar/status_command.c', - 'commands/bar/status_edge_padding.c', - 'commands/bar/status_padding.c', - 'commands/bar/strip_workspace_numbers.c', - 'commands/bar/strip_workspace_name.c', - 'commands/bar/swaybar_command.c', - 'commands/bar/tray_bind.c', - 'commands/bar/tray_output.c', - 'commands/bar/tray_padding.c', - 'commands/bar/workspace_buttons.c', - 'commands/bar/workspace_min_width.c', - 'commands/bar/wrap_scroll.c', + 'commands/input/accel_profile.c', + 'commands/input/calibration_matrix.c', + 'commands/input/click_method.c', + 'commands/input/drag.c', + 'commands/input/drag_lock.c', + 'commands/input/dwt.c', + 'commands/input/dwtp.c', + 'commands/input/events.c', + 'commands/input/left_handed.c', + 'commands/input/map_from_region.c', + 'commands/input/map_to_output.c', + 'commands/input/map_to_region.c', + 'commands/input/middle_emulation.c', + 'commands/input/natural_scroll.c', + 'commands/input/pointer_accel.c', + 'commands/input/repeat_delay.c', + 'commands/input/repeat_rate.c', + 'commands/input/scroll_button.c', + 'commands/input/scroll_factor.c', + 'commands/input/scroll_method.c', + 'commands/input/tap.c', + 'commands/input/tap_button_map.c', + 'commands/input/tool_mode.c', + 'commands/input/xkb_capslock.c', + 'commands/input/xkb_file.c', + 'commands/input/xkb_layout.c', + 'commands/input/xkb_model.c', + 'commands/input/xkb_numlock.c', + 'commands/input/xkb_options.c', + 'commands/input/xkb_rules.c', + 'commands/input/xkb_switch_layout.c', + 'commands/input/xkb_variant.c', - 'commands/input/accel_profile.c', - 'commands/input/calibration_matrix.c', - 'commands/input/click_method.c', - 'commands/input/drag.c', - 'commands/input/drag_lock.c', - 'commands/input/dwt.c', - 'commands/input/events.c', - 'commands/input/left_handed.c', - 'commands/input/map_from_region.c', - 'commands/input/map_to_output.c', - 'commands/input/map_to_region.c', - 'commands/input/middle_emulation.c', - 'commands/input/natural_scroll.c', - 'commands/input/pointer_accel.c', - 'commands/input/repeat_delay.c', - 'commands/input/repeat_rate.c', - 'commands/input/scroll_button.c', - 'commands/input/scroll_factor.c', - 'commands/input/scroll_method.c', - 'commands/input/tap.c', - 'commands/input/tap_button_map.c', - 'commands/input/tool_mode.c', - 'commands/input/xkb_capslock.c', - 'commands/input/xkb_file.c', - 'commands/input/xkb_layout.c', - 'commands/input/xkb_model.c', - 'commands/input/xkb_numlock.c', - 'commands/input/xkb_options.c', - 'commands/input/xkb_rules.c', - 'commands/input/xkb_switch_layout.c', - 'commands/input/xkb_variant.c', + 'commands/output/adaptive_sync.c', + 'commands/output/background.c', + 'commands/output/disable.c', + 'commands/output/dpms.c', + 'commands/output/enable.c', + 'commands/output/max_render_time.c', + 'commands/output/mode.c', + 'commands/output/position.c', + 'commands/output/power.c', + 'commands/output/render_bit_depth.c', + 'commands/output/scale.c', + 'commands/output/scale_filter.c', + 'commands/output/subpixel.c', + 'commands/output/toggle.c', + 'commands/output/transform.c', + 'commands/output/unplug.c', - 'commands/output/adaptive_sync.c', - 'commands/output/background.c', - 'commands/output/disable.c', - 'commands/output/dpms.c', - 'commands/output/enable.c', - 'commands/output/max_render_time.c', - 'commands/output/mode.c', - 'commands/output/position.c', - 'commands/output/render_bit_depth.c', - 'commands/output/scale.c', - 'commands/output/scale_filter.c', - 'commands/output/subpixel.c', - 'commands/output/toggle.c', - 'commands/output/transform.c', - - 'tree/arrange.c', - 'tree/container.c', - 'tree/node.c', - 'tree/root.c', - 'tree/view.c', - 'tree/workspace.c', - 'tree/output.c', + 'tree/arrange.c', + 'tree/container.c', + 'tree/node.c', + 'tree/root.c', + 'tree/view.c', + 'tree/workspace.c', + 'tree/output.c', ) subdir('desktop/shaders') sway_deps = [ - cairo, - drm, - jsonc, - libevdev, - libinput, - libudev, - math, - pango, - pcre, - glesv2, - pixman, - server_protos, - wayland_server, - wlroots, - xkbcommon, - egl, + cairo, + drm, + jsonc, + libevdev, + libinput, + libudev, + math, + pango, + pcre2, + glesv2, + pixman, + threads, + wayland_server, + wlroots, + xkbcommon, + xcb_icccm, + egl, ] if have_xwayland - sway_sources += 'desktop/xwayland.c' - sway_deps += xcb + sway_sources += 'desktop/xwayland.c' + sway_deps += xcb endif executable( - 'sway', - sway_sources, - include_directories: [sway_inc], - dependencies: sway_deps, - link_with: [lib_sway_common], - install: true + 'sway', + sway_sources + wl_protos_src, + include_directories: [sway_inc], + dependencies: sway_deps, + link_with: [lib_sway_common], + install: true ) diff --git a/sway/realtime.c b/sway/realtime.c new file mode 100644 index 00000000..11154af0 --- /dev/null +++ b/sway/realtime.c @@ -0,0 +1,40 @@ +#include <sys/resource.h> +#include <sched.h> +#include <unistd.h> +#include <pthread.h> +#include "sway/server.h" +#include "log.h" + +static void child_fork_callback(void) { + struct sched_param param; + + param.sched_priority = 0; + + int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); + if (ret != 0) { + sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork"); + } +} + +void set_rr_scheduling(void) { + int prio = sched_get_priority_min(SCHED_RR); + int old_policy; + int ret; + struct sched_param param; + + ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); + if (ret != 0) { + sway_log(SWAY_DEBUG, "Failed to get old scheduling priority"); + return; + } + + param.sched_priority = prio; + + ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); + if (ret != 0) { + sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio); + return; + } + + pthread_atfork(NULL, NULL, child_fork_callback); +} diff --git a/sway/server.c b/sway/server.c index 3fab3431..90b2e042 100644 --- a/sway/server.c +++ b/sway/server.c @@ -18,13 +18,16 @@ #include <wlr/types/wlr_export_dmabuf_v1.h> #include <wlr/types/wlr_gamma_control_v1.h> #include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_idle_notify_v1.h> #include <wlr/types/wlr_layer_shell_v1.h> #include <wlr/types/wlr_linux_dmabuf_v1.h> #include <wlr/types/wlr_pointer_constraints_v1.h> #include <wlr/types/wlr_primary_selection_v1.h> #include <wlr/types/wlr_relative_pointer_v1.h> #include <wlr/types/wlr_screencopy_v1.h> +#include <wlr/types/wlr_single_pixel_buffer_v1.h> #include <wlr/types/wlr_server_decoration.h> +#include <wlr/types/wlr_subcompositor.h> #include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_viewporter.h> #include <wlr/types/wlr_xcursor_manager.h> @@ -48,19 +51,6 @@ #include "sway/xwayland.h" #endif -bool server_privileged_prepare(struct sway_server *server) { - sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); - server->wl_display = wl_display_create(); - server->wl_event_loop = wl_display_get_event_loop(server->wl_display); - server->backend = wlr_backend_autocreate(server->wl_display); - - if (!server->backend) { - sway_log(SWAY_ERROR, "Unable to create backend"); - return false; - } - return true; -} - static void handle_drm_lease_request(struct wl_listener *listener, void *data) { /* We only offer non-desktop outputs, but in the future we might want to do * more logic here. */ @@ -73,8 +63,18 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) { } } +#define SWAY_XDG_SHELL_VERSION 2 + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); + server->wl_display = wl_display_create(); + server->wl_event_loop = wl_display_get_event_loop(server->wl_display); + server->backend = wlr_backend_autocreate(server->wl_display); + + if (!server->backend) { + sway_log(SWAY_ERROR, "Unable to create backend"); + return false; + } server->wlr_renderer = wlr_renderer_autocreate(server->backend); if (!server->wlr_renderer) { @@ -109,6 +109,8 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->compositor->events.new_surface, &server->compositor_new_surface); + wlr_subcompositor_create(server->wl_display); + server->data_device_manager = wlr_data_device_manager_create(server->wl_display); @@ -123,6 +125,7 @@ bool server_init(struct sway_server *server) { wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); server->idle = wlr_idle_create(server->wl_display); + server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); server->idle_inhibit_manager_v1 = sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); @@ -131,7 +134,8 @@ bool server_init(struct sway_server *server) { &server->layer_shell_surface); server->layer_shell_surface.notify = handle_layer_shell_surface; - server->xdg_shell = wlr_xdg_shell_create(server->wl_display); + server->xdg_shell = wlr_xdg_shell_create(server->wl_display, + SWAY_XDG_SHELL_VERSION); wl_signal_add(&server->xdg_shell->events.new_surface, &server->xdg_shell_surface); server->xdg_shell_surface.notify = handle_xdg_shell_surface; @@ -188,6 +192,8 @@ bool server_init(struct sway_server *server) { server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); + sway_session_lock_init(); + server->drm_lease_manager= wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); if (server->drm_lease_manager) { @@ -204,6 +210,7 @@ bool server_init(struct sway_server *server) { wlr_data_control_manager_v1_create(server->wl_display); wlr_primary_selection_v1_device_manager_create(server->wl_display); wlr_viewporter_create(server->wl_display); + wlr_single_pixel_buffer_manager_v1_create(server->wl_display); struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); @@ -216,10 +223,12 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->xdg_activation_v1->events.request_activate, &server->xdg_activation_v1_request_activate); + wl_list_init(&server->pending_launcher_ctxs); + // Avoid using "wayland-0" as display socket char name_candidate[16]; - for (int i = 1; i <= 32; ++i) { - sprintf(name_candidate, "wayland-%d", i); + for (unsigned int i = 1; i <= 32; ++i) { + snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { server->socket = strdup(name_candidate); break; diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 8b702b77..e073c45d 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -147,6 +147,10 @@ The following commands may only be used in the configuration file. *input* <identifier> dwt enabled|disabled Enables or disables disable-while-typing for the specified input device. +*input* <identifier> dwtp enabled|disabled + Enables or disables disable-while-trackpointing for the specified input + device. + *input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] Enables or disables send_events for specified input device. Disabling send_events disables the input device. diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index d90fe97a..9121f679 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -213,7 +213,10 @@ following properties: : Whether this output is active/enabled |- dpms : boolean -: Whether this output is on/off (via DPMS) +: (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS) +|- power +: boolean +: Whether this output is on/off |- primary : boolean : For i3 compatibility, this will be false. It does not make sense in Wayland @@ -370,7 +373,7 @@ node and will have the following properties: that can be used as an aid in submitting reproduction steps for bug reports |- fullscreen_mode : integer -: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means +: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means full workspace, and 2 means global fullscreen |- app_id : string @@ -1194,6 +1197,10 @@ following properties will be included for devices that support them: |- dwt : string : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ +|- dwtp +: string +: Whether disable-while-trackpointing is enabled. It can be _enabled_ or + _disabled_ |- calibration_matrix : array : An array of 6 floats representing the calibration matrix for absolute @@ -1233,7 +1240,8 @@ following properties will be included for devices that support them: "click_method": "button_areas", "middle_emulation": "disabled", "scroll_method": "edge", - "dwt": "enabled" + "dwt": "enabled", + "dwtp": "enabled" } }, { @@ -1360,7 +1368,8 @@ one seat. Each object has the following properties: "click_method": "button_areas", "middle_emulation": "disabled", "scroll_method": "edge", - "dwt": "enabled" + "dwt": "enabled", + "dwtp": "enabled" } }, { diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 4159a851..b7d5e577 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -24,7 +24,7 @@ must be separated by one space. For example: # COMMANDS -*output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] +*output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz] Configures the specified output to use the given mode. Modes are a combination of width and height (in pixels) and a refresh rate that your display can be configured to use. For a list of available modes for each @@ -119,12 +119,20 @@ must be separated by one space. For example: Enables or disables the specified output (all outputs are enabled by default). + As opposed to the _power_ command, the output will loose its current + workspace and windows. + *output* <name> toggle Toggle the specified output. +*output* <name> power on|off|toggle + Turns on or off the specified output. + + As opposed to the _enable_ and _disable_ commands, the output keeps its + current workspaces and windows. + *output* <name> dpms on|off|toggle - Enables or disables the specified output via DPMS. To turn an output off - (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. + Deprecated. Alias for _power_. *output* <name> max_render_time off|<msec> Controls when sway composites the output, as a positive number of diff --git a/sway/sway.5.scd b/sway/sway.5.scd index bf936a27..15e60844 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -176,6 +176,12 @@ set|plus|minus|toggle <amount> *layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. + When using the _stacking_ layout, only the focused window in the container is + displayed, with the opened windows' list on the top of the container. + + The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically + split. + *layout* toggle [split|all] Cycles the layout mode of the focused container though a preset list of layouts. If no argument is given, then it cycles through stacking, tabbed @@ -210,15 +216,14 @@ set|plus|minus|toggle <amount> further details. *move* left|right|up|down [<px> px] - Moves the focused container in the direction specified. If the container, - the optional _px_ argument specifies how many pixels to move the container. - If unspecified, the default is 10 pixels. Pixels are ignored when moving - tiled containers. + Moves the focused container in the direction specified. The optional _px_ + argument specifies how many pixels to move the container. If unspecified, + the default is 10 pixels. Pixels are ignored when moving tiled containers. *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt] Moves the focused container to the specified position in the workspace. The position can be specified in pixels or percentage points, omitting - the unit defaults to pixels. If _absolute_ is used, the position is + the unit defaults to pixels. If _absolute_ is used, the position is relative to all outputs. _absolute_ can not be used with percentage points. *move* [absolute] position center @@ -482,6 +487,62 @@ runtime. bindswitch lid:toggle exec echo "Lid moved" ``` +*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \ +<gesture>[:<fingers>][:directions] <command> + Binds _gesture_ to execute the sway command _command_ when detected. + Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally + can be limited to bind to a certain number of _fingers_ or, for a + _pinch_ or _swipe_ gesture, to certain _directions_. + +[[ *type* +:[ *fingers* +:< *direction* +| hold +:- 1 - 5 +: none +| swipe +: 3 - 5 +: up, down, left, right +| pinch +: 2 - 5 +: all above + inward, outward, clockwise, counterclockwise + + The _fingers_ can be limited to any sensible number or left empty to accept + any finger counts. + Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_, + _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture. + Multiple directions can be combined by a plus. + + If a _input-device_ is given, the binding will only be executed for + that input device and will be executed instead of any binding that is + generic to all devices. By default, if you overwrite a binding, + swaynag will give you a warning. To silence this, use the _--no-warn_ flag. + + The _--exact_ flag can be used to ensure a binding only matches when exactly + all specified directions are matched and nothing more. If there is matching + binding with _--exact_, it will be preferred. + + The priority for matching bindings is as follows: input device, then + exact matches followed by matches with the highest number of matching + directions. + + Gestures executed while the pointer is above a bar are not handled by sway. + See the respective documentation, e.g. *bindgesture* in *sway-bar*(5). + + Example: +``` + # Allow switching between workspaces with left and right swipes + bindgesture swipe:right workspace prev + bindgesture swipe:left workspace next + + # Allow container movements by pinching them + bindgesture pinch:inward+up move up + bindgesture pinch:inward+down move down + bindgesture pinch:inward+left move left + bindgesture pinch:inward+right move right + +``` + *client.background* <color> This command is ignored and is only present for i3 compatibility. @@ -632,11 +693,11 @@ The default colors are: after switching between workspaces. *focus_on_window_activation* smart|urgent|focus|none - This option determines what to do when an xwayland client requests - window activation. If set to _urgent_, the urgent state will be set - for that window. If set to _focus_, the window will become focused. - If set to _smart_, the window will become focused only if it is already - visible, otherwise the urgent state will be set. Default is _urgent_. + This option determines what to do when a client requests window activation. + If set to _urgent_, the urgent state will be set for that window. If set to + _focus_, the window will become focused. If set to _smart_, the window will + become focused only if it is already visible, otherwise the urgent state + will be set. Default is _urgent_. *focus_wrapping* yes|no|force|workspace This option determines what to do when attempting to focus over the edge @@ -805,6 +866,11 @@ The default colors are: *unbindswitch* <switch>:<state> Removes a binding for when <switch> changes to <state>. +*unbindgesture* [--exact] [--input-device=<device>] \ +<gesture>[:<fingers>][:directions] + Removes a binding for the specified _gesture_, _fingers_ + and _directions_ combination. + *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ [--to-code] [--input-device=<device>] <key combo> Removes the binding for _key combo_ that was previously bound with the @@ -918,7 +984,8 @@ The following attributes may be matched with: *class* Compare value against the window class. Can be a regular expression. If value is \_\_focused\_\_, then the window class must be the same as that of - the currently focused window. _class_ are specific to X11 applications. + the currently focused window. _class_ are specific to X11 applications and + require XWayland. *con_id* Compare against the internal container ID, which you can find via IPC. If @@ -932,12 +999,14 @@ The following attributes may be matched with: Matches floating windows. *id* - Compare value against the X11 window ID. Must be numeric. + Compare value against the X11 window ID. Must be numeric. id is specific to + X11 applications and requires XWayland. *instance* Compare value against the window instance. Can be a regular expression. If value is \_\_focused\_\_, then the window instance must be the same as that - of the currently focused window. + of the currently focused window. instance is specific to X11 applications and + requires XWayland. *pid* Compare value against the window's process ID. Must be numeric. @@ -962,12 +1031,14 @@ The following attributes may be matched with: *window_role* Compare against the window role (WM_WINDOW_ROLE). Can be a regular expression. If value is \_\_focused\_\_, then the window role must be the - same as that of the currently focused window. + same as that of the currently focused window. window_role is specific to X11 + applications and requires XWayland. *window_type* Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, - popup_menu, tooltip and notification. + popup_menu, tooltip and notification. window_type is specific to X11 + applications and requires XWayland. *workspace* Compare against the workspace name for this view. Can be a regular diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 4aa82c35..9c1a11e5 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -311,12 +311,13 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } - const struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - output->lx = output_box->x; - output->ly = output_box->y; - output->width = output_box->width; - output->height = output_box->height; + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; @@ -328,12 +329,12 @@ void arrange_root(void) { if (config->reloading) { return; } - const struct wlr_box *layout_box = - wlr_output_layout_get_box(root->output_layout, NULL); - root->x = layout_box->x; - root->y = layout_box->y; - root->width = layout_box->width; - root->height = layout_box->height; + struct wlr_box layout_box; + wlr_output_layout_get_box(root->output_layout, NULL, &layout_box); + root->x = layout_box.x; + root->y = layout_box.y; + root->width = layout_box.width; + root->height = layout_box.height; if (root->fullscreen_global) { struct sway_container *fs = root->fullscreen_global; diff --git a/sway/tree/container.c b/sway/tree/container.c index 73bb865c..29a1e226 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -9,6 +9,7 @@ #include <wayland-server-core.h> #include <wlr/types/wlr_linux_dmabuf_v1.h> #include <wlr/types/wlr_output_layout.h> +#include <wlr/types/wlr_subcompositor.h> #include <wlr/render/drm_format_set.h> #include "linux-dmabuf-unstable-v1-protocol.h" #include "cairo_util.h" @@ -51,7 +52,7 @@ struct sway_container *container_create(struct sway_view *view) { c->outputs = create_list(); wl_signal_init(&c->events.destroy); - wl_signal_emit(&root->events.new_node, &c->node); + wl_signal_emit_mutable(&root->events.new_node, &c->node); return c; } @@ -106,7 +107,7 @@ void container_begin_destroy(struct sway_container *con) { container_fullscreen_disable(con); } - wl_signal_emit(&con->node.events.destroy, &con->node); + wl_signal_emit_mutable(&con->node.events.destroy, &con->node); container_end_mouse_operation(con); @@ -196,7 +197,7 @@ static struct sway_container *surface_at_view(struct sway_container *con, double #endif case SWAY_VIEW_XDG_SHELL: _surface = wlr_xdg_surface_surface_at( - view->wlr_xdg_surface, + view->wlr_xdg_toplevel->base, view_sx, view_sy, &_sx, &_sy); break; } @@ -522,7 +523,7 @@ static void render_titlebar_text_texture(struct sway_output *output, to_cairo_subpixel_order(output->wlr_output->subpixel)); } cairo_set_font_options(c, fo); - get_text_size(c, config->font, &width, NULL, &baseline, scale, + get_text_size(c, config->font_description, &width, NULL, &baseline, scale, config->pango_markup, "%s", text); cairo_surface_destroy(dummy_surface); cairo_destroy(c); @@ -556,7 +557,7 @@ static void render_titlebar_text_texture(struct sway_output *output, class->text[2], class->text[3]); cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - render_text(cairo, config->font, scale, pango_markup, "%s", text); + render_text(cairo, config->font_description, scale, pango_markup, "%s", text); cairo_surface_flush(surface); unsigned char *data = cairo_image_surface_get_data(surface); @@ -697,12 +698,13 @@ void floating_calculate_constraints(int *min_width, int *max_width, *min_height = config->floating_minimum_height; } - struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); + struct wlr_box box; + wlr_output_layout_get_box(root->output_layout, NULL, &box); if (config->floating_maximum_width == -1) { // no maximum *max_width = INT_MAX; } else if (config->floating_maximum_width == 0) { // automatic - *max_width = box->width; + *max_width = box.width; } else { *max_width = config->floating_maximum_width; } @@ -710,7 +712,7 @@ void floating_calculate_constraints(int *min_width, int *max_width, if (config->floating_maximum_height == -1) { // no maximum *max_height = INT_MAX; } else if (config->floating_maximum_height == 0) { // automatic - *max_height = box->height; + *max_height = box.height; } else { *max_height = config->floating_maximum_height; } @@ -742,9 +744,9 @@ void container_floating_resize_and_center(struct sway_container *con) { return; } - struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, - ws->output->wlr_output); - if (!ob) { + struct wlr_box ob; + wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob); + if (wlr_box_empty(&ob)) { // On NOOP output. Will be called again when moved to an output con->pending.x = 0; con->pending.y = 0; @@ -756,8 +758,8 @@ void container_floating_resize_and_center(struct sway_container *con) { floating_natural_resize(con); if (!con->view) { if (con->pending.width > ws->width || con->pending.height > ws->height) { - con->pending.x = ob->x + (ob->width - con->pending.width) / 2; - con->pending.y = ob->y + (ob->height - con->pending.height) / 2; + con->pending.x = ob.x + (ob.width - con->pending.width) / 2; + con->pending.y = ob.y + (ob.height - con->pending.height) / 2; } else { con->pending.x = ws->x + (ws->width - con->pending.width) / 2; con->pending.y = ws->y + (ws->height - con->pending.height) / 2; @@ -765,8 +767,8 @@ void container_floating_resize_and_center(struct sway_container *con) { } else { if (con->pending.content_width > ws->width || con->pending.content_height > ws->height) { - con->pending.content_x = ob->x + (ob->width - con->pending.content_width) / 2; - con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2; + con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2; + con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2; } else { con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; @@ -788,11 +790,11 @@ void container_floating_set_default_size(struct sway_container *con) { int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); - struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); - workspace_get_box(con->pending.workspace, box); + struct wlr_box box; + workspace_get_box(con->pending.workspace, &box); - double width = fmax(min_width, fmin(box->width * 0.5, max_width)); - double height = fmax(min_height, fmin(box->height * 0.75, max_height)); + double width = fmax(min_width, fmin(box.width * 0.5, max_width)); + double height = fmax(min_height, fmin(box.height * 0.75, max_height)); if (!con->view) { con->pending.width = width; con->pending.height = height; @@ -801,8 +803,6 @@ void container_floating_set_default_size(struct sway_container *con) { con->pending.content_height = height; container_set_geometry_from_content(con); } - - free(box); } @@ -1413,6 +1413,9 @@ list_t *container_get_siblings(struct sway_container *container) { if (container_is_scratchpad_hidden(container)) { return NULL; } + if (!container->pending.workspace) { + return NULL; + } if (list_find(container->pending.workspace->tiling, container) != -1) { return container->pending.workspace->tiling; } @@ -1707,7 +1710,7 @@ static void update_marks_texture(struct sway_container *con, for (int i = 0; i < con->marks->length; ++i) { char *mark = con->marks->items[i]; if (mark[0] != '_') { - sprintf(part, "[%s]", mark); + snprintf(part, len + 1, "[%s]", mark); strcat(buffer, part); } } diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa5..12361c75 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -18,13 +18,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) { const char *node_type_to_str(enum sway_node_type type) { switch (type) { case N_ROOT: - return "N_ROOT"; + return "root"; case N_OUTPUT: - return "N_OUTPUT"; + return "output"; case N_WORKSPACE: - return "N_WORKSPACE"; + return "workspace"; case N_CONTAINER: - return "N_CONTAINER"; + return "container"; } return ""; } diff --git a/sway/tree/output.c b/sway/tree/output.c index ad8d2482..eccab2f7 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -3,12 +3,12 @@ #include <ctype.h> #include <string.h> #include <strings.h> -#include <wlr/types/wlr_output_damage.h> #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" +#include "sway/server.h" #include "log.h" #include "util.h" @@ -146,7 +146,7 @@ void output_enable(struct sway_output *output) { input_manager_configure_xcursor(); - wl_signal_emit(&root->events.new_node, &output->node); + wl_signal_emit_mutable(&root->events.new_node, &output->node); arrange_layers(output); arrange_root(); @@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) { } sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); - wl_signal_emit(&output->events.disable, output); + wl_signal_emit_mutable(&output->events.disable, output); output_evacuate(output); @@ -286,7 +286,7 @@ void output_begin_destroy(struct sway_output *output) { return; } sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); - wl_signal_emit(&output->node.events.destroy, &output->node); + wl_signal_emit_mutable(&output->node.events.destroy, &output->node); output->node.destroying = true; node_set_dirty(&output->node); @@ -301,10 +301,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, if (!sway_assert(direction, "got invalid direction: %d", direction)) { return NULL; } - struct wlr_box *output_box = - wlr_output_layout_get_box(root->output_layout, reference->wlr_output); - int lx = output_box->x + output_box->width / 2; - int ly = output_box->y + output_box->height / 2; + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box); + int lx = output_box.x + output_box.width / 2; + int ly = output_box.y + output_box.height / 2; struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( root->output_layout, direction, reference->wlr_output, lx, ly); if (!wlr_adjacent) { @@ -390,6 +390,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { box->height = output->height; } +static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) { + struct sway_output_non_desktop *output = + wl_container_of(listener, output, destroy); + + sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name); + + int index = list_find(root->non_desktop_outputs, output); + list_del(root->non_desktop_outputs, index); + + wl_list_remove(&output->destroy.link); + + free(output); +} + +struct sway_output_non_desktop *output_non_desktop_create( + struct wlr_output *wlr_output) { + struct sway_output_non_desktop *output = + calloc(1, sizeof(struct sway_output_non_desktop)); + + output->wlr_output = wlr_output; + + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + output->destroy.notify = handle_destroy_non_desktop; + + return output; +} + enum sway_container_layout output_get_default_layout( struct sway_output *output) { if (config->default_orientation != L_NONE) { diff --git a/sway/tree/root.c b/sway/tree/root.c index 73f3993c..8934721f 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -38,6 +38,7 @@ struct sway_root *root_create(void) { wl_list_init(&root->drag_icons); wl_signal_init(&root->events.new_node); root->outputs = create_list(); + root->non_desktop_outputs = create_list(); root->scratchpad = create_list(); root->output_layout_change.notify = output_layout_handle_change; @@ -183,172 +184,6 @@ void root_scratchpad_hide(struct sway_container *con) { ipc_event_window(con, "move"); } -struct pid_workspace { - pid_t pid; - char *workspace; - struct timespec time_added; - - struct sway_output *output; - struct wl_listener output_destroy; - - struct wl_list link; -}; - -static struct wl_list pid_workspaces; - -/** - * Get the pid of a parent process given the pid of a child process. - * - * Returns the parent pid or NULL if the parent pid cannot be determined. - */ -static pid_t get_parent_pid(pid_t child) { - pid_t parent = -1; - char file_name[100]; - char *buffer = NULL; - const char *sep = " "; - FILE *stat = NULL; - size_t buf_size = 0; - - sprintf(file_name, "/proc/%d/stat", child); - - if ((stat = fopen(file_name, "r"))) { - if (getline(&buffer, &buf_size, stat) != -1) { - strtok(buffer, sep); // pid - strtok(NULL, sep); // executable name - strtok(NULL, sep); // state - char *token = strtok(NULL, sep); // parent pid - parent = strtol(token, NULL, 10); - } - free(buffer); - fclose(stat); - } - - if (parent) { - return (parent == child) ? -1 : parent; - } - - return -1; -} - -static void pid_workspace_destroy(struct pid_workspace *pw) { - wl_list_remove(&pw->output_destroy.link); - wl_list_remove(&pw->link); - free(pw->workspace); - free(pw); -} - -struct sway_workspace *root_workspace_for_pid(pid_t pid) { - if (!pid_workspaces.prev && !pid_workspaces.next) { - wl_list_init(&pid_workspaces); - return NULL; - } - - struct sway_workspace *ws = NULL; - struct pid_workspace *pw = NULL; - - sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); - - do { - struct pid_workspace *_pw = NULL; - wl_list_for_each(_pw, &pid_workspaces, link) { - if (pid == _pw->pid) { - pw = _pw; - sway_log(SWAY_DEBUG, - "found pid_workspace for pid %d, workspace %s", - pid, pw->workspace); - goto found; - } - } - pid = get_parent_pid(pid); - } while (pid > 1); -found: - - if (pw && pw->workspace) { - ws = workspace_by_name(pw->workspace); - - if (!ws) { - sway_log(SWAY_DEBUG, - "Creating workspace %s for pid %d because it disappeared", - pw->workspace, pid); - - struct sway_output *output = pw->output; - if (pw->output && !pw->output->enabled) { - sway_log(SWAY_DEBUG, - "Workspace output %s is disabled, trying another one", - pw->output->wlr_output->name); - output = NULL; - } - - ws = workspace_create(output, pw->workspace); - } - - pid_workspace_destroy(pw); - } - - return ws; -} - -static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { - struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); - pw->output = NULL; - wl_list_remove(&pw->output_destroy.link); - wl_list_init(&pw->output_destroy.link); -} - -void root_record_workspace_pid(pid_t pid) { - sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); - if (!pid_workspaces.prev && !pid_workspaces.next) { - wl_list_init(&pid_workspaces); - } - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (!ws) { - sway_log(SWAY_DEBUG, "Bailing out, no workspace"); - return; - } - struct sway_output *output = ws->output; - if (!output) { - sway_log(SWAY_DEBUG, "Bailing out, no output"); - return; - } - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - // Remove expired entries - static const int timeout = 60; - struct pid_workspace *old, *_old; - wl_list_for_each_safe(old, _old, &pid_workspaces, link) { - if (now.tv_sec - old->time_added.tv_sec >= timeout) { - pid_workspace_destroy(old); - } - } - - struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); - pw->workspace = strdup(ws->name); - pw->output = output; - pw->pid = pid; - memcpy(&pw->time_added, &now, sizeof(struct timespec)); - pw->output_destroy.notify = pw_handle_output_destroy; - wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); - wl_list_insert(&pid_workspaces, &pw->link); -} - -void root_remove_workspace_pid(pid_t pid) { - if (!pid_workspaces.prev || !pid_workspaces.next) { - return; - } - - struct pid_workspace *pw, *tmp; - wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { - if (pid == pw->pid) { - pid_workspace_destroy(pw); - return; - } - } -} - void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), void *data) { for (int i = 0; i < root->outputs->length; ++i) { @@ -443,17 +278,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { box->width = root->width; box->height = root->height; } - -void root_rename_pid_workspaces(const char *old_name, const char *new_name) { - if (!pid_workspaces.prev && !pid_workspaces.next) { - wl_list_init(&pid_workspaces); - } - - struct pid_workspace *pw = NULL; - wl_list_for_each(pw, &pid_workspaces, link) { - if (strcmp(pw->workspace, old_name) == 0) { - free(pw->workspace); - pw->workspace = strdup(new_name); - } - } -} diff --git a/sway/tree/view.c b/sway/tree/view.c index 8b7061ba..9d7b68d1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -6,6 +6,7 @@ #include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_server_decoration.h> +#include <wlr/types/wlr_subcompositor.h> #include <wlr/types/wlr_xdg_decoration_v1.h> #include "config.h" #if HAVE_XWAYLAND @@ -18,6 +19,7 @@ #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/desktop/idle_inhibit_v1.h" +#include "sway/desktop/launcher.h" #include "sway/input/cursor.h" #include "sway/ipc-server.h" #include "sway/output.h" @@ -62,6 +64,8 @@ void view_destroy(struct sway_view *view) { } list_free(view->executed_criteria); + view_assign_ctx(view, NULL); + free(view->title_format); if (view->impl->destroy) { @@ -532,6 +536,20 @@ static void view_populate_pid(struct sway_view *view) { view->pid = pid; } +void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { + if (view->ctx) { + // This ctx has been replaced + launcher_ctx_destroy(view->ctx); + view->ctx = NULL; + } + if (ctx == NULL) { + return; + } + launcher_ctx_consume(ctx); + + view->ctx = ctx; +} + static struct sway_workspace *select_workspace(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(); @@ -567,13 +585,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { } list_free(criterias); if (ws) { - root_remove_workspace_pid(view->pid); + view_assign_ctx(view, NULL); return ws; } // Check if there's a PID mapping - ws = root_workspace_for_pid(view->pid); + ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; if (ws) { + view_assign_ctx(view, NULL); return ws; } @@ -739,6 +758,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, view_populate_pid(view); view->container = container_create(view); + if (view->ctx == NULL) { + struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); + if (ctx != NULL) { + view_assign_ctx(view, ctx); + } + } + // If there is a request to be opened fullscreen on a specific output, try // to honor that request. Otherwise, fallback to assigns, pid mappings, // focused workspace, etc @@ -873,7 +899,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } void view_unmap(struct sway_view *view) { - wl_signal_emit(&view->events.unmap, view); + wl_signal_emit_mutable(&view->events.unmap, view); wl_list_remove(&view->surface_new_subsurface.link); @@ -1308,21 +1334,23 @@ void view_update_title(struct sway_view *view, bool force) { free(view->container->title); free(view->container->formatted_title); - if (title) { - size_t len = parse_title_format(view, NULL); + + size_t len = parse_title_format(view, NULL); + + if (len) { char *buffer = calloc(len + 1, sizeof(char)); if (!sway_assert(buffer, "Unable to allocate title string")) { return; } - parse_title_format(view, buffer); - view->container->title = strdup(title); + parse_title_format(view, buffer); view->container->formatted_title = buffer; } else { - view->container->title = NULL; view->container->formatted_title = NULL; } + view->container->title = title ? strdup(title) : NULL; + // Update title after the global font height is updated container_update_title_textures(view->container); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index c84320bd..ee940466 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -56,6 +56,8 @@ struct sway_output *workspace_get_initial_output(const char *name) { struct sway_workspace *workspace_create(struct sway_output *output, const char *name) { + sway_assert(name, "NULL name given to workspace_create"); + if (output == NULL) { output = workspace_get_initial_output(name); } @@ -69,7 +71,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); - ws->name = name ? strdup(name) : NULL; + ws->name = strdup(name); ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); ws->floating = create_list(); @@ -114,7 +116,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, output_sort_workspaces(output); ipc_event_workspace(NULL, ws, "init"); - wl_signal_emit(&root->events.new_node, &ws->node); + wl_signal_emit_mutable(&root->events.new_node, &ws->node); return ws; } @@ -142,7 +144,7 @@ void workspace_destroy(struct sway_workspace *workspace) { void workspace_begin_destroy(struct sway_workspace *workspace) { sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); ipc_event_workspace(NULL, workspace, "empty"); // intentional - wl_signal_emit(&workspace->node.events.destroy, &workspace->node); + wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); if (workspace->output) { workspace_detach(workspace); diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 6c70c785..2b94136c 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -1,4 +1,5 @@ #include <wlr/types/wlr_xdg_activation_v1.h> +#include "sway/desktop/launcher.h" #include "sway/tree/view.h" void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, @@ -11,8 +12,26 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(event->surface); + if (xdg_surface == NULL) { + return; + } struct sway_view *view = xdg_surface->data; - if (!xdg_surface->mapped || view == NULL) { + if (view == NULL) { + return; + } + + if (!xdg_surface->mapped) { + // This is a startup notification. If we are tracking it, the data + // field is a launcher_ctx. + struct launcher_ctx *ctx = event->token->data; + if (!ctx || ctx->activated) { + // This ctx has already been activated and cannot be used again + // for a startup notification. It will be destroyed + return; + } else { + ctx->activated = true; + view_assign_ctx(view, ctx); + } return; } |