diff options
Diffstat (limited to 'sway')
33 files changed, 610 insertions, 271 deletions
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 83e9e432..133fd089 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -34,11 +34,14 @@ void free_sway_binding(struct sway_binding *binding) { */ static bool binding_key_compare(struct sway_binding *binding_a, struct sway_binding *binding_b) { - if (binding_a->release != binding_b->release) { + if (binding_a->type != binding_b->type) { return false; } - if (binding_a->bindcode != binding_b->bindcode) { + uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER + | BINDING_CONTENTS | BINDING_TITLEBAR; + if ((binding_a->flags & conflict_generating_flags) != + (binding_b->flags & conflict_generating_flags)) { return false; } @@ -69,6 +72,66 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) { return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); } + +/** + * From a keycode, bindcode, or bindsym name and the most likely binding type, + * identify the appropriate numeric value corresponding to the key. Return NULL + * and set *key_val if successful, otherwise return a specific error. Change + * the value of *type if the initial type guess was incorrect and if this + * was the first identified key. + */ +static struct cmd_results *identify_key(const char* name, bool first_key, + uint32_t* key_val, enum binding_input_type* type) { + if (*type == BINDING_KEYCODE) { + // check for keycode + xkb_keycode_t keycode = strtol(name, NULL, 10); + if (!xkb_keycode_is_legal_ext(keycode)) { + return cmd_results_new(CMD_INVALID, "bindcode", + "Invalid keycode '%s'", name); + } + *key_val = keycode; + } else { + // check for keysym + xkb_keysym_t keysym = xkb_keysym_from_name(name, + XKB_KEYSYM_CASE_INSENSITIVE); + + // Check for mouse binding + uint32_t button = 0; + if (strncasecmp(name, "button", strlen("button")) == 0 && + strlen(name) == strlen("button0")) { + button = name[strlen("button")] - '1' + BTN_LEFT; + } + + if (*type == BINDING_KEYSYM) { + if (button) { + if (first_key) { + *type = BINDING_MOUSE; + *key_val = button; + } else { + return cmd_results_new(CMD_INVALID, "bindsym", + "Mixed button '%s' into key sequence", name); + } + } else if (keysym) { + *key_val = keysym; + } else { + return cmd_results_new(CMD_INVALID, "bindsym", + "Unknown key '%s'", name); + } + } else { + if (button) { + *key_val = button; + } else if (keysym) { + return cmd_results_new(CMD_INVALID, "bindsym", + "Mixed keysym '%s' into button sequence", name); + } else { + return cmd_results_new(CMD_INVALID, "bindsym", + "Unknown button '%s'", name); + } + } + } + return NULL; +} + static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, bool bindcode) { const char *bindtype = bindcode ? "bindcode" : "bindsym"; @@ -85,22 +148,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } binding->keys = create_list(); binding->modifiers = 0; - binding->release = false; - binding->locked = false; - binding->bindcode = bindcode; + binding->flags = 0; + binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; + + bool exclude_titlebar = false; // Handle --release and --locked while (argc > 0) { if (strcmp("--release", argv[0]) == 0) { - binding->release = true; + binding->flags |= BINDING_RELEASE; } else if (strcmp("--locked", argv[0]) == 0) { - binding->locked = true; + binding->flags |= BINDING_LOCKED; + } else if (strcmp("--whole-window", argv[0]) == 0) { + binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR; + } else if (strcmp("--border", argv[0]) == 0) { + binding->flags |= BINDING_BORDER; + } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { + exclude_titlebar = true; } else { break; } argv++; argc--; } + if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) + || exclude_titlebar) { + binding->type = BINDING_MOUSE; + } + if (argc < 2) { free_sway_binding(binding); return cmd_results_new(CMD_FAILURE, bindtype, @@ -119,64 +194,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, continue; } - xkb_keycode_t keycode; - xkb_keysym_t keysym; - if (bindcode) { - // parse keycode - keycode = (int)strtol(split->items[i], NULL, 10); - if (!xkb_keycode_is_legal_ext(keycode)) { - error = - cmd_results_new(CMD_INVALID, "bindcode", - "Invalid keycode '%s'", (char *)split->items[i]); - free_sway_binding(binding); - list_free(split); - return error; - } - } else { - // Check for xkb key - keysym = xkb_keysym_from_name(split->items[i], - XKB_KEYSYM_CASE_INSENSITIVE); - - // Check for mouse binding - if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && - strlen(split->items[i]) == strlen("button0")) { - keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; - } - if (!keysym) { - struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", - "Unknown key '%s'", (char *)split->items[i]); - free_sway_binding(binding); - free_flat_list(split); - return ret; - } + // Identify the key and possibly change binding->type + uint32_t key_val = 0; + error = identify_key(split->items[i], binding->keys->length == 0, + &key_val, &binding->type); + if (error) { + free_sway_binding(binding); + list_free(split); + return error; } + uint32_t *key = calloc(1, sizeof(uint32_t)); if (!key) { free_sway_binding(binding); free_flat_list(split); return cmd_results_new(CMD_FAILURE, bindtype, - "Unable to allocate binding"); + "Unable to allocate binding key"); } - - if (bindcode) { - *key = (uint32_t)keycode; - } else { - *key = (uint32_t)keysym; - } - + *key = key_val; list_add(binding->keys, key); } free_flat_list(split); binding->order = binding_order++; + // refine region of interest for mouse binding once we are certain + // that this is one + if (exclude_titlebar) { + binding->flags &= ~BINDING_TITLEBAR; + } else if (binding->type == BINDING_MOUSE) { + binding->flags |= BINDING_TITLEBAR; + } + // sort ascending list_qsort(binding->keys, key_qsort_cmp); list_t *mode_bindings; - if (bindcode) { + if (binding->type == BINDING_KEYCODE) { mode_bindings = config->current_mode->keycode_bindings; - } else { + } else if (binding->type == BINDING_KEYSYM) { mode_bindings = config->current_mode->keysym_bindings; + } else { + mode_bindings = config->current_mode->mouse_bindings; } // overwrite the binding if it already exists @@ -209,3 +267,19 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { struct cmd_results *cmd_bindcode(int argc, char **argv) { return cmd_bindsym_or_bindcode(argc, argv, true); } + + +/** + * Execute the command associated to a binding + */ +void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { + wlr_log(WLR_DEBUG, "running command for binding: %s", + binding->command); + config->handler_context.seat = seat; + struct cmd_results *results = execute_command(binding->command, NULL); + if (results->status != CMD_SUCCESS) { + wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", + binding->command, results->error); + } + free_cmd_results(results); +} diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index c7727857..9bf2b320 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -74,7 +74,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { waitpid(pid, NULL, 0); if (child > 0) { wlr_log(WLR_DEBUG, "Child process created with pid %d", child); - // TODO: add PID to active workspace + workspace_record_pid(child); } else { return cmd_results_new(CMD_FAILURE, "exec_always", "Second fork() failed"); diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c index 9432c9f1..f5d2b3fe 100644 --- a/sway/commands/floating_modifier.c +++ b/sway/commands/floating_modifier.c @@ -1,10 +1,11 @@ +#include "strings.h" #include "sway/commands.h" #include "sway/config.h" #include "util.h" struct cmd_results *cmd_floating_modifier(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) { return error; } @@ -14,6 +15,15 @@ struct cmd_results *cmd_floating_modifier(int argc, char **argv) { "Invalid modifier"); } + if (argc == 1 || strcasecmp(argv[1], "normal") == 0) { + config->floating_mod_inverse = false; + } else if (strcasecmp(argv[1], "inverse") == 0) { + config->floating_mod_inverse = true; + } else { + return cmd_results_new(CMD_INVALID, "floating_modifier", + "Usage: floating_modifier <mod> [inverse|normal]"); + } + config->floating_mod = mod; return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c index 661e7852..0b0e334c 100644 --- a/sway/commands/focus_follows_mouse.c +++ b/sway/commands/focus_follows_mouse.c @@ -1,12 +1,14 @@ #include <string.h> #include <strings.h> #include "sway/commands.h" +#include "util.h" struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { return error; } - config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); + config->focus_follows_mouse = + parse_boolean(argv[0], config->focus_follows_mouse); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/focus_wrapping.c b/sway/commands/focus_wrapping.c index 0a9e0bf2..562ee4f9 100644 --- a/sway/commands/focus_wrapping.c +++ b/sway/commands/focus_wrapping.c @@ -1,6 +1,7 @@ #include <strings.h> #include "sway/commands.h" #include "sway/config.h" +#include "util.h" struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { struct cmd_results *error = NULL; @@ -8,15 +9,12 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { return error; } - if (strcasecmp(argv[0], "no") == 0) { - config->focus_wrapping = WRAP_NO; - } else if (strcasecmp(argv[0], "yes") == 0) { - config->focus_wrapping = WRAP_YES; - } else if (strcasecmp(argv[0], "force") == 0) { + if (strcasecmp(argv[0], "force") == 0) { config->focus_wrapping = WRAP_FORCE; + } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) { + config->focus_wrapping = WRAP_YES; } else { - return cmd_results_new(CMD_INVALID, "focus_wrapping", - "Expected 'focus_wrapping yes|no|force'"); + config->focus_wrapping = WRAP_NO; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index bc1d067f..0892d9e9 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c @@ -1,6 +1,7 @@ #include <strings.h> #include "sway/commands.h" #include "sway/config.h" +#include "util.h" struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { struct cmd_results *error = @@ -9,13 +10,10 @@ struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { return error; } - if (strcasecmp(argv[0], "no") == 0) { - config->focus_wrapping = WRAP_YES; - } else if (strcasecmp(argv[0], "yes") == 0) { + if (parse_boolean(argv[0], config->focus_wrapping == WRAP_FORCE)) { config->focus_wrapping = WRAP_FORCE; } else { - return cmd_results_new(CMD_INVALID, "force_focus_wrapping", - "Expected 'force_focus_wrapping yes|no'"); + config->focus_wrapping = WRAP_YES; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 0b5beaa2..b423fd23 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -5,6 +5,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/layout.h" +#include "util.h" struct cmd_results *cmd_fullscreen(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,17 +19,10 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { "Only views can fullscreen"); } struct sway_view *view = container->sway_view; - bool wants_fullscreen; + bool wants_fullscreen = !view->is_fullscreen; - if (argc == 0 || strcmp(argv[0], "toggle") == 0) { - wants_fullscreen = !view->is_fullscreen; - } else if (strcmp(argv[0], "enable") == 0) { - wants_fullscreen = true; - } else if (strcmp(argv[0], "disable") == 0) { - wants_fullscreen = false; - } else { - return cmd_results_new(CMD_INVALID, "fullscreen", - "Expected 'fullscreen' or 'fullscreen <enable|disable|toggle>'"); + if (argc) { + wants_fullscreen = parse_boolean(argv[0], view->is_fullscreen); } view_set_fullscreen(view, wants_fullscreen); diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 9e32816f..f9ddeef2 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,14 +19,10 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "drag_lock", - "Expected 'drag_lock <enabled|disabled>'"); + new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; } apply_input_config(new_config); diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c index 73937507..15134268 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_dwt(int argc, char **argv) { struct cmd_results *error = NULL; @@ -17,14 +18,10 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "dwt", - "Expected 'dwt <enabled|disabled>'"); + new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; } apply_input_config(new_config); diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c index 769ce98c..e770043a 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_left_handed(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,15 +19,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { - new_config->left_handed = 1; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->left_handed = 0; - } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "left_handed", - "Expected 'left_handed <enabled|disabled>'"); - } + new_config->left_handed = parse_boolean(argv[0], true); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c index 7ca01629..414d4d2b 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,15 +19,11 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { + } else { new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; - } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "middle_emulation", - "Expected 'middle_emulation <enabled|disabled>'"); } apply_input_config(new_config); diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c index 55236790..77c3ff00 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,15 +19,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { - new_config->natural_scroll = 1; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->natural_scroll = 0; - } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "natural_scroll", - "Expected 'natural_scroll <enabled|disabled>'"); - } + new_config->natural_scroll = parse_boolean(argv[0], true); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index a8d1a10c..ac3b8237 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c @@ -4,6 +4,7 @@ #include "sway/commands.h" #include "sway/input/input-manager.h" #include "log.h" +#include "util.h" struct cmd_results *input_cmd_tap(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,14 +19,10 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "tap", - "Expected 'tap <enabled|disabled>'"); + new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; } wlr_log(WLR_DEBUG, "apply-tap for device: %s", diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 0959ea6b..3492061e 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c @@ -1,5 +1,6 @@ #include "sway/commands.h" #include "sway/config.h" +#include "util.h" struct cmd_results *output_cmd_dpms(int argc, char **argv) { if (!config->handler_context.output_config) { @@ -9,13 +10,10 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument."); } - if (strcmp(*argv, "on") == 0) { + if (parse_boolean(argv[0], true)) { config->handler_context.output_config->dpms_state = DPMS_ON; - } else if (strcmp(*argv, "off") == 0) { - config->handler_context.output_config->dpms_state = DPMS_OFF; } else { - return cmd_results_new(CMD_INVALID, "output", - "Invalid dpms state, valid states are on/off."); + config->handler_context.output_config->dpms_state = DPMS_OFF; } config->handler_context.leftovers.argc = argc - 1; diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index c7fdc538..434a0e27 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -7,6 +7,7 @@ #include "list.h" #include "log.h" #include "stringop.h" +#include "util.h" static void rebuild_marks_iterator(struct sway_container *con, void *data) { if (con->type == C_VIEW) { @@ -20,14 +21,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { return error; } - if (strcmp(*argv, "yes") == 0) { - config->show_marks = true; - } else if (strcmp(*argv, "no") == 0) { - config->show_marks = false; - } else { - return cmd_results_new(CMD_INVALID, "show_marks", - "Expected 'show_marks <yes|no>'"); - } + config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { container_for_each_descendant_dfs(&root_container, diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 2fc88308..4e3a9cce 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,5 +1,6 @@ #include <strings.h> #include <wlr/util/log.h> +#include "config.h" #include "sway/commands.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" @@ -14,10 +15,14 @@ static bool test_con_id(struct sway_container *container, void *con_id) { } static bool test_id(struct sway_container *container, void *id) { +#ifdef HAVE_XWAYLAND xcb_window_t *wid = id; return (container->type == C_VIEW && container->sway_view->type == SWAY_VIEW_XWAYLAND && container->sway_view->wlr_xwayland_surface->window_id == *wid); +#else + return false; +#endif } static bool test_mark(struct sway_container *container, void *mark) { @@ -43,8 +48,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { +#ifdef HAVE_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); other = container_find(&root_container, test_id, (void *)&id); +#endif } else if (strcasecmp(argv[2], "con_id") == 0) { size_t con_id = atoi(value); other = container_find(&root_container, test_con_id, (void *)con_id); diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c index d199858a..51c497c4 100644 --- a/sway/commands/urgent.c +++ b/sway/commands/urgent.c @@ -5,6 +5,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/layout.h" +#include "util.h" struct cmd_results *cmd_urgent(int argc, char **argv) { struct cmd_results *error = NULL; @@ -19,17 +20,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) { } struct sway_view *view = container->sway_view; - if (strcmp(argv[0], "enable") == 0) { - view_set_urgent(view, true); - } else if (strcmp(argv[0], "disable") == 0) { - view_set_urgent(view, false); - } else if (strcmp(argv[0], "allow") == 0) { + if (strcmp(argv[0], "allow") == 0) { view->allow_request_urgent = true; } else if (strcmp(argv[0], "deny") == 0) { view->allow_request_urgent = false; } else { - return cmd_results_new(CMD_INVALID, "urgent", - "Expected 'urgent <enable|disable|allow|deny>'"); + view_set_urgent(view, parse_boolean(argv[0], view_is_urgent(view))); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/config.c b/sway/config.c index ed624bfa..2afffab1 100644 --- a/sway/config.c +++ b/sway/config.c @@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) { } list_free(mode->keycode_bindings); } + if (mode->mouse_bindings) { + for (i = 0; i < mode->mouse_bindings->length; i++) { + free_sway_binding(mode->mouse_bindings->items[i]); + } + list_free(mode->mouse_bindings); + } free(mode); } @@ -87,7 +93,6 @@ void free_config(struct sway_config *config) { } list_free(config->cmd_queue); list_free(config->workspace_outputs); - list_free(config->pid_workspaces); if (config->output_configs) { for (int i = 0; i < config->output_configs->length; i++) { free_output_config(config->output_configs->items[i]); @@ -157,7 +162,6 @@ static void config_defaults(struct sway_config *config) { if (!(config->modes = create_list())) goto cleanup; if (!(config->bars = create_list())) goto cleanup; if (!(config->workspace_outputs = create_list())) goto cleanup; - if (!(config->pid_workspaces = create_list())) goto cleanup; if (!(config->criteria = create_list())) goto cleanup; if (!(config->no_focus = create_list())) goto cleanup; if (!(config->input_configs = create_list())) goto cleanup; @@ -172,9 +176,11 @@ static void config_defaults(struct sway_config *config) { strcpy(config->current_mode->name, "default"); if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; + if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; list_add(config->modes, config->current_mode); config->floating_mod = 0; + config->floating_mod_inverse = false; config->dragging_key = BTN_LEFT; config->resizing_key = BTN_RIGHT; diff --git a/sway/criteria.c b/sway/criteria.c index c2e9c07e..39d300ea 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -10,6 +10,7 @@ #include "stringop.h" #include "list.h" #include "log.h" +#include "config.h" bool criteria_is_empty(struct criteria *criteria) { return !criteria->title @@ -19,7 +20,9 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->instance && !criteria->con_mark && !criteria->con_id +#ifdef HAVE_XWAYLAND && !criteria->id +#endif && !criteria->window_role && !criteria->window_type && !criteria->floating @@ -127,12 +130,14 @@ static bool criteria_matches_view(struct criteria *criteria, } } +#ifdef HAVE_XWAYLAND if (criteria->id) { // X11 window ID uint32_t x11_window_id = view_get_x11_window_id(view); if (!x11_window_id || x11_window_id != criteria->id) { return false; } } +#endif if (criteria->window_role) { // TODO @@ -265,7 +270,9 @@ enum criteria_token { T_CON_ID, T_CON_MARK, T_FLOATING, +#ifdef HAVE_XWAYLAND T_ID, +#endif T_INSTANCE, T_SHELL, T_TILING, @@ -287,8 +294,10 @@ static enum criteria_token token_from_name(char *name) { return T_CON_ID; } else if (strcmp(name, "con_mark") == 0) { return T_CON_MARK; +#ifdef HAVE_XWAYLAND } else if (strcmp(name, "id") == 0) { return T_ID; +#endif } else if (strcmp(name, "instance") == 0) { return T_INSTANCE; } else if (strcmp(name, "shell") == 0) { @@ -355,7 +364,9 @@ static char *get_focused_prop(enum criteria_token token) { case T_CON_ID: // These do not support __focused__ case T_CON_MARK: case T_FLOATING: +#ifdef HAVE_XWAYLAND case T_ID: +#endif case T_TILING: case T_URGENT: case T_WINDOW_TYPE: @@ -426,12 +437,14 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_WINDOW_TYPE: // TODO: This is a string but will be stored as an enum or integer break; +#ifdef HAVE_XWAYLAND case T_ID: criteria->id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'id' should be numeric"); } break; +#endif case T_FLOATING: criteria->floating = true; break; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index cbd6ef86..1764b4e3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -14,6 +14,7 @@ #include <wlr/types/wlr_surface.h> #include <wlr/util/region.h> #include "log.h" +#include "config.h" #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" @@ -128,7 +129,7 @@ void output_layer_for_each_surface(struct wl_list *layer_surfaces, user_data); } } - +#ifdef HAVE_XWAYLAND void output_unmanaged_for_each_surface(struct wl_list *unmanaged, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -143,7 +144,7 @@ void output_unmanaged_for_each_surface(struct wl_list *unmanaged, iterator, user_data); } } - +#endif void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -232,13 +233,13 @@ static void send_frame_done_layer(struct send_frame_done_data *data, output_layer_for_each_surface(layer_surfaces, &data->root_geo, send_frame_done_iterator, data); } - +#ifdef HAVE_XWAYLAND static void send_frame_done_unmanaged(struct send_frame_done_data *data, struct wl_list *unmanaged) { output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, send_frame_done_iterator, data); } - +#endif static void send_frame_done_drag_icons(struct send_frame_done_data *data, struct wl_list *drag_icons) { output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, @@ -280,11 +281,12 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { if (workspace->current.ws_fullscreen) { send_frame_done_container_iterator( workspace->current.ws_fullscreen->swayc, &data); - +#ifdef HAVE_XWAYLAND if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); } +#endif } else { send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); @@ -294,8 +296,10 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_done_container(&data, workspace); send_frame_done_container(&data, workspace->sway_workspace->floating); +#ifdef HAVE_XWAYLAND send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); +#endif send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index d6c3fa8c..15c5b94c 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -14,6 +14,7 @@ #include <wlr/types/wlr_surface.h> #include <wlr/util/region.h> #include "log.h" +#include "config.h" #include "sway/config.h" #include "sway/debug.h" #include "sway/input/input-manager.h" @@ -132,7 +133,7 @@ static void render_layer(struct sway_output *output, output_layer_for_each_surface(layer_surfaces, &data.root_geo, render_surface_iterator, &data); } - +#ifdef HAVE_XWAYLAND static void render_unmanaged(struct sway_output *output, pixman_region32_t *damage, struct wl_list *unmanaged) { struct render_data data = { @@ -143,7 +144,7 @@ static void render_unmanaged(struct sway_output *output, output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, render_surface_iterator, &data); } - +#endif static void render_drag_icons(struct sway_output *output, pixman_region32_t *damage, struct wl_list *drag_icons) { struct render_data data = { @@ -857,11 +858,12 @@ void output_render(struct sway_output *output, struct timespec *when, } else { render_view_surfaces(fullscreen_view, output, damage, 1.0f); } - +#ifdef HAVE_XWAYLAND if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); } +#endif } else { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; @@ -879,9 +881,10 @@ void output_render(struct sway_output *output, struct timespec *when, render_container(output, damage, workspace, workspace->current.focused); render_floating(output, damage); - +#ifdef HAVE_XWAYLAND render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); +#endif render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 62c3abc8..f3e4fef8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -418,9 +418,6 @@ 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; - // TODO: - // - Look up pid and open on appropriate workspace - xdg_shell_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 7fb85410..46fd4769 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -409,9 +409,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; - // TODO: - // - Look up pid and open on appropriate workspace - xdg_shell_v6_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 2546168b..65d4fcd4 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -514,9 +514,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); xwayland_view->view.wlr_xwayland_surface = xsurface; - // TODO: - // - Look up pid and open on appropriate workspace - wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); xwayland_view->destroy.notify = handle_destroy; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 65d04cac..02bd2239 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -11,6 +11,7 @@ #include <wlr/types/wlr_idle.h> #include "list.h" #include "log.h" +#include "config.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -54,6 +55,7 @@ static struct sway_container *container_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first +#ifdef HAVE_XWAYLAND struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; struct sway_xwayland_unmanaged *unmanaged_surface; wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { @@ -69,7 +71,7 @@ static struct sway_container *container_at_coords( return NULL; } } - +#endif // find the output the cursor is on struct wlr_output_layout *output_layout = root_container.sway_root->output_layout; @@ -449,17 +451,25 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, bool over_title = edge == WLR_EDGE_NONE && !surface; // Check for beginning move - if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && + uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat_begin_move(seat, cont, BTN_LEFT); + seat_begin_move(seat, cont, button); return; } // Check for beginning resize bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; - bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; + uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; + bool resizing_via_mod = button == btn_resize && mod_pressed; if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { + if (edge == WLR_EDGE_NONE) { + edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + } seat_begin_resize(seat, cont, button, edge); return; } @@ -469,6 +479,83 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, seat_pointer_notify_button(seat, time_msec, button, state); } +/** + * Remove a button (and duplicates) to the sorted list of currently pressed buttons + */ +static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { + size_t j = 0; + for (size_t i = 0; i < cursor->pressed_button_count; ++i) { + if (i > j) { + cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; + } + if (cursor->pressed_buttons[i] != button) { + ++j; + } + } + while (cursor->pressed_button_count > j) { + --cursor->pressed_button_count; + cursor->pressed_buttons[cursor->pressed_button_count] = 0; + } +} + +/** + * Add a button to the sorted list of currently pressed buttons, if there + * is space. + */ +static void state_add_button(struct sway_cursor *cursor, uint32_t button) { + if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { + return; + } + size_t i = 0; + while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { + ++i; + } + size_t j = cursor->pressed_button_count; + while (j > i) { + cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; + --j; + } + cursor->pressed_buttons[i] = button; + cursor->pressed_button_count++; +} + +/** + * Return the mouse binding which matches modifier, click location, release, + * and pressed button state, otherwise return null. + */ +static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor, + list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, + bool on_border, bool on_content) { + uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | + (on_border ? BINDING_BORDER : 0) | + (on_content ? BINDING_CONTENTS : 0); + + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; + if (modifiers ^ binding->modifiers || + cursor->pressed_button_count != (size_t)binding->keys->length || + release != (binding->flags & BINDING_RELEASE) || + !(click_region & binding->flags)) { + continue; + } + + bool match = true; + for (size_t j = 0; j < cursor->pressed_button_count; j++) { + uint32_t key = *(uint32_t *)binding->keys->items[j]; + if (key != cursor->pressed_buttons[j]) { + match = false; + break; + } + } + if (!match) { + continue; + } + + return binding; + } + return NULL; +} + void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { if (cursor->seat->operation != OP_NONE && @@ -485,6 +572,31 @@ void dispatch_cursor_button(struct sway_cursor *cursor, double sx, sy; struct sway_container *cont = container_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + + // Handle mouse bindings + bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE); + bool on_contents = !on_border && surface; + bool on_titlebar = !on_border && !surface; + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + struct sway_binding *binding = NULL; + if (state == WLR_BUTTON_PRESSED) { + state_add_button(cursor, button); + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents); + } else { + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents); + state_erase_button(cursor, button); + } + if (binding) { + seat_execute_command(cursor->seat, binding); + // TODO: do we want to pass on the event? + } + if (surface && wlr_surface_is_layer_surface(surface)) { struct wlr_layer_surface *layer = wlr_layer_surface_from_wlr_surface(surface); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ede38519..49241db8 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,11 +3,11 @@ #include <wlr/backend/multi.h> #include <wlr/backend/session.h> #include <wlr/types/wlr_idle.h> +#include "sway/commands.h" #include "sway/desktop/transaction.h" -#include "sway/input/seat.h" -#include "sway/input/keyboard.h" #include "sway/input/input-manager.h" -#include "sway/commands.h" +#include "sway/input/keyboard.h" +#include "sway/input/seat.h" #include "log.h" /** @@ -88,11 +88,13 @@ static void get_active_binding(const struct sway_shortcut_state *state, uint32_t modifiers, bool release, bool locked) { for (int i = 0; i < bindings->length; ++i) { struct sway_binding *binding = bindings->items[i]; + bool binding_locked = binding->flags & BINDING_LOCKED; + bool binding_release = binding->flags & BINDING_RELEASE; if (modifiers ^ binding->modifiers || state->npressed != (size_t)binding->keys->length || - locked > binding->locked || - release != binding->release) { + release != binding_release || + locked > binding_locked) { continue; } @@ -119,23 +121,6 @@ static void get_active_binding(const struct sway_shortcut_state *state, } /** - * Execute the command associated to a binding - */ -static void keyboard_execute_command(struct sway_keyboard *keyboard, - struct sway_binding *binding) { - wlr_log(WLR_DEBUG, "running command for binding: %s", - binding->command); - config->handler_context.seat = keyboard->seat_device->sway_seat; - struct cmd_results *results = execute_command(binding->command, NULL); - transaction_commit_dirty(); - if (results->status != CMD_SUCCESS) { - wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", - binding->command, results->error); - } - free_cmd_results(results); -} - -/** * Execute a built-in, hardcoded compositor binding. These are triggered from a * single keysym. * @@ -211,12 +196,13 @@ static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, static void handle_keyboard_key(struct wl_listener *listener, void *data) { struct sway_keyboard *keyboard = wl_container_of(listener, keyboard, keyboard_key); - struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; + 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; - wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); + wlr_idle_notify_activity(seat->input->server->idle, wlr_seat); struct wlr_event_keyboard_key *event = data; - bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; + bool input_inhibited = seat->exclusive_client != NULL; // Identify new keycode, raw keysym(s), and translated keysym(s) xkb_keycode_t keycode = event->keycode + 8; @@ -266,7 +252,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && event->state == WLR_KEY_RELEASED) { - keyboard_execute_command(keyboard, keyboard->held_binding); + seat_execute_command(seat, keyboard->held_binding); handled = true; } if (binding_released != keyboard->held_binding) { @@ -290,7 +276,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { raw_modifiers, false, input_inhibited); if (binding_pressed) { - keyboard_execute_command(keyboard, binding_pressed); + seat_execute_command(seat, binding_pressed); handled = true; } } @@ -312,6 +298,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); } + + transaction_commit_dirty(); } static void handle_keyboard_modifiers(struct wl_listener *listener, diff --git a/sway/input/seat.c b/sway/input/seat.c index fc9e54b6..8698d69e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -12,6 +12,7 @@ #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_xcursor_manager.h> #include "log.h" +#include "config.h" #include "sway/debug.h" #include "sway/desktop.h" #include "sway/input/cursor.h" @@ -103,11 +104,13 @@ static void seat_send_focus(struct sway_container *con, if (con->type == C_VIEW && seat_is_input_allowed(seat, con->sway_view->surface)) { +#ifdef HAVE_XWAYLAND if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = seat->input->server->xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); } +#endif struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { wlr_seat_keyboard_notify_enter(seat->wlr_seat, @@ -181,6 +184,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener, bool set_focus = focus != NULL && (focus == con || container_has_child(con, focus)) && + con->parent && con->parent->children->length > 1 && con->type != C_WORKSPACE; seat_container_destroy(seat_con); diff --git a/sway/meson.build b/sway/meson.build index 30c848e2..649a3ac2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -18,7 +18,6 @@ sway_sources = files( 'desktop/transaction.c', 'desktop/xdg_shell_v6.c', 'desktop/xdg_shell.c', - 'desktop/xwayland.c', 'input/input-manager.c', 'input/seat.c', @@ -152,6 +151,10 @@ sway_sources = files( 'tree/output.c', ) +if get_option('enable-xwayland') + sway_sources += 'desktop/xwayland.c' +endif + sway_deps = [ cairo, gdk_pixbuf, diff --git a/sway/server.c b/sway/server.c index 89dfbf8c..10ca9614 100644 --- a/sway/server.c +++ b/sway/server.c @@ -25,7 +25,10 @@ #include "sway/input/input-manager.h" #include "sway/server.h" #include "sway/tree/layout.h" +#include "config.h" +#ifdef HAVE_XWAYLAND #include "sway/xwayland.h" +#endif bool server_privileged_prepare(struct sway_server *server) { wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); @@ -81,6 +84,7 @@ bool server_init(struct sway_server *server) { server->xdg_shell_surface.notify = handle_xdg_shell_surface; // TODO make xwayland optional +#ifdef HAVE_XWAYLAND server->xwayland.wlr_xwayland = wlr_xwayland_create(server->wl_display, server->compositor, true); wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, @@ -101,6 +105,7 @@ bool server_init(struct sway_server *server) { image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y); } +#endif // TODO: Integration with sway borders struct wlr_server_decoration_manager *deco_manager = diff --git a/sway/tree/container.c b/sway/tree/container.c index 4f743c40..b56b4e87 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -527,10 +527,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc, double _sx, _sy; struct wlr_surface *_surface = NULL; switch (sview->type) { +#ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: _surface = wlr_surface_surface_at(sview->surface, view_sx, view_sy, &_sx, &_sy); break; +#endif case SWAY_VIEW_XDG_SHELL_V6: _surface = wlr_xdg_surface_v6_surface_at( sview->wlr_xdg_surface_v6, diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a2be0ef3..2b3263f8 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -6,6 +6,7 @@ #include <string.h> #include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output_layout.h> +#include "config.h" #include "sway/debug.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" @@ -39,7 +40,9 @@ void layout_init(void) { root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); root_container.sway_root->output_layout = wlr_output_layout_create(); wl_list_init(&root_container.sway_root->outputs); +#ifdef HAVE_XWAYLAND wl_list_init(&root_container.sway_root->xwayland_unmanaged); +#endif wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); root_container.sway_root->scratchpad = create_list(); diff --git a/sway/tree/view.c b/sway/tree/view.c index 9d88d7aa..beeb8144 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,6 +3,10 @@ #include <wayland-server.h> #include <wlr/render/wlr_renderer.h> #include <wlr/types/wlr_output_layout.h> +#include "config.h" +#ifdef HAVE_XWAYLAND +#include <wlr/xwayland.h> +#endif #include "list.h" #include "log.h" #include "sway/criteria.h" @@ -107,14 +111,14 @@ const char *view_get_instance(struct sway_view *view) { } return NULL; } - +#ifdef HAVE_XWAYLAND uint32_t view_get_x11_window_id(struct sway_view *view) { if (view->impl->get_int_prop) { return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); } return 0; } - +#endif const char *view_get_window_role(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); @@ -135,8 +139,10 @@ const char *view_get_shell(struct sway_view *view) { return "xdg_shell_v6"; case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; +#ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; +#endif } return "unknown"; } @@ -561,9 +567,27 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { return; } + pid_t pid; +#ifdef HAVE_XWAYLAND + if (view->type == SWAY_VIEW_XWAYLAND) { + struct wlr_xwayland_surface *surf = + wlr_xwayland_surface_from_wlr_surface(wlr_surface); + pid = surf->pid; + } else { + struct wl_client *client = + wl_resource_get_client(wlr_surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); + } +#else + struct wl_client *client = + wl_resource_get_client(wlr_surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); +#endif + struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = + struct sway_container *target_sibling = seat_get_focus_inactive(seat, &root_container); + struct sway_container *prev_focus = target_sibling; struct sway_container *cont = NULL; // Check if there's any `assign` criteria for the view @@ -577,22 +601,35 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!workspace) { workspace = workspace_create(NULL, criteria->target); } - focus = seat_get_focus_inactive(seat, workspace); + prev_focus = target_sibling; + target_sibling = seat_get_focus_inactive(seat, workspace); } else { // CT_ASSIGN_OUTPUT struct sway_container *output = output_by_name(criteria->target); if (output) { - focus = seat_get_focus_inactive(seat, output); + prev_focus = seat_get_focus_inactive(seat, output); } } } + list_free(criterias); + + if (!workspace) { + workspace = workspace_for_pid(pid); + if (workspace) { + prev_focus = target_sibling; + target_sibling = seat_get_focus_inactive(seat, workspace); + } + } // If we're about to launch the view into the floating container, then // launch it as a tiled view in the root of the workspace instead. - if (container_is_floating(focus)) { - focus = focus->parent->parent; + if (container_is_floating(target_sibling)) { + if (prev_focus == target_sibling) { + prev_focus = target_sibling->parent->parent; + } + target_sibling = target_sibling->parent->parent; } - list_free(criterias); - cont = container_view_create(focus, view); + + cont = container_view_create(target_sibling, view); view->surface = wlr_surface; view->swayc = cont; @@ -615,7 +652,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_set_tiled(view, true); } - if (should_focus(view)) { + if (should_focus(view) && prev_focus == target_sibling) { input_manager_set_focus(input_manager, cont); if (workspace) { workspace_switch(workspace); @@ -799,11 +836,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); return view_from_wlr_xdg_surface_v6(xdg_surface_v6); } +#ifdef HAVE_XWAYLAND if (wlr_surface_is_xwayland_surface(wlr_surface)) { struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_from_wlr_surface(wlr_surface); return view_from_wlr_xwayland_surface(xsurface); } +#endif if (wlr_surface_is_subsurface(wlr_surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(wlr_surface); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 622f01ec..62974cd7 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -9,6 +9,7 @@ #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -107,96 +108,103 @@ static bool workspace_valid_on_output(const char *output_name, return true; } -char *workspace_next_name(const char *output_name) { - wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", - output_name); - // Scan all workspace bindings to find the next available workspace name, - // if none are found/available then default to a number - struct sway_mode *mode = config->current_mode; +static void workspace_name_from_binding(const struct sway_binding * binding, + const char* output_name, int *min_order, char **earliest_name) { + char *cmdlist = strdup(binding->command); + char *dup = cmdlist; + char *name = NULL; - // TODO: iterate over keycode bindings too - int order = INT_MAX; - char *target = NULL; - for (int i = 0; i < mode->keysym_bindings->length; ++i) { - struct sway_binding *binding = mode->keysym_bindings->items[i]; - char *cmdlist = strdup(binding->command); - char *dup = cmdlist; - char *name = NULL; - - // workspace n - char *cmd = argsep(&cmdlist, " "); - if (cmdlist) { - name = argsep(&cmdlist, ",;"); - } + // workspace n + char *cmd = argsep(&cmdlist, " "); + if (cmdlist) { + name = argsep(&cmdlist, ",;"); + } - if (strcmp("workspace", cmd) == 0 && name) { - char *_target = strdup(name); - _target = do_var_replacement(_target); - strip_quotes(_target); - while (isspace(*_target)) { - memmove(_target, _target+1, strlen(_target+1)); - } - wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", - _target); + if (strcmp("workspace", cmd) == 0 && name) { + char *_target = strdup(name); + _target = do_var_replacement(_target); + strip_quotes(_target); + while (isspace(*_target)) { + memmove(_target, _target+1, strlen(_target+1)); + } + wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", + _target); - // Make sure that the command references an actual workspace - // not a command about workspaces - if (strcmp(_target, "next") == 0 || + // Make sure that the command references an actual workspace + // not a command about workspaces + if (strcmp(_target, "next") == 0 || strcmp(_target, "prev") == 0 || strcmp(_target, "next_on_output") == 0 || strcmp(_target, "prev_on_output") == 0 || strcmp(_target, "number") == 0 || strcmp(_target, "back_and_forth") == 0 || - strcmp(_target, "current") == 0) - { - free(_target); - free(dup); - continue; - } - - // If the command is workspace number <name>, isolate the name - if (strncmp(_target, "number ", strlen("number ")) == 0) { - size_t length = strlen(_target) - strlen("number ") + 1; - char *temp = malloc(length); - strncpy(temp, _target + strlen("number "), length - 1); - temp[length - 1] = '\0'; - free(_target); - _target = temp; - wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); - - // Make sure the workspace number doesn't already exist - if (workspace_by_number(_target)) { - free(_target); - free(dup); - continue; - } - } + strcmp(_target, "current") == 0) { + free(_target); + free(dup); + return; + } - // Make sure that the workspace doesn't already exist - if (workspace_by_name(_target)) { + // If the command is workspace number <name>, isolate the name + if (strncmp(_target, "number ", strlen("number ")) == 0) { + size_t length = strlen(_target) - strlen("number ") + 1; + char *temp = malloc(length); + strncpy(temp, _target + strlen("number "), length - 1); + temp[length - 1] = '\0'; + free(_target); + _target = temp; + wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); + + // Make sure the workspace number doesn't already exist + if (workspace_by_number(_target)) { free(_target); free(dup); - continue; + return; } + } - // make sure that the workspace can appear on the given - // output - if (!workspace_valid_on_output(output_name, _target)) { - free(_target); - free(dup); - continue; - } + // Make sure that the workspace doesn't already exist + if (workspace_by_name(_target)) { + free(_target); + free(dup); + return; + } - if (binding->order < order) { - order = binding->order; - free(target); - target = _target; - wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); - } else { - free(_target); - } + // make sure that the workspace can appear on the given + // output + if (!workspace_valid_on_output(output_name, _target)) { + free(_target); + free(dup); + return; + } + + if (binding->order < *min_order) { + *min_order = binding->order; + free(*earliest_name); + *earliest_name = _target; + wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); + } else { + free(_target); } - free(dup); + } + free(dup); +} + +char *workspace_next_name(const char *output_name) { + wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", + output_name); + // Scan all workspace bindings to find the next available workspace name, + // if none are found/available then default to a number + struct sway_mode *mode = config->current_mode; + + int order = INT_MAX; + char *target = NULL; + for (int i = 0; i < mode->keysym_bindings->length; ++i) { + workspace_name_from_binding(mode->keysym_bindings->items[i], + output_name, &order, &target); + } + for (int i = 0; i < mode->keycode_bindings->length; ++i) { + workspace_name_from_binding(mode->keycode_bindings->items[i], + output_name, &order, &target); } if (target != NULL) { return target; @@ -529,3 +537,116 @@ void workspace_detect_urgent(struct sway_container *workspace) { container_damage_whole(workspace); } } + +struct pid_workspace { + pid_t pid; + char *workspace; + struct timespec time_added; + + struct sway_container *output; + struct wl_listener output_destroy; + + struct wl_list link; +}; + +static struct wl_list pid_workspaces; + +struct sway_container *workspace_for_pid(pid_t pid) { + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + return NULL; + } + + struct sway_container *ws = NULL; + struct pid_workspace *pw = NULL; + + wlr_log(WLR_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; + wlr_log(WLR_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) { + wlr_log(WLR_DEBUG, + "Creating workspace %s for pid %d because it disappeared", + pw->workspace, pid); + ws = workspace_create(pw->output, pw->workspace); + } + + wl_list_remove(&pw->output_destroy.link); + wl_list_remove(&pw->link); + free(pw->workspace); + free(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 workspace_record_pid(pid_t pid) { + wlr_log(WLR_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(input_manager); + struct sway_container *ws = + seat_get_focus_inactive(seat, &root_container); + if (ws && ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + if (!ws) { + wlr_log(WLR_DEBUG, "Bailing out, no workspace"); + return; + } + struct sway_container *output = ws->parent; + if (!output) { + wlr_log(WLR_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) { + wl_list_remove(&old->output_destroy.link); + wl_list_remove(&old->link); + free(old->workspace); + free(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->sway_output->wlr_output->events.destroy, + &pw->output_destroy); + wl_list_insert(&pid_workspaces, &pw->link); +} |