aboutsummaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c61
-rw-r--r--sway/commands/bar/binding_mode_indicator.c12
-rw-r--r--sway/commands/bar/pango_markup.c13
-rw-r--r--sway/commands/bar/workspace_buttons.c12
-rw-r--r--sway/commands/bar/wrap_scroll.c14
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/create_output.c4
-rw-r--r--sway/commands/floating.c14
-rw-r--r--sway/commands/focus_follows_mouse.c11
-rw-r--r--sway/commands/gaps.c151
-rw-r--r--sway/commands/input.c1
-rw-r--r--sway/commands/input/pointer_accel.c9
-rw-r--r--sway/commands/input/scroll_factor.c32
-rw-r--r--sway/commands/input/xkb_capslock.c10
-rw-r--r--sway/commands/input/xkb_numlock.c10
-rw-r--r--sway/commands/resize.c111
-rw-r--r--sway/commands/seat/fallback.c13
-rw-r--r--sway/commands/smart_gaps.c10
-rw-r--r--sway/commands/sticky.c17
-rw-r--r--sway/commands/swap.c25
-rw-r--r--sway/commands/workspace.c137
-rw-r--r--sway/commands/ws_auto_back_and_forth.c4
-rw-r--r--sway/config.c8
-rw-r--r--sway/config/bar.c1
-rw-r--r--sway/config/input.c4
-rw-r--r--sway/config/output.c1
-rw-r--r--sway/criteria.c27
-rw-r--r--sway/desktop/desktop.c4
-rw-r--r--sway/desktop/output.c39
-rw-r--r--sway/desktop/render.c57
-rw-r--r--sway/desktop/transaction.c50
-rw-r--r--sway/desktop/xdg_shell.c10
-rw-r--r--sway/desktop/xdg_shell_v6.c10
-rw-r--r--sway/desktop/xwayland.c18
-rw-r--r--sway/input/cursor.c52
-rw-r--r--sway/input/seat.c2
-rw-r--r--sway/ipc-json.c32
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c6
-rw-r--r--sway/sway-input.5.scd10
-rw-r--r--sway/sway.5.scd54
-rw-r--r--sway/tree/container.c80
-rw-r--r--sway/tree/output.c7
-rw-r--r--sway/tree/root.c2
-rw-r--r--sway/tree/view.c61
-rw-r--r--sway/tree/workspace.c126
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,