diff options
Diffstat (limited to 'sway')
46 files changed, 827 insertions, 508 deletions
diff --git a/sway/commands.c b/sway/commands.c index 37c7169a..4b86c2fa 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -353,12 +353,14 @@ struct cmd_results *config_command(char *exec) { struct cmd_results *results = NULL; int argc; char **argv = split_args(exec, &argc); + + // Check for empty lines if (!argc) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); goto cleanup; } - // Start block + // Check for the start of a block if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { char *block = join_args(argv, argc - 1); results = cmd_results_new(CMD_BLOCK, block, NULL); @@ -366,22 +368,54 @@ struct cmd_results *config_command(char *exec) { goto cleanup; } - // Endblock + // Check for the end of a block if (strcmp(argv[argc - 1], "}") == 0) { results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); goto cleanup; } - wlr_log(WLR_INFO, "handling config command '%s'", exec); + + // Make sure the command is not stored in a variable + if (*argv[0] == '$') { + argv[0] = do_var_replacement(argv[0]); + char *temp = join_args(argv, argc); + free_argv(argc, argv); + argv = split_args(temp, &argc); + free(temp); + if (!argc) { + results = cmd_results_new(CMD_SUCCESS, NULL, NULL); + goto cleanup; + } + } + + // Determine the command handler + wlr_log(WLR_INFO, "Config command: %s", exec); struct cmd_handler *handler = find_handler(argv[0], NULL, 0); - if (!handler) { + if (!handler || !handler->handle) { char *input = argv[0] ? argv[0] : "(empty)"; - results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); + char *error = handler + ? "This command is shimmed, but unimplemented" + : "Unknown/invalid command"; + results = cmd_results_new(CMD_INVALID, input, error); goto cleanup; } - int i; - // Var replacement, for all but first argument of set - // TODO commands - for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { + + // Do variable replacement + if (handler->handle == cmd_set && argc > 1 && *argv[1] == '$') { + // Escape the variable name so it does not get replaced by one shorter + char *temp = calloc(1, strlen(argv[1]) + 2); + temp[0] = '$'; + strcpy(&temp[1], argv[1]); + free(argv[1]); + argv[1] = temp; + } + char *command = do_var_replacement(join_args(argv, argc)); + wlr_log(WLR_INFO, "After replacement: %s", command); + free_argv(argc, argv); + argv = split_args(command, &argc); + free(command); + + // Strip quotes and unescape the string + for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { if (handler->handle != cmd_exec && handler->handle != cmd_exec_always && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode @@ -389,14 +423,11 @@ struct cmd_results *config_command(char *exec) { && (*argv[i] == '\"' || *argv[i] == '\'')) { strip_quotes(argv[i]); } - argv[i] = do_var_replacement(argv[i]); unescape_string(argv[i]); } - if (handler->handle) { - results = handler->handle(argc-1, argv+1); - } else { - results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented"); - } + + // Run command + results = handler->handle(argc - 1, argv + 1); cleanup: free_argv(argc, argv); diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index f18b8d7c..b048b7b9 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -2,6 +2,7 @@ #include <strings.h> #include "sway/commands.h" #include "log.h" +#include "util.h" struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { struct cmd_results *error = NULL; @@ -13,17 +14,14 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { - config->current_bar->binding_mode_indicator = true; + config->current_bar->binding_mode_indicator = + parse_boolean(argv[0], config->current_bar->binding_mode_indicator); + if (config->current_bar->binding_mode_indicator) { wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id); - } else if (strcasecmp("no", argv[0]) == 0) { - config->current_bar->binding_mode_indicator = false; + } else { wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); - } else { - return cmd_results_new(CMD_INVALID, "binding_mode_indicator", - "Invalid value %s", argv[0]); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index 857571fb..d57cc45c 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c @@ -2,6 +2,7 @@ #include <strings.h> #include "sway/commands.h" #include "log.h" +#include "util.h" struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { struct cmd_results *error = NULL; @@ -11,18 +12,14 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); } - if (strcasecmp("enabled", argv[0]) == 0) { - config->current_bar->pango_markup = true; + config->current_bar->pango_markup + = parse_boolean(argv[0], config->current_bar->pango_markup); + if (config->current_bar->pango_markup) { wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); - } else if (strcasecmp("disabled", argv[0]) == 0) { - config->current_bar->pango_markup = false; + } else { wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id); - } else { - error = cmd_results_new(CMD_INVALID, "pango_markup", - "Invalid value %s", argv[0]); - return error; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index a4079b2a..cd001e20 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c @@ -2,6 +2,7 @@ #include <strings.h> #include "sway/commands.h" #include "log.h" +#include "util.h" struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { struct cmd_results *error = NULL; @@ -12,17 +13,14 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { - config->current_bar->workspace_buttons = true; + config->current_bar->workspace_buttons = + parse_boolean(argv[0], config->current_bar->workspace_buttons); + if (config->current_bar->workspace_buttons) { wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id); - } else if (strcasecmp("no", argv[0]) == 0) { - config->current_bar->workspace_buttons = false; + } else { wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id); - } else { - return cmd_results_new(CMD_INVALID, "workspace_buttons", - "Invalid value %s", argv[0]); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index 701de00a..04a4e6b8 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c @@ -2,6 +2,7 @@ #include <strings.h> #include "sway/commands.h" #include "log.h" +#include "util.h" struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { struct cmd_results *error = NULL; @@ -11,17 +12,14 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { - config->current_bar->wrap_scroll = true; + config->current_bar->wrap_scroll = + parse_boolean(argv[0], config->current_bar->wrap_scroll); + if (config->current_bar->wrap_scroll) { wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s", - config->current_bar->id); - } else if (strcasecmp("no", argv[0]) == 0) { - config->current_bar->wrap_scroll = false; + config->current_bar->id); + } else { wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id); - } else { - return cmd_results_new(CMD_INVALID, - "wrap_scroll", "Invalid value %s", argv[0]); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/border.c b/sway/commands/border.c index b6eab550..d51741d2 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -93,7 +93,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { } if (container_is_floating(container)) { - container_set_geometry_from_floating_view(container); + container_set_geometry_from_content(container); } arrange_container(container); diff --git a/sway/commands/create_output.c b/sway/commands/create_output.c index 1c2464ea..3f870acb 100644 --- a/sway/commands/create_output.c +++ b/sway/commands/create_output.c @@ -1,7 +1,7 @@ #include <wlr/config.h> #include <wlr/backend/multi.h> #include <wlr/backend/wayland.h> -#ifdef WLR_HAS_X11_BACKEND +#if WLR_HAS_X11_BACKEND #include <wlr/backend/x11.h> #endif #include "sway/commands.h" @@ -18,7 +18,7 @@ static void create_output(struct wlr_backend *backend, void *data) { wlr_wl_output_create(backend); *done = true; } -#ifdef WLR_HAS_X11_BACKEND +#if WLR_HAS_X11_BACKEND else if (wlr_backend_is_x11(backend)) { wlr_x11_output_create(backend); *done = true; diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 81bb86f8..4b82921c 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -9,6 +9,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" +#include "util.h" struct cmd_results *cmd_floating(int argc, char **argv) { struct cmd_results *error = NULL; @@ -40,17 +41,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) { } } - bool wants_floating; - if (strcasecmp(argv[0], "enable") == 0) { - wants_floating = true; - } else if (strcasecmp(argv[0], "disable") == 0) { - wants_floating = false; - } else if (strcasecmp(argv[0], "toggle") == 0) { - wants_floating = !container_is_floating(container); - } else { - return cmd_results_new(CMD_FAILURE, "floating", - "Expected 'floating <enable|disable|toggle>'"); - } + bool wants_floating = + parse_boolean(argv[0], container_is_floating(container)); container_set_floating(container, wants_floating); diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c index 0b0e334c..d0d2cb8a 100644 --- a/sway/commands/focus_follows_mouse.c +++ b/sway/commands/focus_follows_mouse.c @@ -7,8 +7,15 @@ 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; + } else if(strcmp(argv[0], "no") == 0) { + config->focus_follows_mouse = FOLLOWS_NO; + } else if(strcmp(argv[0], "yes") == 0) { + config->focus_follows_mouse = FOLLOWS_YES; + } else if(strcmp(argv[0], "always") == 0) { + config->focus_follows_mouse = FOLLOWS_ALWAYS; + } else { + return cmd_results_new(CMD_FAILURE, "focus_follows_mouse", + "Expected 'focus_follows_mouse no|yes|always'"); } - 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/gaps.c b/sway/commands/gaps.c index 3f0ef155..faaeab37 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -16,73 +16,128 @@ enum gaps_op { struct gaps_data { bool inner; + struct { + bool top; + bool right; + bool bottom; + bool left; + } outer; enum gaps_op operation; int amount; }; -// gaps inner|outer <px> +// Prevent negative outer gaps from moving windows out of the workspace. +static void prevent_invalid_outer_gaps(void) { + if (config->gaps_outer.top < -config->gaps_inner) { + config->gaps_outer.top = -config->gaps_inner; + } + if (config->gaps_outer.right < -config->gaps_inner) { + config->gaps_outer.right = -config->gaps_inner; + } + if (config->gaps_outer.bottom < -config->gaps_inner) { + config->gaps_outer.bottom = -config->gaps_inner; + } + if (config->gaps_outer.left < -config->gaps_inner) { + config->gaps_outer.left = -config->gaps_inner; + } +} + +// gaps inner|outer|horizontal|vertical|top|right|bottom|left <px> +static const char *expected_defaults = + "'gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; static struct cmd_results *gaps_set_defaults(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); if (error) { return error; } - bool inner; - if (strcasecmp(argv[0], "inner") == 0) { - inner = true; - } else if (strcasecmp(argv[0], "outer") == 0) { - inner = false; - } else { - return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer <px>'"); - } - char *end; int amount = strtol(argv[1], &end, 10); if (strlen(end) && strcasecmp(end, "px") != 0) { return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer <px>'"); + "Expected %s", expected_defaults); } - if (inner) { + + bool valid = false; + if (!strcasecmp(argv[0], "inner")) { + valid = true; config->gaps_inner = (amount >= 0) ? amount : 0; } else { - config->gaps_outer = amount; - } - - // Prevent negative outer gaps from moving windows out of the workspace. - if (config->gaps_outer < -config->gaps_inner) { - config->gaps_outer = -config->gaps_inner; + if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical") + || !strcasecmp(argv[0], "top")) { + valid = true; + config->gaps_outer.top = amount; + } + if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal") + || !strcasecmp(argv[0], "right")) { + valid = true; + config->gaps_outer.right = amount; + } + if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical") + || !strcasecmp(argv[0], "bottom")) { + valid = true; + config->gaps_outer.bottom = amount; + } + if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal") + || !strcasecmp(argv[0], "left")) { + valid = true; + config->gaps_outer.left = amount; + } + } + if (!valid) { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected %s", expected_defaults); } + prevent_invalid_outer_gaps(); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static void configure_gaps(struct sway_workspace *ws, void *_data) { - struct gaps_data *data = _data; - int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer; - - switch (data->operation) { +static void apply_gaps_op(int *prop, enum gaps_op op, int amount) { + switch (op) { case GAPS_OP_SET: - *prop = data->amount; + *prop = amount; break; case GAPS_OP_ADD: - *prop += data->amount; + *prop += amount; break; case GAPS_OP_SUBTRACT: - *prop -= data->amount; + *prop -= amount; break; } +} + +static void configure_gaps(struct sway_workspace *ws, void *_data) { + // Apply operation to gaps + struct gaps_data *data = _data; + if (data->inner) { + apply_gaps_op(&ws->gaps_inner, data->operation, data->amount); + } + if (data->outer.top) { + apply_gaps_op(&(ws->gaps_outer.top), data->operation, data->amount); + } + if (data->outer.right) { + apply_gaps_op(&(ws->gaps_outer.right), data->operation, data->amount); + } + if (data->outer.bottom) { + apply_gaps_op(&(ws->gaps_outer.bottom), data->operation, data->amount); + } + if (data->outer.left) { + apply_gaps_op(&(ws->gaps_outer.left), data->operation, data->amount); + } + // Prevent invalid gaps configurations. if (ws->gaps_inner < 0) { ws->gaps_inner = 0; } - if (ws->gaps_outer < -ws->gaps_inner) { - ws->gaps_outer = -ws->gaps_inner; - } + prevent_invalid_outer_gaps(); arrange_workspace(ws); } -// gaps inner|outer current|all set|plus|minus <px> +// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all +// set|plus|minus <px> +static const char *expected_runtime = "'gaps inner|outer|horizontal|vertical|" + "top|right|bottom|left current|all set|plus|minus <px>'"; static struct cmd_results *gaps_set_runtime(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); if (error) { @@ -93,15 +148,24 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { "Can't run this command while there's no outputs connected."); } - struct gaps_data data; + struct gaps_data data = {0}; if (strcasecmp(argv[0], "inner") == 0) { data.inner = true; - } else if (strcasecmp(argv[0], "outer") == 0) { - data.inner = false; } else { + data.outer.top = !strcasecmp(argv[0], "outer") || + !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "top"); + data.outer.right = !strcasecmp(argv[0], "outer") || + !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "right"); + data.outer.bottom = !strcasecmp(argv[0], "outer") || + !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "bottom"); + data.outer.left = !strcasecmp(argv[0], "outer") || + !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "left"); + } + if (!data.inner && !data.outer.top && !data.outer.right && + !data.outer.bottom && !data.outer.left) { return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + "Expected %s", expected_runtime); } bool all; @@ -111,7 +175,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { all = true; } else { return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + "Expected %s", expected_runtime); } if (strcasecmp(argv[2], "set") == 0) { @@ -122,14 +186,14 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { data.operation = GAPS_OP_SUBTRACT; } else { return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + "Expected %s", expected_runtime); } char *end; data.amount = strtol(argv[3], &end, 10); if (strlen(end) && strcasecmp(end, "px") != 0) { return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + "Expected %s", expected_runtime); } if (all) { @@ -141,8 +205,10 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -// gaps inner|outer <px> - sets defaults for workspaces -// gaps inner|outer current|all set|plus|minus <px> - runtime only +// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces +// gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only +// <dir> = horizontal|vertical +// <side> = top|right|bottom|left struct cmd_results *cmd_gaps(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); if (error) { @@ -159,9 +225,8 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { } if (config_loading) { return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer <px>'"); + "Expected %s", expected_defaults); } return cmd_results_new(CMD_INVALID, "gaps", - "Expected 'gaps inner|outer <px>' or " - "'gaps inner|outer current|all set|plus|minus <px>'"); + "Expected %s or %s", expected_runtime, expected_defaults); } diff --git a/sway/commands/input.c b/sway/commands/input.c index c50926a8..b5765c38 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -22,6 +22,7 @@ static struct cmd_handler input_handlers[] = { { "repeat_delay", input_cmd_repeat_delay }, { "repeat_rate", input_cmd_repeat_rate }, { "scroll_button", input_cmd_scroll_button }, + { "scroll_factor", input_cmd_scroll_factor }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, { "tap_button_map", input_cmd_tap_button_map }, diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c index df487b1c..efd81ee6 100644 --- a/sway/commands/input/pointer_accel.c +++ b/sway/commands/input/pointer_accel.c @@ -1,8 +1,10 @@ +#include <math.h> #include <stdlib.h> #include <string.h> #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { struct cmd_results *error = NULL; @@ -15,8 +17,11 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { "pointer_accel", "No input device defined."); } - float pointer_accel = atof(argv[0]); - if (pointer_accel < -1 || pointer_accel > 1) { + float pointer_accel = parse_float(argv[0]); + if (isnan(pointer_accel)) { + return cmd_results_new(CMD_INVALID, "pointer_accel", + "Invalid pointer accel; expected float."); + } if (pointer_accel < -1 || pointer_accel > 1) { return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]"); } diff --git a/sway/commands/input/scroll_factor.c b/sway/commands/input/scroll_factor.c new file mode 100644 index 00000000..52d943b0 --- /dev/null +++ b/sway/commands/input/scroll_factor.c @@ -0,0 +1,32 @@ +#include <errno.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_scroll_factor(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scroll_factor", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *ic = config->handler_context.input_config; + if (!ic) { + return cmd_results_new(CMD_FAILURE, + "scroll_factor", "No input device defined."); + } + + float scroll_factor = parse_float(argv[0]); + if (isnan(scroll_factor)) { + return cmd_results_new(CMD_INVALID, "scroll_factor", + "Invalid scroll factor; expected float."); + } else if (scroll_factor < 0) { + return cmd_results_new(CMD_INVALID, "scroll_factor", + "Scroll factor cannot be negative."); + } + ic->scroll_factor = scroll_factor; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c index 669b4ea9..a939c72f 100644 --- a/sway/commands/input/xkb_capslock.c +++ b/sway/commands/input/xkb_capslock.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_xkb_capslock(int argc, char **argv) { struct cmd_results *error = NULL; @@ -15,14 +16,7 @@ struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) { "No input device defined."); } - if (strcasecmp(argv[0], "enabled") == 0) { - ic->xkb_capslock = 1; - } else if (strcasecmp(argv[0], "disabled") == 0) { - ic->xkb_capslock = 0; - } else { - return cmd_results_new(CMD_INVALID, "xkb_capslock", - "Expected 'xkb_capslock <enabled|disabled>'"); - } + ic->xkb_capslock = parse_boolean(argv[0], false); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index 1367da44..2e962c5b 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.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_xkb_numlock(int argc, char **argv) { struct cmd_results *error = NULL; @@ -15,14 +16,7 @@ struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) { "No input device defined."); } - if (strcasecmp(argv[0], "enabled") == 0) { - ic->xkb_numlock = 1; - } else if (strcasecmp(argv[0], "disabled") == 0) { - ic->xkb_numlock = 0; - } else { - return cmd_results_new(CMD_INVALID, "xkb_numlock", - "Expected 'xkb_numlock <enabled|disabled>'"); - } + ic->xkb_numlock = parse_boolean(argv[0], false); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 8635b309..a90d578e 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -404,13 +404,10 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis, con->width += grow_width; con->height += grow_height; - if (con->view) { - struct sway_view *view = con->view; - view->x += grow_x; - view->y += grow_y; - view->width += grow_width; - view->height += grow_height; - } + con->content_x += grow_x; + con->content_y += grow_y; + con->content_width += grow_width; + con->content_height += grow_height; arrange_container(con); @@ -499,7 +496,7 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, } if (height->unit == RESIZE_UNIT_PX) { resize_tiled(con, height->amount - con->height, - RESIZE_AXIS_HORIZONTAL); + RESIZE_AXIS_VERTICAL); } } @@ -511,25 +508,46 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, */ static struct cmd_results *resize_set_floating(struct sway_container *con, struct resize_amount *width, struct resize_amount *height) { - int min_width, max_width, min_height, max_height; + int min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0; calculate_constraints(&min_width, &max_width, &min_height, &max_height); - width->amount = fmax(min_width, fmin(width->amount, max_width)); - height->amount = fmax(min_height, fmin(height->amount, max_height)); - int grow_width = width->amount - con->width; - int grow_height = height->amount - con->height; - con->x -= grow_width / 2; - con->y -= grow_height / 2; - con->width = width->amount; - con->height = height->amount; - - if (con->view) { - struct sway_view *view = con->view; - view->x -= grow_width / 2; - view->y -= grow_height / 2; - view->width += grow_width; - view->height += grow_height; + + if (width->amount) { + if (width->unit == RESIZE_UNIT_PPT || + width->unit == RESIZE_UNIT_DEFAULT) { + // Convert to px + width->amount = con->workspace->width * width->amount / 100; + width->unit = RESIZE_UNIT_PX; + } + if (width->unit == RESIZE_UNIT_PX) { + width->amount = fmax(min_width, fmin(width->amount, max_width)); + grow_width = width->amount - con->width; + + con->x -= grow_width / 2; + con->width = width->amount; + } } + if (height->amount) { + if (height->unit == RESIZE_UNIT_PPT || + height->unit == RESIZE_UNIT_DEFAULT) { + // Convert to px + height->amount = con->workspace->height * height->amount / 100; + height->unit = RESIZE_UNIT_PX; + } + if (height->unit == RESIZE_UNIT_PX) { + height->amount = fmax(min_height, fmin(height->amount, max_height)); + grow_height = height->amount - con->height; + + con->y -= grow_height / 2; + con->height = height->amount; + } + } + + con->content_x -= grow_width / 2; + con->content_y -= grow_height / 2; + con->content_width += grow_width; + con->content_height += grow_height; + arrange_container(con); return cmd_results_new(CMD_SUCCESS, NULL, NULL); @@ -538,34 +556,45 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, /** * resize set <args> * - * args: <width> [px|ppt] <height> [px|ppt] + * args: [width] <width> [px|ppt] + * : height <height> [px|ppt] + * : [width] <width> [px|ppt] [height] <height> [px|ppt] */ static struct cmd_results *cmd_resize_set(int argc, char **argv) { struct cmd_results *error; - if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 1))) { return error; } - const char *usage = "Expected 'resize set <width> <height>'"; + const char *usage = "Expected 'resize set [width] <width> [px|ppt]' or " + "'resize set height <height> [px|ppt]' or " + "'resize set [width] <width> [px|ppt] [height] <height> [px|ppt]'"; // Width - struct resize_amount width; - int num_consumed_args = parse_resize_amount(argc, argv, &width); - argc -= num_consumed_args; - argv += num_consumed_args; - if (width.unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); + struct resize_amount width = {0}; + if (argc >= 2 && !strcmp(argv[0], "width") && strcmp(argv[1], "height")) { + argc--; argv++; } - if (!argc) { - return cmd_results_new(CMD_INVALID, "resize", usage); + if (strcmp(argv[0], "height")) { + int num_consumed_args = parse_resize_amount(argc, argv, &width); + argc -= num_consumed_args; + argv += num_consumed_args; + if (width.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize set", usage); + } } // Height - struct resize_amount height; - num_consumed_args = parse_resize_amount(argc, argv, &height); - argc -= num_consumed_args; - argv += num_consumed_args; - if (height.unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); + struct resize_amount height = {0}; + if (argc) { + if (argc >= 2 && !strcmp(argv[0], "height")) { + argc--; argv++; + } + int num_consumed_args = parse_resize_amount(argc, argv, &height); + argc -= num_consumed_args; + argv += num_consumed_args; + if (width.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize set", usage); + } } // If 0, don't resize that dimension diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c index 11f5a08c..a0ddf3ef 100644 --- a/sway/commands/seat/fallback.c +++ b/sway/commands/seat/fallback.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 *seat_cmd_fallback(int argc, char **argv) { struct cmd_results *error = NULL; @@ -16,16 +17,8 @@ struct cmd_results *seat_cmd_fallback(int argc, char **argv) { } struct seat_config *new_config = new_seat_config(current_seat_config->name); - - if (strcasecmp(argv[0], "true") == 0) { - new_config->fallback = 1; - } else if (strcasecmp(argv[0], "false") == 0) { - new_config->fallback = 0; - } else { - free_seat_config(new_config); - return cmd_results_new(CMD_INVALID, "fallback", - "Expected 'fallback <true|false>'"); - } + + new_config->fallback = parse_boolean(argv[0], false); if (!config->validating) { apply_seat_config(new_config); diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index 273905df..f14b6760 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -6,6 +6,7 @@ #include "sway/tree/container.h" #include "log.h" #include "stringop.h" +#include "util.h" struct cmd_results *cmd_smart_gaps(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "smart_gaps", EXPECTED_AT_LEAST, 1); @@ -14,14 +15,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { return error; } - if (strcmp(argv[0], "on") == 0) { - config->smart_gaps = true; - } else if (strcmp(argv[0], "off") == 0) { - config->smart_gaps = false; - } else { - return cmd_results_new(CMD_INVALID, "smart_gaps", - "Expected 'smart_gaps <on|off>' "); - } + config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); arrange_root(); diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index f18322b7..7cd358a4 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -9,6 +9,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" +#include "util.h" struct cmd_results *cmd_sticky(int argc, char **argv) { struct cmd_results *error = NULL; @@ -26,21 +27,9 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { "Can't set sticky on a tiled container"); } - bool wants_sticky; - if (strcasecmp(argv[0], "enable") == 0) { - wants_sticky = true; - } else if (strcasecmp(argv[0], "disable") == 0) { - wants_sticky = false; - } else if (strcasecmp(argv[0], "toggle") == 0) { - wants_sticky = !container->is_sticky; - } else { - return cmd_results_new(CMD_FAILURE, "sticky", - "Expected 'sticky <enable|disable|toggle>'"); - } - - container->is_sticky = wants_sticky; + container->is_sticky = parse_boolean(argv[0], container->is_sticky); - if (wants_sticky) { + if (container->is_sticky) { // move container to active workspace struct sway_workspace *active_workspace = output_get_active_workspace(container->workspace->output); diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 23e8d583..08860264 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -144,19 +144,18 @@ static void container_swap(struct sway_container *con1, } } -static bool test_con_id(struct sway_container *container, void *con_id) { - return container->node.id == (size_t)con_id; +static bool test_con_id(struct sway_container *container, void *data) { + size_t *con_id = data; + return container->node.id == *con_id; } -static bool test_id(struct sway_container *container, void *id) { -#ifdef HAVE_XWAYLAND - xcb_window_t *wid = id; +#if HAVE_XWAYLAND +static bool test_id(struct sway_container *container, void *data) { + xcb_window_t *wid = data; return (container->view && container->view->type == SWAY_VIEW_XWAYLAND && container->view->wlr_xwayland_surface->window_id == *wid); -#else - return false; -#endif } +#endif static bool test_mark(struct sway_container *container, void *mark) { if (container->marks->length) { @@ -181,19 +180,19 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } struct sway_container *current = config->handler_context.container; - struct sway_container *other; + struct sway_container *other = NULL; char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); - other = root_find_container(test_id, (void *)&id); + other = root_find_container(test_id, &id); #endif } else if (strcasecmp(argv[2], "con_id") == 0) { size_t con_id = atoi(value); - other = root_find_container(test_con_id, (void *)con_id); + other = root_find_container(test_con_id, &con_id); } else if (strcasecmp(argv[2], "mark") == 0) { - other = root_find_container(test_mark, (void *)value); + other = root_find_container(test_mark, value); } else { free(value); return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 5abbb676..92118ecf 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -21,18 +21,105 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) { return NULL; } wsc->workspace = strdup(ws_name); + wsc->outputs = create_list(); wsc->gaps_inner = INT_MIN; - wsc->gaps_outer = INT_MIN; + wsc->gaps_outer.top = INT_MIN; + wsc->gaps_outer.right = INT_MIN; + wsc->gaps_outer.bottom = INT_MIN; + wsc->gaps_outer.left = INT_MIN; list_add(config->workspace_configs, wsc); return wsc; } void free_workspace_config(struct workspace_config *wsc) { free(wsc->workspace); - free(wsc->output); + free_flat_list(wsc->outputs); free(wsc); } +static void prevent_invalid_outer_gaps(struct workspace_config *wsc) { + if (wsc->gaps_outer.top != INT_MIN && + wsc->gaps_outer.top < -wsc->gaps_inner) { + wsc->gaps_outer.top = -wsc->gaps_inner; + } + if (wsc->gaps_outer.right != INT_MIN && + wsc->gaps_outer.right < -wsc->gaps_inner) { + wsc->gaps_outer.right = -wsc->gaps_inner; + } + if (wsc->gaps_outer.bottom != INT_MIN && + wsc->gaps_outer.bottom < -wsc->gaps_inner) { + wsc->gaps_outer.bottom = -wsc->gaps_inner; + } + if (wsc->gaps_outer.left != INT_MIN && + wsc->gaps_outer.left < -wsc->gaps_inner) { + wsc->gaps_outer.left = -wsc->gaps_inner; + } +} + +static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, + int gaps_location) { + const char *expected = "Expected 'workspace <name> gaps " + "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, + gaps_location + 3))) { + return error; + } + char *ws_name = join_args(argv, argc - 3); + struct workspace_config *wsc = workspace_config_find_or_create(ws_name); + free(ws_name); + if (!wsc) { + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Unable to allocate workspace output"); + } + + char *end; + int amount = strtol(argv[gaps_location + 2], &end, 10); + if (strlen(end)) { + free(end); + return cmd_results_new(CMD_FAILURE, "workspace gaps", expected); + } + + bool valid = false; + char *type = argv[gaps_location + 1]; + if (!strcasecmp(type, "inner")) { + valid = true; + wsc->gaps_inner = (amount >= 0) ? amount : 0; + } else { + if (!strcasecmp(type, "outer") || !strcasecmp(type, "vertical") + || !strcasecmp(type, "top")) { + valid = true; + wsc->gaps_outer.top = amount; + } + if (!strcasecmp(type, "outer") || !strcasecmp(type, "horizontal") + || !strcasecmp(type, "right")) { + valid = true; + wsc->gaps_outer.right = amount; + } + if (!strcasecmp(type, "outer") || !strcasecmp(type, "vertical") + || !strcasecmp(type, "bottom")) { + valid = true; + wsc->gaps_outer.bottom = amount; + } + if (!strcasecmp(type, "outer") || !strcasecmp(type, "horizontal") + || !strcasecmp(type, "left")) { + valid = true; + wsc->gaps_outer.left = amount; + } + } + if (!valid) { + return cmd_results_new(CMD_INVALID, "workspace gaps", expected); + } + + // Prevent invalid gaps configurations. + if (wsc->gaps_inner != INT_MIN && wsc->gaps_inner < 0) { + wsc->gaps_inner = 0; + } + prevent_invalid_outer_gaps(wsc); + + return error; +} + struct cmd_results *cmd_workspace(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { @@ -55,56 +142,24 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } } if (output_location >= 0) { - if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { + if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, + output_location + 2))) { return error; } - char *ws_name = join_args(argv, argc - 2); + char *ws_name = join_args(argv, output_location); struct workspace_config *wsc = workspace_config_find_or_create(ws_name); free(ws_name); if (!wsc) { return cmd_results_new(CMD_FAILURE, "workspace output", "Unable to allocate workspace output"); } - free(wsc->output); - wsc->output = strdup(argv[output_location + 1]); + for (int i = output_location + 1; i < argc; ++i) { + list_add(wsc->outputs, strdup(argv[i])); + } } else if (gaps_location >= 0) { - if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { + if ((error = cmd_workspace_gaps(argc, argv, gaps_location))) { return error; } - char *ws_name = join_args(argv, argc - 3); - struct workspace_config *wsc = workspace_config_find_or_create(ws_name); - free(ws_name); - if (!wsc) { - return cmd_results_new(CMD_FAILURE, "workspace gaps", - "Unable to allocate workspace output"); - } - int *prop = NULL; - if (strcasecmp(argv[gaps_location + 1], "inner") == 0) { - prop = &wsc->gaps_inner; - } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) { - prop = &wsc->gaps_outer; - } else { - return cmd_results_new(CMD_FAILURE, "workspace gaps", - "Expected 'workspace <ws> gaps inner|outer <px>'"); - } - char *end; - int val = strtol(argv[gaps_location + 2], &end, 10); - - if (strlen(end)) { - free(end); - return cmd_results_new(CMD_FAILURE, "workspace gaps", - "Expected 'workspace <ws> gaps inner|outer <px>'"); - } - *prop = val; - - // Prevent invalid gaps configurations. - if (wsc->gaps_inner < 0) { - wsc->gaps_inner = 0; - } - if (wsc->gaps_outer < -wsc->gaps_inner) { - wsc->gaps_outer = -wsc->gaps_inner; - } - } else { if (config->reading || !config->active) { return cmd_results_new(CMD_DEFER, "workspace", NULL); diff --git a/sway/commands/ws_auto_back_and_forth.c b/sway/commands/ws_auto_back_and_forth.c index 2485db35..adb851c2 100644 --- a/sway/commands/ws_auto_back_and_forth.c +++ b/sway/commands/ws_auto_back_and_forth.c @@ -1,12 +1,14 @@ #include <string.h> #include <strings.h> #include "sway/commands.h" +#include "util.h" struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { return error; } - config->auto_back_and_forth = !strcasecmp(argv[0], "yes"); + config->auto_back_and_forth = + parse_boolean(argv[0], config->auto_back_and_forth); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/config.c b/sway/config.c index 7ef3ef38..c1320acf 100644 --- a/sway/config.c +++ b/sway/config.c @@ -220,7 +220,7 @@ static void config_defaults(struct sway_config *config) { config->floating_minimum_height = 50; // Flags - config->focus_follows_mouse = true; + config->focus_follows_mouse = FOLLOWS_YES; config->mouse_warping = WARP_OUTPUT; config->focus_wrapping = WRAP_YES; config->validating = false; @@ -234,7 +234,10 @@ static void config_defaults(struct sway_config *config) { config->smart_gaps = false; config->gaps_inner = 0; - config->gaps_outer = 0; + config->gaps_outer.top = 0; + config->gaps_outer.right = 0; + config->gaps_outer.bottom = 0; + config->gaps_outer.left = 0; if (!(config->active_bar_modifiers = create_list())) goto cleanup; @@ -254,7 +257,6 @@ static void config_defaults(struct sway_config *config) { // border colors set_color(config->border_colors.focused.border, 0x4C7899); - set_color(config->border_colors.focused.border, 0x4C7899); set_color(config->border_colors.focused.background, 0x285577); set_color(config->border_colors.focused.text, 0xFFFFFFFF); set_color(config->border_colors.focused.indicator, 0x2E9EF4); diff --git a/sway/config/bar.c b/sway/config/bar.c index 94405a92..7009d0a0 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -46,6 +46,7 @@ void free_bar_config(struct bar_config *bar) { free(bar->position); free(bar->hidden_state); free(bar->status_command); + free(bar->swaybar_command); free(bar->font); free(bar->separator_symbol); for (int i = 0; i < bar->bindings->length; i++) { diff --git a/sway/config/input.c b/sway/config/input.c index 794d5194..d5d2d90b 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -29,6 +29,7 @@ struct input_config *new_input_config(const char* identifier) { input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; input->pointer_accel = FLT_MIN; + input->scroll_factor = FLT_MIN; input->scroll_button = INT_MIN; input->scroll_method = INT_MIN; input->left_handed = INT_MIN; @@ -68,6 +69,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->pointer_accel != FLT_MIN) { dst->pointer_accel = src->pointer_accel; } + if (src->scroll_factor != FLT_MIN) { + dst->scroll_factor = src->scroll_factor; + } if (src->repeat_delay != INT_MIN) { dst->repeat_delay = src->repeat_delay; } diff --git a/sway/config/output.c b/sway/config/output.c index 2b041353..07543e3c 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -326,6 +326,7 @@ void free_output_config(struct output_config *oc) { free(oc->name); free(oc->background); free(oc->background_option); + free(oc->background_fallback); free(oc); } diff --git a/sway/criteria.c b/sway/criteria.c index 2f9992e9..3393852c 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -21,7 +21,7 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->app_id && !criteria->con_mark && !criteria->con_id -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND && !criteria->class && !criteria->id && !criteria->instance @@ -35,18 +35,19 @@ bool criteria_is_empty(struct criteria *criteria) { } void criteria_destroy(struct criteria *criteria) { + free(criteria->raw); + free(criteria->cmdlist); + free(criteria->target); pcre_free(criteria->title); pcre_free(criteria->shell); pcre_free(criteria->app_id); -#ifdef HAVE_XWAYLAND + pcre_free(criteria->con_mark); +#if HAVE_XWAYLAND pcre_free(criteria->class); pcre_free(criteria->instance); pcre_free(criteria->window_role); #endif - pcre_free(criteria->con_mark); free(criteria->workspace); - free(criteria->cmdlist); - free(criteria->raw); free(criteria); } @@ -54,7 +55,7 @@ static int regex_cmp(const char *item, const pcre *regex) { return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND static bool view_has_window_type(struct sway_view *view, enum atom_name name) { if (view->type != SWAY_VIEW_XWAYLAND) { return false; @@ -139,7 +140,7 @@ static bool criteria_matches_view(struct criteria *criteria, } } -#ifdef HAVE_XWAYLAND +#if 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) { @@ -275,7 +276,7 @@ static bool generate_regex(pcre **regex, char *value) { return true; } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND static enum atom_name parse_window_type(const char *type) { if (strcasecmp(type, "normal") == 0) { return NET_WM_WINDOW_TYPE_NORMAL; @@ -307,7 +308,7 @@ enum criteria_token { T_CON_ID, T_CON_MARK, T_FLOATING, -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND T_CLASS, T_ID, T_INSTANCE, @@ -330,7 +331,7 @@ 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 +#if HAVE_XWAYLAND } else if (strcmp(name, "class") == 0) { return T_CLASS; } else if (strcmp(name, "id") == 0) { @@ -401,7 +402,7 @@ static char *get_focused_prop(enum criteria_token token) { snprintf(id_str, id_size, "%zu", id); value = id_str; break; -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND case T_CLASS: value = view_get_class(view); break; @@ -473,7 +474,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_CON_MARK: generate_regex(&criteria->con_mark, effective_value); break; -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND case T_CLASS: generate_regex(&criteria->class, effective_value); break; @@ -576,7 +577,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; struct criteria *criteria = calloc(1, sizeof(struct criteria)); -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND criteria->window_type = ATOM_LAST; // default value #endif char *name = NULL, *value = NULL; diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index 771b58fe..d8dd0240 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -28,8 +28,8 @@ void desktop_damage_box(struct wlr_box *box) { void desktop_damage_view(struct sway_view *view) { desktop_damage_whole_container(view->container); struct wlr_box box = { - .x = view->container->current.view_x - view->geometry.x, - .y = view->container->current.view_y - view->geometry.y, + .x = view->container->current.content_x - view->geometry.x, + .y = view->container->current.content_y - view->geometry.y, .width = view->surface->current.width, .height = view->surface->current.height, }; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d48ddef3..d649100f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -160,12 +160,12 @@ void output_view_for_each_surface(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.view_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->wlr_output->lx - view->geometry.x, - .oy = view->container->current.view_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->wlr_output->ly - view->geometry.y, - .width = view->container->current.view_width, - .height = view->container->current.view_height, + .width = view->container->current.content_width, + .height = view->container->current.content_height, .rotation = 0, // TODO }; @@ -179,12 +179,12 @@ void output_view_for_each_popup(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.view_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->wlr_output->lx - view->geometry.x, - .oy = view->container->current.view_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->wlr_output->ly - view->geometry.y, - .width = view->container->current.view_width, - .height = view->container->current.view_height, + .width = view->container->current.content_width, + .height = view->container->current.content_height, .rotation = 0, // TODO }; @@ -204,7 +204,7 @@ void output_layer_for_each_surface(struct sway_output *output, } } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND void output_unmanaged_for_each_surface(struct sway_output *output, struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, void *user_data) { @@ -274,7 +274,7 @@ static void output_for_each_surface(struct sway_output *output, for_each_surface_container_iterator(floater, &data); } } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, iterator, user_data); #endif @@ -289,7 +289,7 @@ static void output_for_each_surface(struct sway_output *output, workspace_for_each_container(workspace, for_each_surface_container_iterator, &data); -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, iterator, user_data); #endif @@ -469,23 +469,14 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) { wlr_output_damage_add_box(output->damage, &box); } -static void output_damage_whole_container_iterator(struct sway_container *con, - void *data) { - if (!sway_assert(con->view, "expected a view")) { - return; - } - struct sway_output *output = data; - output_damage_view(output, con->view, true); -} - void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.con_x - output->wlr_output->lx - 1, - .y = con->current.con_y - output->wlr_output->ly - 1, - .width = con->current.con_width + 2, - .height = con->current.con_height + 2, + .x = con->current.x - output->wlr_output->lx - 1, + .y = con->current.y - output->wlr_output->ly - 1, + .width = con->current.width + 2, + .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index cf6da682..8d4a701b 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -140,7 +140,7 @@ static void render_layer(struct sway_output *output, render_surface_iterator, &data); } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND static void render_unmanaged(struct sway_output *output, pixman_region32_t *damage, struct wl_list *unmanaged) { struct render_data data = { @@ -211,9 +211,9 @@ static void render_view_toplevels(struct sway_view *view, .alpha = alpha, }; // Render all toplevels without descending into popups - double ox = view->container->current.view_x - + double ox = view->container->current.content_x - output->wlr_output->lx - view->geometry.x; - double oy = view->container->current.view_y - + double oy = view->container->current.content_y - output->wlr_output->ly - view->geometry.y; output_surface_for_each_surface(output, view->surface, ox, oy, render_surface_iterator, &data); @@ -247,9 +247,9 @@ static void render_saved_view(struct sway_view *view, return; } struct wlr_box box = { - .x = view->container->current.view_x - output->wlr_output->lx - + .x = view->container->current.content_x - output->wlr_output->lx - view->saved_geometry.x, - .y = view->container->current.view_y - output->wlr_output->ly - + .y = view->container->current.content_y - output->wlr_output->ly - view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, @@ -300,10 +300,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (state->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = state->con_x; - box.y = state->view_y; + box.x = state->x; + box.y = state->content_y; box.width = state->border_thickness; - box.height = state->view_height; + box.height = state->content_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -319,10 +319,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = state->view_x + state->view_width; - box.y = state->view_y; + box.x = state->content_x + state->content_width; + box.y = state->content_y; box.width = state->border_thickness; - box.height = state->view_height; + box.height = state->content_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -334,9 +334,9 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = state->con_x; - box.y = state->view_y + state->view_height; - box.width = state->con_width; + box.x = state->x; + box.y = state->content_y + state->content_height; + box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); @@ -585,9 +585,9 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = state->con_x; - box.y = state->con_y; - box.width = state->con_width; + box.x = state->x; + box.y = state->y; + box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -641,8 +641,8 @@ static void render_containers_linear(struct sway_output *output, } if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, state->con_x, - state->con_y, state->con_width, colors, + render_titlebar(output, damage, child, state->x, + state->y, state->width, colors, title_texture, marks_texture); } else if (state->border == B_PIXEL) { render_top_border(output, damage, child, colors); @@ -696,7 +696,7 @@ static void render_containers_tabbed(struct sway_output *output, marks_texture = child->marks_unfocused; } - int x = cstate->con_x + tab_width * i; + int x = cstate->x + tab_width * i; // Make last tab use the remaining width of the parent if (i == parent->children->length - 1) { @@ -801,10 +801,10 @@ static void render_container(struct sway_output *output, struct parent_data data = { .layout = con->current.layout, .box = { - .x = con->current.con_x, - .y = con->current.con_y, - .width = con->current.con_width, - .height = con->current.con_height, + .x = con->current.x, + .y = con->current.y, + .width = con->current.width, + .height = con->current.height, }, .children = con->current.children, .focused = focused, @@ -853,8 +853,8 @@ static void render_floating_container(struct sway_output *soutput, } if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->current.con_x, - con->current.con_y, con->current.con_width, colors, + render_titlebar(soutput, damage, con, con->current.x, + con->current.y, con->current.width, colors, title_texture, marks_texture); } else if (con->current.border == B_PIXEL) { render_top_border(soutput, damage, con, colors); @@ -966,7 +966,7 @@ void output_render(struct sway_output *output, struct timespec *when, render_floating_container(output, damage, floater); } } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND render_unmanaged(output, damage, &root->xwayland_unmanaged); #endif } else { @@ -986,7 +986,7 @@ void output_render(struct sway_output *output, struct timespec *when, render_workspace(output, damage, workspace, workspace->current.focused); render_floating(output, damage); -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND render_unmanaged(output, damage, &root->xwayland_unmanaged); #endif render_layer(output, damage, @@ -1019,6 +1019,7 @@ renderer_end: } wlr_renderer_scissor(renderer, NULL); + wlr_output_render_software_cursors(wlr_output, damage); wlr_renderer_end(renderer); if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { return; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 44156d41..bf0038b4 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -130,10 +130,10 @@ static void copy_container_state(struct sway_container *container, struct sway_container_state *state = &instruction->container_state; state->layout = container->layout; - state->con_x = container->x; - state->con_y = container->y; - state->con_width = container->width; - state->con_height = container->height; + state->x = container->x; + state->y = container->y; + state->width = container->width; + state->height = container->height; state->is_fullscreen = container->is_fullscreen; state->parent = container->parent; state->workspace = container->workspace; @@ -143,14 +143,12 @@ static void copy_container_state(struct sway_container *container, state->border_left = container->border_left; state->border_right = container->border_right; state->border_bottom = container->border_bottom; + state->content_x = container->content_x; + state->content_y = container->content_y; + state->content_width = container->content_width; + state->content_height = container->content_height; - if (container->view) { - struct sway_view *view = container->view; - state->view_x = view->x; - state->view_y = view->y; - state->view_width = view->width; - state->view_height = view->height; - } else { + if (!container->view) { state->children = create_list(); list_cat(state->children, container->children); } @@ -217,8 +215,8 @@ static void apply_container_state(struct sway_container *container, desktop_damage_whole_container(container); if (view && view->saved_buffer) { struct wlr_box box = { - .x = container->current.view_x - view->saved_geometry.x, - .y = container->current.view_y - view->saved_geometry.y, + .x = container->current.content_x - view->saved_geometry.x, + .y = container->current.content_y - view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, }; @@ -245,8 +243,8 @@ static void apply_container_state(struct sway_container *container, if (view && view->surface) { struct wlr_surface *surface = view->surface; struct wlr_box box = { - .x = container->current.view_x - view->geometry.x, - .y = container->current.view_y - view->geometry.y, + .x = container->current.content_x - view->geometry.x, + .y = container->current.content_y - view->geometry.y, .width = surface->current.width, .height = surface->current.height, }; @@ -382,18 +380,18 @@ static bool should_configure(struct sway_node *node, } struct sway_container_state *cstate = &node->sway_container->current; struct sway_container_state *istate = &instruction->container_state; -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND // Xwayland views are position-aware and need to be reconfigured // when their position changes. if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { - if (cstate->view_x != istate->view_x || - cstate->view_y != istate->view_y) { + if (cstate->content_x != istate->content_x || + cstate->content_y != istate->content_y) { return true; } } #endif - if (cstate->view_width == istate->view_width && - cstate->view_height == istate->view_height) { + if (cstate->content_width == istate->content_width && + cstate->content_height == istate->content_height) { return false; } return true; @@ -409,10 +407,10 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_node *node = instruction->node; if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, - instruction->container_state.view_x, - instruction->container_state.view_y, - instruction->container_state.view_width, - instruction->container_state.view_height); + instruction->container_state.content_x, + instruction->container_state.content_y, + instruction->container_state.content_width, + instruction->container_state.content_height); ++transaction->num_waiting; // From here on we are rendering a saved buffer of the view, which @@ -504,8 +502,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, int width, int height) { struct sway_transaction_instruction *instruction = view->container->node.instruction; - if (instruction->container_state.view_width == width && - instruction->container_state.view_height == height) { + if (instruction->container_state.content_width == width && + instruction->container_state.content_height == height) { set_instruction_ready(instruction); } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 0b2ebc96..801dcee0 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -75,8 +75,8 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = output->lx - view->x, - .y = output->ly - view->y, + .x = output->lx - view->container->content_x, + .y = output->ly - view->container->content_y, .width = output->width, .height = output->height, }; @@ -286,9 +286,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { } else { struct wlr_box new_geo; wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); + struct sway_container *con = view->container; - if ((new_geo.width != view->width || new_geo.height != view->height) && - container_is_floating(view->container)) { + if ((new_geo.width != con->content_width || + new_geo.height != con->content_height) && + container_is_floating(con)) { // A floating view has unexpectedly sent a new size desktop_damage_view(view); view_update_size(view, new_geo.width, new_geo.height); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 692cfbf5..4bc83b8e 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -74,8 +74,8 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = output->lx - view->x, - .y = output->ly - view->y, + .x = output->lx - view->container->content_x, + .y = output->ly - view->container->content_y, .width = output->width, .height = output->height, }; @@ -283,9 +283,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { } else { struct wlr_box new_geo; wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); + struct sway_container *con = view->container; - if ((new_geo.width != view->width || new_geo.height != view->height) && - container_is_floating(view->container)) { + if ((new_geo.width != con->content_width || + new_geo.height != con->content_height) && + container_is_floating(con)) { // A floating view has unexpectedly sent a new size desktop_damage_view(view); view_update_size(view, new_geo.width, new_geo.height); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 0c41d960..1838ad32 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -332,9 +332,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { } else { struct wlr_box new_geo; get_geometry(view, &new_geo); + struct sway_container *con = view->container; - if ((new_geo.width != view->width || new_geo.height != view->height) && - container_is_floating(view->container)) { + if ((new_geo.width != con->content_width || + new_geo.height != con->content_height) && + container_is_floating(con)) { // A floating view has unexpectedly sent a new size // eg. The Firefox "Save As" dialog when downloading a file desktop_damage_view(view); @@ -432,13 +434,13 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } if (container_is_floating(view->container)) { - configure(view, view->container->current.view_x, - view->container->current.view_y, ev->width, ev->height); + configure(view, view->container->current.content_x, + view->container->current.content_y, ev->width, ev->height); } else { - configure(view, view->container->current.view_x, - view->container->current.view_y, - view->container->current.view_width, - view->container->current.view_height); + configure(view, view->container->current.content_x, + view->container->current.content_y, + view->container->current.content_width, + view->container->current.content_height); } } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c539df40..d89f64d8 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -5,6 +5,7 @@ #elif __FreeBSD__ #include <dev/evdev/input-event-codes.h> #endif +#include <float.h> #include <limits.h> #include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_xcursor_manager.h> @@ -63,7 +64,7 @@ static struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND struct wl_list *unmanaged = &root->xwayland_unmanaged; struct sway_xwayland_unmanaged *unmanaged_surface; wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { @@ -348,7 +349,7 @@ static void handle_move_tiling_motion(struct sway_seat *seat, } // Find the closest edge - size_t thickness = fmin(con->view->width, con->view->height) * 0.3; + size_t thickness = fmin(con->content_width, con->content_height) * 0.3; size_t closest_dist = INT_MAX; size_t dist; seat->op_target_edge = WLR_EDGE_NONE; @@ -374,10 +375,10 @@ static void handle_move_tiling_motion(struct sway_seat *seat, } seat->op_target_node = node; - seat->op_drop_box.x = con->view->x; - seat->op_drop_box.y = con->view->y; - seat->op_drop_box.width = con->view->width; - seat->op_drop_box.height = con->view->height; + seat->op_drop_box.x = con->content_x; + seat->op_drop_box.y = con->content_y; + seat->op_drop_box.width = con->content_width; + seat->op_drop_box.height = con->content_height; resize_box(&seat->op_drop_box, seat->op_target_edge, thickness); desktop_damage_box(&seat->op_drop_box); } @@ -498,13 +499,10 @@ static void handle_resize_floating_motion(struct sway_seat *seat, con->width += relative_grow_width; con->height += relative_grow_height; - if (con->view) { - struct sway_view *view = con->view; - view->x += relative_grow_x; - view->y += relative_grow_y; - view->width += relative_grow_width; - view->height += relative_grow_height; - } + con->content_x += relative_grow_x; + con->content_y += relative_grow_y; + con->content_width += relative_grow_width; + con->content_height += relative_grow_height; arrange_container(con); } @@ -637,7 +635,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, cursor->previous.y = cursor->cursor->y; cursor->previous.node = node; - if (node && config->focus_follows_mouse) { + if (node && (config->focus_follows_mouse == FOLLOWS_YES || + config->focus_follows_mouse == FOLLOWS_ALWAYS)) { struct sway_node *focus = seat_get_focus(seat); if (focus && node->type == N_WORKSPACE) { // Only follow the mouse if it would move to a new output @@ -652,9 +651,10 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, // - cursor is over a new view, i.e. entered a new window; and // - the new view is visible, i.e. not hidden in a stack or tab; and // - the seat does not have a keyboard grab - if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && + if ((!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && node != prev_node && - view_is_visible(node->sway_container->view)) { + view_is_visible(node->sway_container->view)) || + config->focus_follows_mouse == FOLLOWS_ALWAYS) { seat_set_focus(seat, node); } else { struct sway_node *next_focus = @@ -861,8 +861,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } // Handle tiling resize via border - if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && - !is_floating) { + if (cont && resize_edge && button == BTN_LEFT && + state == WLR_BUTTON_PRESSED && !is_floating) { seat_set_focus_container(seat, cont); seat_begin_resize_tiling(seat, cont, button, edge); return; @@ -871,7 +871,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle tiling resize via mod bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - if (!is_floating_or_child && mod_pressed && state == WLR_BUTTON_PRESSED) { + if (cont && !is_floating_or_child && mod_pressed && + state == WLR_BUTTON_PRESSED) { uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; if (button == btn_resize) { @@ -899,7 +900,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } // Handle beginning floating move - if (is_floating_or_child && !is_fullscreen_or_child && + 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 && state == WLR_BUTTON_PRESSED && @@ -914,7 +915,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } // Handle beginning floating resize - if (is_floating_or_child && !is_fullscreen_or_child && + if (cont && is_floating_or_child && !is_fullscreen_or_child && state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { @@ -979,6 +980,8 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { static void dispatch_cursor_axis(struct sway_cursor *cursor, struct wlr_event_pointer_axis *event) { struct sway_seat *seat = cursor->seat; + struct sway_input_device *input_device = event->device->data; + struct input_config *ic = input_device_get_config(input_device); // Determine what's under the cursor struct wlr_surface *surface = NULL; @@ -990,6 +993,8 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; bool on_border = edge != WLR_EDGE_NONE; bool on_titlebar = cont && !on_border && !surface; + float scroll_factor = + (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; // Scrolling on a tabbed or stacked title bar if (on_titlebar) { @@ -1000,7 +1005,7 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, seat_get_active_tiling_child(seat, tabcontainer); list_t *siblings = container_get_siblings(cont); int desired = list_find(siblings, active->sway_container) + - event->delta_discrete; + round(scroll_factor * event->delta_discrete); if (desired < 0) { desired = 0; } else if (desired >= siblings->length) { @@ -1024,7 +1029,8 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, } wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, event->delta, event->delta_discrete, event->source); + event->orientation, scroll_factor * event->delta, + round(scroll_factor * event->delta_discrete), event->source); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 54fdf40b..663c5140 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -92,7 +92,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { node->sway_container->view : NULL; if (view && seat_is_input_allowed(seat, view->surface)) { -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); diff --git a/sway/ipc-json.c b/sway/ipc-json.c index cf1b42a6..05e453ec 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -12,6 +12,7 @@ #include "sway/input/seat.h" #include <wlr/types/wlr_box.h> #include <wlr/types/wlr_output.h> +#include <xkbcommon/xkbcommon.h> #include "wlr-layer-shell-unstable-v1-protocol.h" static const char *ipc_json_layout_description(enum sway_container_layout l) { @@ -245,10 +246,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "marks", marks); struct wlr_box window_box = { - c->view->x - c->x, + c->content_x - c->x, (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, - c->view->width, - c->view->height + c->content_width, + c->content_height }; json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); @@ -257,7 +258,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object if (c->current.border == B_NORMAL) { deco_box.width = c->width; - deco_box.height = c->view->y - c->y; + deco_box.height = c->content_y - c->y; } json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); @@ -265,7 +266,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object struct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height}; json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry)); -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND if (c->view->type == SWAY_VIEW_XWAYLAND) { json_object_object_add(object, "window", json_object_new_int(view_get_x11_window_id(c->view))); @@ -503,6 +504,27 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_object_add(object, "type", json_object_new_string(describe_device_type(device))); + if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { + struct wlr_keyboard *keyboard = device->wlr_device->keyboard; + struct xkb_keymap *keymap = keyboard->keymap; + struct xkb_state *state = keyboard->xkb_state; + xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap); + xkb_layout_index_t layout_idx; + for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) { + bool is_active = + xkb_state_layout_index_is_active(state, + layout_idx, + XKB_STATE_LAYOUT_EFFECTIVE); + if (is_active) { + const char *layout = + xkb_keymap_layout_get_name(keymap, layout_idx); + json_object_object_add(object, "xkb_active_layout_name", + json_object_new_string(layout)); + break; + } + } + } + return object; } diff --git a/sway/meson.build b/sway/meson.build index debd7a91..14822dbd 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -136,6 +136,7 @@ sway_sources = files( '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', diff --git a/sway/server.c b/sway/server.c index f06173d1..cd3fcdf6 100644 --- a/sway/server.c +++ b/sway/server.c @@ -26,7 +26,7 @@ #include "sway/server.h" #include "sway/tree/root.h" #include "config.h" -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND #include "sway/xwayland.h" #endif @@ -94,7 +94,7 @@ bool server_init(struct sway_server *server) { setenv("XCURSOR_THEME", cursor_theme, 1); } -#ifdef HAVE_XWAYLAND +#if 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, @@ -164,7 +164,7 @@ bool server_init(struct sway_server *server) { void server_fini(struct sway_server *server) { // TODO: free sway-specific resources -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 82273ef3..45994644 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -105,14 +105,18 @@ The following commands may only be used in the configuration file. *input* <identifier> repeat\_rate <characters per second> Sets the frequency of key repeats once the repeat\_delay has passed. -*input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down - Changes the scroll method for the specified input device. - *input* <identifier> scroll\_button <button\_identifier> Sets button used for scroll\_method on\_button\_down. The button identifier can be obtained from `libinput debug-events`. If set to 0, it disables the scroll\_button on\_button\_down. +*input* <identifier> scroll\_factor <floating point value> + Changes the scroll factor for the specified input device. Scroll speed will + be scaled by the given value, which must be non-negative. + +*input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down + Changes the scroll method for the specified input device. + *input* <identifier> tap enabled|disabled Enables or disables tap for specified input device. diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 4a645837..1a11015f 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -133,9 +133,12 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *fullscreen* Toggles fullscreen for the focused view. -*gaps* inner|outer all|current set|plus|minus <amount> +*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current +set|plus|minus <amount> Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the - _current_ workspace. + _current_ workspace. _outer_ gaps can be altered per side with _top_, + _right_, _bottom_, and _left_ or per direction with _horizontal_ and + _vertical_. *layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. @@ -207,11 +210,23 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). percentage points. If the units are omitted, floating containers are resized in px and tiled containers by ppt. _amount_ will default to 10 if omitted. -*resize set* <width> [px|ppt] <height> [px|ppt] - Sets the width and height of the currently focused container to _width_ and - _height_, specified in pixels or percentage points. If the units are - omitted, floating containers are resized in px and tiled containers by ppt. - If _width_ or _height_ is 0, no resize is done on that axis. +*resize set* height <height> [px|ppt] + Sets the height of the container to _height_, specified in pixels or + percentage points. If the units are omitted, floating containers are + resized in px and tiled containers by ppt. If _height_ is 0, the container + will not be resized. + +*resize set* [width] <width> [px|ppt] + Sets the width of the container to _width_, specified in pixels or + percentage points. If the units are omitted, floating containers are + resized in px and tiled containers by ppt. If _width_ is 0, the container + will not be resized. + +*resize set* [width] <width> [px|ppt] [height] <height> [px|ppt] + Sets the width and height of the container to _width_ and _height_, + specified in pixels or percentage points. If the units are omitted, + floating containers are resized in px and tiled containers by ppt. If + _width_ or _height_ is 0, the container will not be resized on that axis. *scratchpad show* Shows a window from the scratchpad. Repeatedly using this command will @@ -404,8 +419,10 @@ The default colors are: specified direction while holding the floating modifier. Resets the command, when given no arguments. -*focus\_follows\_mouse* yes|no - If set to _yes_, moving your mouse over a window will focus that window. +*focus\_follows\_mouse* yes|no|always + If set to _yes_, moving your mouse over a window will focus that window. If + set to _always_, the window under the cursor will always be focused, even + after switching between workspaces. *focus\_wrapping* yes|no|force This option determines what to do when attempting to focus over the edge @@ -429,14 +446,16 @@ The default colors are: _focus\_wrapping force_. This is only available for convenience. Please use _focus\_wrapping_ instead when possible. -*gaps* inner|outer <amount> +*gaps* inner|outer|horizontal|vertical|top|right|bottom|left <amount> Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner affects spacing around each view and outer affects the spacing around each workspace. Outer gaps are in addition to inner gaps. To reduce or remove - outer gaps, outer gaps can be set to a negative value. + outer gaps, outer gaps can be set to a negative value. _outer_ gaps can + also be specified per side with _top_, _right_, _bottom_, and _left_ or + per direction with _horizontal_ and _vertical_. This affects new workspaces only, and is used when the workspace doesn't - have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>). + have its own gaps settings (see: workspace <ws> gaps ...). *hide\_edge\_borders* none|vertical|horizontal|both|smart|smart\_no\_gaps Hides window borders adjacent to the screen edges. Default is _none_. @@ -549,12 +568,17 @@ The default colors are: *workspace* back\_and\_forth Switches to the previously focused workspace. -*workspace* <name> gaps inner|outer <amount> +*workspace* <name> gaps inner|outer|horizontal|vertical|top|right|bottom|left +<amount> Specifies that workspace _name_ should have the given gaps settings when it is created. -*workspace* <name> output <output> - Specifies that workspace _name_ should be shown on the specified _output_. +*workspace* <name> output <outputs...> + Specifies that workspace _name_ should be shown on the specified _outputs_. + Multiple outputs can be listed and the first available will be used. If the + workspace gets placed on an output further down the list and an output that + is higher on the list becomes available, the workspace will be move to the + higher priority output. *workspace\_auto\_back\_and\_forth* yes|no When _yes_, repeating a workspace switch command will switch back to the diff --git a/sway/tree/container.c b/sway/tree/container.c index 458ed7ff..cf6f5b54 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -165,13 +165,13 @@ static struct sway_container *surface_at_view(struct sway_container *con, double return NULL; } struct sway_view *view = con->view; - double view_sx = lx - view->x + view->geometry.x; - double view_sy = ly - view->y + view->geometry.y; + double view_sx = lx - con->content_x + view->geometry.x; + double view_sy = ly - con->content_y + view->geometry.y; double _sx, _sy; struct wlr_surface *_surface = NULL; switch (view->type) { -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: _surface = wlr_surface_surface_at(view->surface, view_sx, view_sy, &_sx, &_sy); @@ -641,16 +641,18 @@ void container_init_floating(struct sway_container *con) { con->y = ws->y + (ws->height - con->height) / 2; } else { struct sway_view *view = con->view; - view->width = fmax(min_width, fmin(view->natural_width, max_width)); - view->height = fmax(min_height, fmin(view->natural_height, max_height)); - view->x = ws->x + (ws->width - view->width) / 2; - view->y = ws->y + (ws->height - view->height) / 2; + con->content_width = + fmax(min_width, fmin(view->natural_width, max_width)); + con->content_height = + fmax(min_height, fmin(view->natural_height, max_height)); + con->content_x = ws->x + (ws->width - con->content_width) / 2; + con->content_y = ws->y + (ws->height - con->content_height) / 2; // If the view's border is B_NONE then these properties are ignored. con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; - container_set_geometry_from_floating_view(con); + container_set_geometry_from_content(con); } } @@ -707,14 +709,13 @@ void container_set_floating(struct sway_container *container, bool enable) { ipc_event_window(container, "floating"); } -void container_set_geometry_from_floating_view(struct sway_container *con) { +void container_set_geometry_from_content(struct sway_container *con) { if (!sway_assert(con->view, "Expected a view")) { return; } if (!sway_assert(container_is_floating(con), "Expected a floating view")) { return; } - struct sway_view *view = con->view; size_t border_width = 0; size_t top = 0; @@ -724,10 +725,10 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { container_titlebar_height() : border_width; } - con->x = view->x - border_width; - con->y = view->y - top; - con->width = view->width + border_width * 2; - con->height = top + view->height + border_width; + con->x = con->content_x - border_width; + con->y = con->content_y - top; + con->width = con->content_width + border_width * 2; + con->height = top + con->content_height + border_width; node_set_dirty(&con->node); } @@ -756,15 +757,16 @@ void container_floating_translate(struct sway_container *con, double x_amount, double y_amount) { con->x += x_amount; con->y += y_amount; - if (con->view) { - con->view->x += x_amount; - con->view->y += y_amount; - } else { + con->content_x += x_amount; + con->content_y += y_amount; + + if (con->children) { for (int i = 0; i < con->children->length; ++i) { struct sway_container *child = con->children->items[i]; container_floating_translate(child, x_amount, y_amount); } } + node_set_dirty(&con->node); } @@ -964,10 +966,10 @@ static void surface_send_leave_iterator(struct wlr_surface *surface, void container_discover_outputs(struct sway_container *con) { struct wlr_box con_box = { - .x = con->current.con_x, - .y = con->current.con_y, - .width = con->current.con_width, - .height = con->current.con_height, + .x = con->current.x, + .y = con->current.y, + .width = con->current.width, + .height = con->current.height, }; struct sway_output *old_output = container_get_effective_output(con); @@ -1009,19 +1011,25 @@ void container_discover_outputs(struct sway_container *con) { } void container_remove_gaps(struct sway_container *c) { - if (c->current_gaps == 0) { + if (c->current_gaps.top == 0 && c->current_gaps.right == 0 && + c->current_gaps.bottom == 0 && c->current_gaps.left == 0) { return; } - c->width += c->current_gaps * 2; - c->height += c->current_gaps * 2; - c->x -= c->current_gaps; - c->y -= c->current_gaps; - c->current_gaps = 0; + c->width += c->current_gaps.left + c->current_gaps.right; + c->height += c->current_gaps.top + c->current_gaps.bottom; + c->x -= c->current_gaps.left; + c->y -= c->current_gaps.top; + + c->current_gaps.top = 0; + c->current_gaps.right = 0; + c->current_gaps.bottom = 0; + c->current_gaps.left = 0; } void container_add_gaps(struct sway_container *c) { - if (c->current_gaps > 0) { + if (c->current_gaps.top > 0 || c->current_gaps.right > 0 || + c->current_gaps.bottom > 0 || c->current_gaps.left > 0) { return; } // Linear containers don't have gaps because it'd create double gaps @@ -1054,11 +1062,15 @@ void container_add_gaps(struct sway_container *c) { struct sway_workspace *ws = c->workspace; - c->current_gaps = ws->gaps_inner; - c->x += c->current_gaps; - c->y += c->current_gaps; - c->width -= 2 * c->current_gaps; - c->height -= 2 * c->current_gaps; + c->current_gaps.top = c->y == ws->y ? ws->gaps_inner : 0; + c->current_gaps.right = ws->gaps_inner; + c->current_gaps.bottom = ws->gaps_inner; + c->current_gaps.left = c->x == ws->x ? ws->gaps_inner : 0; + + c->x += c->current_gaps.left; + c->y += c->current_gaps.top; + c->width -= c->current_gaps.left + c->current_gaps.right; + c->height -= c->current_gaps.top + c->current_gaps.bottom; } enum sway_container_layout container_parent_layout(struct sway_container *con) { diff --git a/sway/tree/output.c b/sway/tree/output.c index 2704920d..3c4614a8 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -31,6 +31,13 @@ static void restore_workspaces(struct sway_output *output) { j--; } } + + if (other->workspaces->length == 0) { + char *next = workspace_next_name(other->wlr_output->name); + struct sway_workspace *ws = workspace_create(other, next); + free(next); + ipc_event_workspace(NULL, ws, "init"); + } } // Saved workspaces diff --git a/sway/tree/root.c b/sway/tree/root.c index 9bda7c28..544d666a 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -31,7 +31,7 @@ struct sway_root *root_create(void) { node_init(&root->node, N_ROOT, root); root->output_layout = wlr_output_layout_create(); wl_list_init(&root->all_outputs); -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND wl_list_init(&root->xwayland_unmanaged); #endif wl_list_init(&root->drag_icons); diff --git a/sway/tree/view.c b/sway/tree/view.c index 1aa59e68..d7110619 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -8,7 +8,7 @@ #include <wlr/types/wlr_server_decoration.h> #include <wlr/types/wlr_xdg_decoration_v1.h> #include "config.h" -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND #include <wlr/xwayland.h> #endif #include "list.h" @@ -101,7 +101,7 @@ const char *view_get_instance(struct sway_view *view) { } return NULL; } -#ifdef HAVE_XWAYLAND +#if 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); @@ -136,7 +136,7 @@ const char *view_get_shell(struct sway_view *view) { return "xdg_shell_v6"; case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; #endif @@ -185,31 +185,33 @@ bool view_is_only_visible(struct sway_view *view) { static bool gaps_to_edge(struct sway_view *view) { struct sway_container *con = view->container; while (con) { - if (con->current_gaps > 0) { + if (con->current_gaps.top > 0 || con->current_gaps.right > 0 || + con->current_gaps.bottom > 0 || con->current_gaps.left > 0) { return true; } con = con->parent; } - return view->container->workspace->current_gaps > 0; + struct side_gaps gaps = view->container->workspace->current_gaps; + return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; } void view_autoconfigure(struct sway_view *view) { - if (!view->container->workspace) { + struct sway_container *con = view->container; + if (!con->workspace) { // Hidden in the scratchpad return; } - struct sway_output *output = view->container->workspace->output; + struct sway_output *output = con->workspace->output; - if (view->container->is_fullscreen) { - view->x = output->lx; - view->y = output->ly; - view->width = output->width; - view->height = output->height; + if (con->is_fullscreen) { + con->content_x = output->lx; + con->content_y = output->ly; + con->content_width = output->width; + con->content_height = output->height; return; } struct sway_workspace *ws = view->container->workspace; - struct sway_container *con = view->container; bool smart = config->hide_edge_borders == E_SMART || config->hide_edge_borders == E_SMART_NO_GAPS; @@ -222,15 +224,15 @@ void view_autoconfigure(struct sway_view *view) { if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_VERTICAL || (smart && !other_views && no_gaps)) { - con->border_left = con->x - con->current_gaps != ws->x; - int right_x = con->x + con->width + con->current_gaps; + con->border_left = con->x - con->current_gaps.left != ws->x; + int right_x = con->x + con->width + con->current_gaps.right; con->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_HORIZONTAL || (smart && !other_views && no_gaps)) { - con->border_top = con->y - con->current_gaps != ws->y; - int bottom_y = con->y + con->height + con->current_gaps; + con->border_top = con->y - con->current_gaps.top != ws->y; + int bottom_y = con->y + con->height + con->current_gaps.bottom; con->border_bottom = bottom_y != ws->y + ws->height; } @@ -287,10 +289,10 @@ void view_autoconfigure(struct sway_view *view) { break; } - view->x = x; - view->y = y; - view->width = width; - view->height = height; + con->content_x = x; + con->content_y = y; + con->content_width = width; + con->content_height = height; } void view_set_activated(struct sway_view *view, bool activated) { @@ -482,7 +484,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { // Check if there's a PID mapping pid_t pid; -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland_surface *surf = wlr_xwayland_surface_from_wlr_surface(view->surface); @@ -665,11 +667,11 @@ void view_update_size(struct sway_view *view, int width, int height) { "Expected a floating container")) { return; } - view->width = width; - view->height = height; - view->container->current.view_width = width; - view->container->current.view_height = height; - container_set_geometry_from_floating_view(view->container); + view->container->content_width = width; + view->container->content_height = height; + view->container->current.content_width = width; + view->container->current.content_height = height; + container_set_geometry_from_content(view->container); } static void subsurface_get_root_coords(struct sway_view_child *child, @@ -705,7 +707,8 @@ static void view_child_damage(struct sway_view_child *child, bool whole) { int sx, sy; child->impl->get_root_coords(child, &sx, &sy); desktop_damage_surface(child->surface, - child->view->x + sx, child->view->y + sy, whole); + child->view->container->content_x + sx, + child->view->container->content_y + sy, whole); } static void view_child_handle_surface_commit(struct wl_listener *listener, @@ -799,7 +802,7 @@ 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 HAVE_XWAYLAND if (wlr_surface_is_xwayland_surface(wlr_surface)) { struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_from_wlr_surface(wlr_surface); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 05cda5c0..4be63311 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -33,14 +33,15 @@ struct workspace_config *workspace_find_config(const char *ws_name) { struct sway_output *workspace_get_initial_output(const char *name) { // Check workspace configs for a workspace<->output pair struct workspace_config *wsc = workspace_find_config(name); - if (wsc && wsc->output) { - struct sway_output *output = output_by_name(wsc->output); - if (!output) { - output = output_by_identifier(wsc->output); - } - - if (output) { - return output; + if (wsc) { + for (int i = 0; i < wsc->outputs->length; i++) { + struct sway_output *output = output_by_name(wsc->outputs->items[i]); + if (!output) { + output = output_by_identifier(wsc->outputs->items[i]); + } + if (output) { + return output; + } } } // Otherwise put it on the focused output @@ -49,6 +50,21 @@ struct sway_output *workspace_get_initial_output(const char *name) { return focus->output; } +static void prevent_invalid_outer_gaps(struct sway_workspace *ws) { + if (ws->gaps_outer.top < -ws->gaps_inner) { + ws->gaps_outer.top = -ws->gaps_inner; + } + if (ws->gaps_outer.right < -ws->gaps_inner) { + ws->gaps_outer.right = -ws->gaps_inner; + } + if (ws->gaps_outer.bottom < -ws->gaps_inner) { + ws->gaps_outer.bottom = -ws->gaps_inner; + } + if (ws->gaps_outer.left < -ws->gaps_inner) { + ws->gaps_outer.left = -ws->gaps_inner; + } +} + struct sway_workspace *workspace_create(struct sway_output *output, const char *name) { if (output == NULL) { @@ -70,22 +86,41 @@ struct sway_workspace *workspace_create(struct sway_output *output, ws->floating = create_list(); ws->tiling = create_list(); ws->output_priority = create_list(); - workspace_output_add_priority(ws, output); ws->gaps_outer = config->gaps_outer; ws->gaps_inner = config->gaps_inner; if (name) { struct workspace_config *wsc = workspace_find_config(name); if (wsc) { - if (wsc->gaps_outer != INT_MIN) { - ws->gaps_outer = wsc->gaps_outer; + if (wsc->gaps_outer.top != INT_MIN) { + ws->gaps_outer.top = wsc->gaps_outer.top; + } + if (wsc->gaps_outer.right != INT_MIN) { + ws->gaps_outer.right = wsc->gaps_outer.right; + } + if (wsc->gaps_outer.bottom != INT_MIN) { + ws->gaps_outer.bottom = wsc->gaps_outer.bottom; + } + if (wsc->gaps_outer.left != INT_MIN) { + ws->gaps_outer.left = wsc->gaps_outer.left; } if (wsc->gaps_inner != INT_MIN) { ws->gaps_inner = wsc->gaps_inner; } + // Since default outer gaps can be smaller than the negation of + // workspace specific inner gaps, check outer gaps again + prevent_invalid_outer_gaps(ws); + + // Add output priorities + for (int i = 0; i < wsc->outputs->length; ++i) { + list_add(ws->output_priority, strdup(wsc->outputs->items[i])); + } } } + // If not already added, add the output to the lowest priority + workspace_output_add_priority(ws, output); + output_add_workspace(output, ws); output_sort_workspaces(output); @@ -107,8 +142,7 @@ void workspace_destroy(struct sway_workspace *workspace) { free(workspace->name); free(workspace->representation); - list_foreach(workspace->output_priority, free); - list_free(workspace->output_priority); + free_flat_list(workspace->output_priority); list_free(workspace->floating); list_free(workspace->tiling); list_free(workspace->current.floating); @@ -150,8 +184,19 @@ static bool workspace_valid_on_output(const char *output_name, char identifier[128]; struct sway_output *output = output_by_name(output_name); output_get_identifier(identifier, sizeof(identifier), output); - - return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0 || strcasecmp(identifier, output_name) == 0; + + if (!wsc) { + return true; + } + + for (int i = 0; i < wsc->outputs->length; i++) { + if (strcmp(wsc->outputs->items[i], output_name) == 0 || + strcmp(wsc->outputs->items[i], identifier) == 0) { + return true; + } + } + + return false; } static void workspace_name_from_binding(const struct sway_binding * binding, @@ -254,10 +299,19 @@ char *workspace_next_name(const char *output_name) { for (int i = 0; i < config->workspace_configs->length; ++i) { // Unlike with bindings, this does not guarantee order const struct workspace_config *wsc = config->workspace_configs->items[i]; - if (wsc->output && strcmp(wsc->output, output_name) == 0 - && workspace_by_name(wsc->workspace) == NULL) { - free(target); - target = strdup(wsc->workspace); + if (workspace_by_name(wsc->workspace)) { + continue; + } + bool found = false; + for (int j = 0; j < wsc->outputs->length; ++j) { + if (strcmp(wsc->outputs->items[j], output_name) == 0) { + found = true; + free(target); + target = strdup(wsc->workspace); + break; + } + } + if (found) { break; } } @@ -615,19 +669,25 @@ void workspace_insert_tiling(struct sway_workspace *workspace, } void workspace_remove_gaps(struct sway_workspace *ws) { - if (ws->current_gaps == 0) { + if (ws->current_gaps.top == 0 && ws->current_gaps.right == 0 && + ws->current_gaps.bottom == 0 && ws->current_gaps.left == 0) { return; } - ws->width += ws->current_gaps * 2; - ws->height += ws->current_gaps * 2; - ws->x -= ws->current_gaps; - ws->y -= ws->current_gaps; - ws->current_gaps = 0; + ws->width += ws->current_gaps.left + ws->current_gaps.right; + ws->height += ws->current_gaps.top + ws->current_gaps.bottom; + ws->x -= ws->current_gaps.left; + ws->y -= ws->current_gaps.top; + + ws->current_gaps.top = 0; + ws->current_gaps.right = 0; + ws->current_gaps.bottom = 0; + ws->current_gaps.left = 0; } void workspace_add_gaps(struct sway_workspace *ws) { - if (ws->current_gaps > 0) { + if (ws->current_gaps.top > 0 || ws->current_gaps.right > 0 || + ws->current_gaps.bottom > 0 || ws->current_gaps.left > 0) { return; } if (config->smart_gaps) { @@ -643,18 +703,20 @@ void workspace_add_gaps(struct sway_workspace *ws) { } ws->current_gaps = ws->gaps_outer; - if (ws->layout == L_TABBED || ws->layout == L_STACKED) { // We have to add inner gaps for this, because children of tabbed and // stacked containers don't apply their own gaps - they assume the // tabbed/stacked container is using gaps. - ws->current_gaps += ws->gaps_inner; + ws->current_gaps.top += ws->gaps_inner; + ws->current_gaps.right += ws->gaps_inner; + ws->current_gaps.bottom += ws->gaps_inner; + ws->current_gaps.left += ws->gaps_inner; } - ws->x += ws->current_gaps; - ws->y += ws->current_gaps; - ws->width -= 2 * ws->current_gaps; - ws->height -= 2 * ws->current_gaps; + ws->x += ws->current_gaps.left; + ws->y += ws->current_gaps.top; + ws->width -= ws->current_gaps.left + ws->current_gaps.right; + ws->height -= ws->current_gaps.top + ws->current_gaps.bottom; } struct sway_container *workspace_split(struct sway_workspace *workspace, |