aboutsummaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
authoremersion <contact@emersion.fr>2018-08-02 23:49:25 +0100
committerGitHub <noreply@github.com>2018-08-02 23:49:25 +0100
commit3a54e2291c017397ceff60511c29fe70d229bc8b (patch)
treed340b7776f945462f5ecffc830ada4d5fbe82f51 /sway
parentc35a34262f8da368f65d37f811a2264647e0dae6 (diff)
parente07da5fc5c6ac5c186662b56b08ca71531119de0 (diff)
Merge branch 'master' into wlr-gamma-control
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c4
-rw-r--r--sway/commands/bind.c218
-rw-r--r--sway/commands/exec_always.c6
-rw-r--r--sway/commands/floating.c21
-rw-r--r--sway/commands/floating_modifier.c30
-rw-r--r--sway/commands/focus.c25
-rw-r--r--sway/commands/focus_follows_mouse.c4
-rw-r--r--sway/commands/focus_wrapping.c12
-rw-r--r--sway/commands/force_focus_wrapping.c8
-rw-r--r--sway/commands/fullscreen.c28
-rw-r--r--sway/commands/input.c21
-rw-r--r--sway/commands/input/drag_lock.c9
-rw-r--r--sway/commands/input/dwt.c9
-rw-r--r--sway/commands/input/left_handed.c11
-rw-r--r--sway/commands/input/middle_emulation.c9
-rw-r--r--sway/commands/input/natural_scroll.c11
-rw-r--r--sway/commands/input/tap.c9
-rw-r--r--sway/commands/input/xkb_capslock.c33
-rw-r--r--sway/commands/input/xkb_numlock.c33
-rw-r--r--sway/commands/mark.c2
-rw-r--r--sway/commands/mode.c1
-rw-r--r--sway/commands/move.c43
-rw-r--r--sway/commands/output/dpms.c8
-rw-r--r--sway/commands/reload.c29
-rw-r--r--sway/commands/scratchpad.c44
-rw-r--r--sway/commands/show_marks.c10
-rw-r--r--sway/commands/split.c4
-rw-r--r--sway/commands/swap.c7
-rw-r--r--sway/commands/urgent.c10
-rw-r--r--sway/config.c10
-rw-r--r--sway/config/bar.c4
-rw-r--r--sway/config/input.c8
-rw-r--r--sway/criteria.c22
-rw-r--r--sway/desktop/layer_shell.c7
-rw-r--r--sway/desktop/output.c339
-rw-r--r--sway/desktop/render.c132
-rw-r--r--sway/desktop/transaction.c87
-rw-r--r--sway/desktop/xdg_shell.c105
-rw-r--r--sway/desktop/xdg_shell_v6.c106
-rw-r--r--sway/desktop/xwayland.c60
-rw-r--r--sway/input/cursor.c413
-rw-r--r--sway/input/input-manager.c36
-rw-r--r--sway/input/keyboard.c108
-rw-r--r--sway/input/seat.c135
-rw-r--r--sway/ipc-json.c9
-rw-r--r--sway/ipc-server.c136
-rw-r--r--sway/main.c1
-rw-r--r--sway/meson.build12
-rw-r--r--sway/scratchpad.c181
-rw-r--r--sway/server.c5
-rw-r--r--sway/sway-input.5.scd10
-rw-r--r--sway/sway.1.scd2
-rw-r--r--sway/tree/arrange.c18
-rw-r--r--sway/tree/container.c369
-rw-r--r--sway/tree/layout.c122
-rw-r--r--sway/tree/output.c2
-rw-r--r--sway/tree/view.c315
-rw-r--r--sway/tree/workspace.c268
58 files changed, 2811 insertions, 870 deletions
diff --git a/sway/commands.c b/sway/commands.c
index f1f03574..fdae1961 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -103,6 +103,7 @@ static struct cmd_handler handlers[] = {
{ "exec_always", cmd_exec_always },
{ "floating_maximum_size", cmd_floating_maximum_size },
{ "floating_minimum_size", cmd_floating_minimum_size },
+ { "floating_modifier", cmd_floating_modifier },
{ "focus", cmd_focus },
{ "focus_follows_mouse", cmd_focus_follows_mouse },
{ "focus_wrapping", cmd_focus_wrapping },
@@ -148,6 +149,7 @@ static struct cmd_handler command_handlers[] = {
{ "reload", cmd_reload },
{ "rename", cmd_rename },
{ "resize", cmd_resize },
+ { "scratchpad", cmd_scratchpad },
{ "split", cmd_split },
{ "splith", cmd_splith },
{ "splitt", cmd_splitt },
@@ -325,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
} while(head);
cleanup:
free(exec);
- free(views);
+ list_free(views);
if (!results) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 83e9e432..8270b958 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,3 +1,4 @@
+#define _XOPEN_SOURCE 500
#ifdef __linux__
#include <linux/input-event-codes.h>
#elif __FreeBSD__
@@ -5,9 +6,11 @@
#endif
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-names.h>
+#include <string.h>
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "sway/ipc-server.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
@@ -27,6 +30,33 @@ void free_sway_binding(struct sway_binding *binding) {
free(binding);
}
+static struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
+ struct sway_binding *new_sb = calloc(1, sizeof(struct sway_binding));
+ if (!new_sb) {
+ return NULL;
+ }
+
+ new_sb->type = sb->type;
+ new_sb->order = sb->order;
+ new_sb->flags = sb->flags;
+ new_sb->modifiers = sb->modifiers;
+ new_sb->command = strdup(sb->command);
+
+ new_sb->keys = create_list();
+ int i;
+ for (i = 0; i < sb->keys->length; ++i) {
+ xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
+ if (!key) {
+ free_sway_binding(new_sb);
+ return NULL;
+ }
+ *key = *(xkb_keysym_t *)sb->keys->items[i];
+ list_add(new_sb->keys, key);
+ }
+
+ return new_sb;
+}
+
/**
* Returns true if the bindings have the same key and modifier combinations.
* Note that keyboard layout is not considered, so the bindings might actually
@@ -34,11 +64,14 @@ void free_sway_binding(struct sway_binding *binding) {
*/
static bool binding_key_compare(struct sway_binding *binding_a,
struct sway_binding *binding_b) {
- if (binding_a->release != binding_b->release) {
+ if (binding_a->type != binding_b->type) {
return false;
}
- if (binding_a->bindcode != binding_b->bindcode) {
+ uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER
+ | BINDING_CONTENTS | BINDING_TITLEBAR;
+ if ((binding_a->flags & conflict_generating_flags) !=
+ (binding_b->flags & conflict_generating_flags)) {
return false;
}
@@ -69,6 +102,66 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0);
}
+
+/**
+ * From a keycode, bindcode, or bindsym name and the most likely binding type,
+ * identify the appropriate numeric value corresponding to the key. Return NULL
+ * and set *key_val if successful, otherwise return a specific error. Change
+ * the value of *type if the initial type guess was incorrect and if this
+ * was the first identified key.
+ */
+static struct cmd_results *identify_key(const char* name, bool first_key,
+ uint32_t* key_val, enum binding_input_type* type) {
+ if (*type == BINDING_KEYCODE) {
+ // check for keycode
+ xkb_keycode_t keycode = strtol(name, NULL, 10);
+ if (!xkb_keycode_is_legal_ext(keycode)) {
+ return cmd_results_new(CMD_INVALID, "bindcode",
+ "Invalid keycode '%s'", name);
+ }
+ *key_val = keycode;
+ } else {
+ // check for keysym
+ xkb_keysym_t keysym = xkb_keysym_from_name(name,
+ XKB_KEYSYM_CASE_INSENSITIVE);
+
+ // Check for mouse binding
+ uint32_t button = 0;
+ if (strncasecmp(name, "button", strlen("button")) == 0 &&
+ strlen(name) == strlen("button0")) {
+ button = name[strlen("button")] - '1' + BTN_LEFT;
+ }
+
+ if (*type == BINDING_KEYSYM) {
+ if (button) {
+ if (first_key) {
+ *type = BINDING_MOUSE;
+ *key_val = button;
+ } else {
+ return cmd_results_new(CMD_INVALID, "bindsym",
+ "Mixed button '%s' into key sequence", name);
+ }
+ } else if (keysym) {
+ *key_val = keysym;
+ } else {
+ return cmd_results_new(CMD_INVALID, "bindsym",
+ "Unknown key '%s'", name);
+ }
+ } else {
+ if (button) {
+ *key_val = button;
+ } else if (keysym) {
+ return cmd_results_new(CMD_INVALID, "bindsym",
+ "Mixed keysym '%s' into button sequence", name);
+ } else {
+ return cmd_results_new(CMD_INVALID, "bindsym",
+ "Unknown button '%s'", name);
+ }
+ }
+ }
+ return NULL;
+}
+
static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
bool bindcode) {
const char *bindtype = bindcode ? "bindcode" : "bindsym";
@@ -85,22 +178,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
}
binding->keys = create_list();
binding->modifiers = 0;
- binding->release = false;
- binding->locked = false;
- binding->bindcode = bindcode;
+ binding->flags = 0;
+ binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM;
+
+ bool exclude_titlebar = false;
// Handle --release and --locked
while (argc > 0) {
if (strcmp("--release", argv[0]) == 0) {
- binding->release = true;
+ binding->flags |= BINDING_RELEASE;
} else if (strcmp("--locked", argv[0]) == 0) {
- binding->locked = true;
+ binding->flags |= BINDING_LOCKED;
+ } else if (strcmp("--whole-window", argv[0]) == 0) {
+ binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR;
+ } else if (strcmp("--border", argv[0]) == 0) {
+ binding->flags |= BINDING_BORDER;
+ } else if (strcmp("--exclude-titlebar", argv[0]) == 0) {
+ exclude_titlebar = true;
} else {
break;
}
argv++;
argc--;
}
+ if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)
+ || exclude_titlebar) {
+ binding->type = BINDING_MOUSE;
+ }
+
if (argc < 2) {
free_sway_binding(binding);
return cmd_results_new(CMD_FAILURE, bindtype,
@@ -119,64 +224,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
continue;
}
- xkb_keycode_t keycode;
- xkb_keysym_t keysym;
- if (bindcode) {
- // parse keycode
- keycode = (int)strtol(split->items[i], NULL, 10);
- if (!xkb_keycode_is_legal_ext(keycode)) {
- error =
- cmd_results_new(CMD_INVALID, "bindcode",
- "Invalid keycode '%s'", (char *)split->items[i]);
- free_sway_binding(binding);
- list_free(split);
- return error;
- }
- } else {
- // Check for xkb key
- keysym = xkb_keysym_from_name(split->items[i],
- XKB_KEYSYM_CASE_INSENSITIVE);
-
- // Check for mouse binding
- if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
- strlen(split->items[i]) == strlen("button0")) {
- keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
- }
- if (!keysym) {
- struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
- "Unknown key '%s'", (char *)split->items[i]);
- free_sway_binding(binding);
- free_flat_list(split);
- return ret;
- }
+ // Identify the key and possibly change binding->type
+ uint32_t key_val = 0;
+ error = identify_key(split->items[i], binding->keys->length == 0,
+ &key_val, &binding->type);
+ if (error) {
+ free_sway_binding(binding);
+ list_free(split);
+ return error;
}
+
uint32_t *key = calloc(1, sizeof(uint32_t));
if (!key) {
free_sway_binding(binding);
free_flat_list(split);
return cmd_results_new(CMD_FAILURE, bindtype,
- "Unable to allocate binding");
- }
-
- if (bindcode) {
- *key = (uint32_t)keycode;
- } else {
- *key = (uint32_t)keysym;
+ "Unable to allocate binding key");
}
-
+ *key = key_val;
list_add(binding->keys, key);
}
free_flat_list(split);
binding->order = binding_order++;
+ // refine region of interest for mouse binding once we are certain
+ // that this is one
+ if (exclude_titlebar) {
+ binding->flags &= ~BINDING_TITLEBAR;
+ } else if (binding->type == BINDING_MOUSE) {
+ binding->flags |= BINDING_TITLEBAR;
+ }
+
// sort ascending
list_qsort(binding->keys, key_qsort_cmp);
list_t *mode_bindings;
- if (bindcode) {
+ if (binding->type == BINDING_KEYCODE) {
mode_bindings = config->current_mode->keycode_bindings;
- } else {
+ } else if (binding->type == BINDING_KEYSYM) {
mode_bindings = config->current_mode->keysym_bindings;
+ } else {
+ mode_bindings = config->current_mode->mouse_bindings;
}
// overwrite the binding if it already exists
@@ -209,3 +297,39 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
struct cmd_results *cmd_bindcode(int argc, char **argv) {
return cmd_bindsym_or_bindcode(argc, argv, true);
}
+
+
+/**
+ * Execute the command associated to a binding
+ */
+void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {
+ wlr_log(WLR_DEBUG, "running command for binding: %s",
+ binding->command);
+
+ struct sway_binding *binding_copy = binding;
+ bool reload = false;
+ // if this is a reload command we need to make a duplicate of the
+ // binding since it will be gone after the reload has completed.
+ if (strcasecmp(binding->command, "reload") == 0) {
+ reload = true;
+ binding_copy = sway_binding_dup(binding);
+ if (!binding_copy) {
+ wlr_log(WLR_ERROR, "Failed to duplicate binding during reload");
+ return;
+ }
+ }
+
+ config->handler_context.seat = seat;
+ struct cmd_results *results = execute_command(binding->command, NULL);
+ if (results->status == CMD_SUCCESS) {
+ ipc_event_binding(binding_copy);
+ } else {
+ wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
+ binding->command, results->error);
+ }
+
+ if (reload) { // free the binding if we made a copy
+ free_sway_binding(binding_copy);
+ }
+ free_cmd_results(results);
+}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index c7727857..c730cb8b 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -4,6 +4,7 @@
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <signal.h>
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/tree/container.h"
@@ -47,6 +48,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
if ((pid = fork()) == 0) {
// Fork child process again
setsid();
+ sigset_t set;
+ sigemptyset(&set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
close(fd[0]);
if ((child = fork()) == 0) {
close(fd[1]);
@@ -74,7 +78,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
waitpid(pid, NULL, 0);
if (child > 0) {
wlr_log(WLR_DEBUG, "Child process created with pid %d", child);
- // TODO: add PID to active workspace
+ workspace_record_pid(child);
} else {
return cmd_results_new(CMD_FAILURE, "exec_always",
"Second fork() failed");
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index 6ab56c3b..31de5ec3 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -17,9 +17,24 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
}
struct sway_container *container =
config->handler_context.current_container;
- if (container->type != C_VIEW) {
- // TODO: This doesn't strictly speaking have to be true
- return cmd_results_new(CMD_INVALID, "float", "Only views can float");
+ if (container->type == C_WORKSPACE && container->children->length == 0) {
+ return cmd_results_new(CMD_INVALID, "floating",
+ "Can't float an empty workspace");
+ }
+ if (container->type == C_WORKSPACE) {
+ // Wrap the workspace's children in a container so we can float it
+ struct sway_container *workspace = container;
+ container = container_wrap_children(container);
+ workspace->layout = L_HORIZ;
+ seat_set_focus(config->handler_context.seat, container);
+ }
+
+ // If the container is in a floating split container,
+ // operate on the split container instead of the child.
+ if (container_is_floating_or_child(container)) {
+ while (container->parent->layout != L_FLOATING) {
+ container = container->parent;
+ }
}
bool wants_floating;
diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c
new file mode 100644
index 00000000..f5d2b3fe
--- /dev/null
+++ b/sway/commands/floating_modifier.c
@@ -0,0 +1,30 @@
+#include "strings.h"
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "util.h"
+
+struct cmd_results *cmd_floating_modifier(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+
+ uint32_t mod = get_modifier_mask_by_name(argv[0]);
+ if (!mod) {
+ return cmd_results_new(CMD_INVALID, "floating_modifier",
+ "Invalid modifier");
+ }
+
+ if (argc == 1 || strcasecmp(argv[1], "normal") == 0) {
+ config->floating_mod_inverse = false;
+ } else if (strcasecmp(argv[1], "inverse") == 0) {
+ config->floating_mod_inverse = true;
+ } else {
+ return cmd_results_new(CMD_INVALID, "floating_modifier",
+ "Usage: floating_modifier <mod> [inverse|normal]");
+ }
+
+ config->floating_mod = mod;
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 9cd8bfae..76d3f1dc 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -35,14 +35,25 @@ static struct cmd_results *focus_mode(struct sway_container *con,
struct sway_seat *seat, bool floating) {
struct sway_container *ws = con->type == C_WORKSPACE ?
con : container_parent(con, C_WORKSPACE);
- struct sway_container *new_focus = ws;
- if (floating) {
- new_focus = ws->sway_workspace->floating;
- if (new_focus->children->length == 0) {
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+
+ // If the container is in a floating split container,
+ // operate on the split container instead of the child.
+ if (container_is_floating_or_child(con)) {
+ while (con->parent->layout != L_FLOATING) {
+ con = con->parent;
}
}
- seat_set_focus(seat, seat_get_active_child(seat, new_focus));
+
+ struct sway_container *new_focus = NULL;
+ if (floating) {
+ new_focus = seat_get_focus_inactive(seat, ws->sway_workspace->floating);
+ } else {
+ new_focus = seat_get_focus_inactive_tiling(seat, ws);
+ }
+ if (!new_focus) {
+ new_focus = ws;
+ }
+ seat_set_focus(seat, new_focus);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -97,7 +108,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
} else if (strcmp(argv[0], "tiling") == 0) {
return focus_mode(con, seat, false);
} else if (strcmp(argv[0], "mode_toggle") == 0) {
- return focus_mode(con, seat, !container_is_floating(con));
+ return focus_mode(con, seat, !container_is_floating_or_child(con));
}
if (strcmp(argv[0], "output") == 0) {
diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c
index 661e7852..0b0e334c 100644
--- a/sway/commands/focus_follows_mouse.c
+++ b/sway/commands/focus_follows_mouse.c
@@ -1,12 +1,14 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
+#include "util.h"
struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
return error;
}
- config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
+ config->focus_follows_mouse =
+ parse_boolean(argv[0], config->focus_follows_mouse);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/focus_wrapping.c b/sway/commands/focus_wrapping.c
index 0a9e0bf2..562ee4f9 100644
--- a/sway/commands/focus_wrapping.c
+++ b/sway/commands/focus_wrapping.c
@@ -1,6 +1,7 @@
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "util.h"
struct cmd_results *cmd_focus_wrapping(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -8,15 +9,12 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) {
return error;
}
- if (strcasecmp(argv[0], "no") == 0) {
- config->focus_wrapping = WRAP_NO;
- } else if (strcasecmp(argv[0], "yes") == 0) {
- config->focus_wrapping = WRAP_YES;
- } else if (strcasecmp(argv[0], "force") == 0) {
+ if (strcasecmp(argv[0], "force") == 0) {
config->focus_wrapping = WRAP_FORCE;
+ } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) {
+ config->focus_wrapping = WRAP_YES;
} else {
- return cmd_results_new(CMD_INVALID, "focus_wrapping",
- "Expected 'focus_wrapping yes|no|force'");
+ config->focus_wrapping = WRAP_NO;
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c
index bc1d067f..0892d9e9 100644
--- a/sway/commands/force_focus_wrapping.c
+++ b/sway/commands/force_focus_wrapping.c
@@ -1,6 +1,7 @@
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "util.h"
struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
struct cmd_results *error =
@@ -9,13 +10,10 @@ struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
return error;
}
- if (strcasecmp(argv[0], "no") == 0) {
- config->focus_wrapping = WRAP_YES;
- } else if (strcasecmp(argv[0], "yes") == 0) {
+ if (parse_boolean(argv[0], config->focus_wrapping == WRAP_FORCE)) {
config->focus_wrapping = WRAP_FORCE;
} else {
- return cmd_results_new(CMD_INVALID, "force_focus_wrapping",
- "Expected 'force_focus_wrapping yes|no'");
+ config->focus_wrapping = WRAP_YES;
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 0b5beaa2..5ad06e40 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -5,6 +5,7 @@
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/layout.h"
+#include "util.h"
struct cmd_results *cmd_fullscreen(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -13,25 +14,24 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
}
struct sway_container *container =
config->handler_context.current_container;
- if (container->type != C_VIEW) {
+ if (container->type == C_WORKSPACE && container->children->length == 0) {
return cmd_results_new(CMD_INVALID, "fullscreen",
- "Only views can fullscreen");
+ "Can't fullscreen an empty workspace");
}
- struct sway_view *view = container->sway_view;
- bool wants_fullscreen;
+ if (container->type == C_WORKSPACE) {
+ // Wrap the workspace's children in a container so we can fullscreen it
+ struct sway_container *workspace = container;
+ container = container_wrap_children(container);
+ workspace->layout = L_HORIZ;
+ seat_set_focus(config->handler_context.seat, container);
+ }
+ bool enable = !container->is_fullscreen;
- if (argc == 0 || strcmp(argv[0], "toggle") == 0) {
- wants_fullscreen = !view->is_fullscreen;
- } else if (strcmp(argv[0], "enable") == 0) {
- wants_fullscreen = true;
- } else if (strcmp(argv[0], "disable") == 0) {
- wants_fullscreen = false;
- } else {
- return cmd_results_new(CMD_INVALID, "fullscreen",
- "Expected 'fullscreen' or 'fullscreen <enable|disable|toggle>'");
+ if (argc) {
+ enable = parse_boolean(argv[0], container->is_fullscreen);
}
- view_set_fullscreen(view, wants_fullscreen);
+ container_set_fullscreen(container, enable);
struct sway_container *workspace = container_parent(container, C_WORKSPACE);
arrange_windows(workspace->parent);
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 5b203ea0..84888fbb 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -31,6 +31,12 @@ static struct cmd_handler input_handlers[] = {
{ "xkb_variant", input_cmd_xkb_variant },
};
+// must be in order for the bsearch
+static struct cmd_handler input_config_handlers[] = {
+ { "xkb_capslock", input_cmd_xkb_capslock },
+ { "xkb_numlock", input_cmd_xkb_numlock },
+};
+
struct cmd_results *cmd_input(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
@@ -44,8 +50,21 @@ struct cmd_results *cmd_input(int argc, char **argv) {
return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
}
- struct cmd_results *res = config_subcommand(argv + 1, argc - 1,
+ struct cmd_results *res;
+
+ if (find_handler(argv[1], input_config_handlers,
+ sizeof(input_config_handlers))) {
+ if (config->reading) {
+ res = config_subcommand(argv + 1, argc - 1,
+ input_config_handlers, sizeof(input_config_handlers));
+ } else {
+ res = cmd_results_new(CMD_FAILURE, "input",
+ "Can only be used in config file.");
+ }
+ } else {
+ res = config_subcommand(argv + 1, argc - 1,
input_handlers, sizeof(input_handlers));
+ }
free_input_config(config->handler_context.input_config);
config->handler_context.input_config = NULL;
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index 9e32816f..f9ddeef2 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -3,6 +3,7 @@
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
+#include "util.h"
struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -18,14 +19,10 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
struct input_config *new_config =
new_input_config(current_input_config->identifier);
- if (strcasecmp(argv[0], "enabled") == 0) {
+ if (parse_boolean(argv[0], true)) {
new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
- } else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
} else {
- free_input_config(new_config);
- return cmd_results_new(CMD_INVALID, "drag_lock",
- "Expected 'drag_lock <enabled|disabled>'");
+ new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
}
apply_input_config(new_config);
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index 73937507..15134268 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -3,6 +3,7 @@
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
+#include "util.h"
struct cmd_results *input_cmd_dwt(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -17,14 +18,10 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) {
struct input_config *new_config =
new_input_config(current_input_config->identifier);
- if (strcasecmp(argv[0], "enabled") == 0) {
+ if (parse_boolean(argv[0], true)) {
new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
- } else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
} else {
- free_input_config(new_config);
- return cmd_results_new(CMD_INVALID, "dwt",
- "Expected 'dwt <enabled|disabled>'");
+ new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
}
apply_input_config(new_config);
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index 769ce98c..e770043a 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -3,6 +3,7 @@
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
+#include "util.h"
struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -18,15 +19,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
struct input_config *new_config =
new_input_config(current_input_config->identifier);
- if (strcasecmp(argv[0], "enabled") == 0) {
- new_config->left_handed = 1;
- } else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->left_handed = 0;
- } else {
- free_input_config(new_config);
- return cmd_results_new(CMD_INVALID, "left_handed",
- "Expected 'left_handed <enabled|disabled>'");
- }
+ new_config->left_handed = parse_boolean(argv[0], true);
apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index 7ca01629..414d4d2b 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -3,6 +3,7 @@
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
+#include "util.h"
struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -18,15 +19,11 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
struct input_config *new_config =
new_input_config(current_input_config->identifier);
- if (strcasecmp(argv[0], "enabled") == 0) {
+ if (parse_boolean(argv[0], true)) {
new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
- } else if (strcasecmp(argv[0], "disabled") == 0) {
+ } else {
new_config->middle_emulation =
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
- } else {
- free_input_config(new_config);
- return cmd_results_new(CMD_INVALID, "middle_emulation",
- "Expected 'middle_emulation <enabled|disabled>'");
}
apply_input_config(new_config);
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 55236790..77c3ff00 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -3,6 +3,7 @@
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
+#include "util.h"
struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -18,15 +19,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
struct input_config *new_config =
new_input_config(current_input_config->identifier);
- if (strcasecmp(argv[0], "enabled") == 0) {
- new_config->natural_scroll = 1;
- } else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->natural_scroll = 0;
- } else {
- free_input_config(new_config);
- return cmd_results_new(CMD_INVALID, "natural_scroll",
- "Expected 'natural_scroll <enabled|disabled>'");
- }
+ new_config->natural_scroll = parse_boolean(argv[0], true);
apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index a8d1a10c..ac3b8237 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -4,6 +4,7 @@
#include "sway/commands.h"
#include "sway/input/input-manager.h"
#include "log.h"
+#include "util.h"
struct cmd_results *input_cmd_tap(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -18,14 +19,10 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
struct input_config *new_config =
new_input_config(current_input_config->identifier);
- if (strcasecmp(argv[0], "enabled") == 0) {
+ if (parse_boolean(argv[0], true)) {
new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
- } else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
} else {
- free_input_config(new_config);
- return cmd_results_new(CMD_INVALID, "tap",
- "Expected 'tap <enabled|disabled>'");
+ new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
}
wlr_log(WLR_DEBUG, "apply-tap for device: %s",
diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c
new file mode 100644
index 00000000..5442c463
--- /dev/null
+++ b/sway/commands/input/xkb_capslock.c
@@ -0,0 +1,33 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_capslock",
+ "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ if (strcasecmp(argv[0], "enabled") == 0) {
+ new_config->xkb_capslock = 1;
+ } else if (strcasecmp(argv[0], "disabled") == 0) {
+ new_config->xkb_capslock = 0;
+ } else {
+ free_input_config(new_config);
+ return cmd_results_new(CMD_INVALID, "xkb_capslock",
+ "Expected 'xkb_capslock <enabled|disabled>'");
+ }
+
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
new file mode 100644
index 00000000..39675366
--- /dev/null
+++ b/sway/commands/input/xkb_numlock.c
@@ -0,0 +1,33 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_numlock",
+ "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ if (strcasecmp(argv[0], "enabled") == 0) {
+ new_config->xkb_numlock = 1;
+ } else if (strcasecmp(argv[0], "disabled") == 0) {
+ new_config->xkb_numlock = 0;
+ } else {
+ free_input_config(new_config);
+ return cmd_results_new(CMD_INVALID, "xkb_numlock",
+ "Expected 'xkb_numlock <enabled|disabled>'");
+ }
+
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index 5a897e69..9ea8c301 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -58,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
view_find_and_unmark(mark);
if (!toggle || !had_mark) {
- list_add(view->marks, strdup(mark));
+ view_add_mark(view, mark);
}
free(mark);
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index b460fcb5..637ca45e 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -56,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
mode->name = strdup(mode_name);
mode->keysym_bindings = create_list();
mode->keycode_bindings = create_list();
+ mode->mouse_bindings = create_list();
mode->pango = pango;
list_add(config->modes, mode);
}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 6ec050a8..702b42d9 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -9,6 +9,7 @@
#include "sway/input/cursor.h"
#include "sway/input/seat.h"
#include "sway/output.h"
+#include "sway/scratchpad.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/layout.h"
@@ -58,8 +59,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
&& strcasecmp(argv[2], "workspace") == 0) {
// move container to workspace x
if (current->type == C_WORKSPACE) {
- // TODO: Wrap children in a container and move that
- return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+ current = container_wrap_children(current);
} else if (current->type != C_CONTAINER && current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
@@ -97,7 +97,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
container_move_to(current, destination);
struct sway_container *focus = seat_get_focus_inactive(
config->handler_context.seat, old_parent);
- seat_set_focus(config->handler_context.seat, focus);
+ seat_set_focus_warp(config->handler_context.seat, focus, true, false);
container_reap_empty(old_parent);
container_reap_empty(destination->parent);
@@ -134,7 +134,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
struct sway_container *old_parent = current->parent;
struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
container_move_to(current, focus);
- seat_set_focus(config->handler_context.seat, old_parent);
+ seat_set_focus_warp(config->handler_context.seat, old_parent, true, false);
container_reap_empty(old_parent);
container_reap_empty(focus->parent);
@@ -195,7 +195,7 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
"Cannot move workspaces in a direction");
}
if (container_is_floating(container)) {
- if (container->type == C_VIEW && container->sway_view->is_fullscreen) {
+ if (container->is_fullscreen) {
return cmd_results_new(CMD_FAILURE, "move",
"Cannot move fullscreen floating container");
}
@@ -296,6 +296,34 @@ static struct cmd_results *move_to_position(struct sway_container *container,
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
+static struct cmd_results *move_to_scratchpad(struct sway_container *con) {
+ if (con->type == C_WORKSPACE && con->children->length == 0) {
+ return cmd_results_new(CMD_INVALID, "move",
+ "Can't move an empty workspace to the scratchpad");
+ }
+ if (con->type == C_WORKSPACE) {
+ // Wrap the workspace's children in a container
+ struct sway_container *workspace = con;
+ con = container_wrap_children(con);
+ workspace->layout = L_HORIZ;
+ }
+
+ // If the container is in a floating split container,
+ // operate on the split container instead of the child.
+ if (container_is_floating_or_child(con)) {
+ while (con->parent->layout != L_FLOATING) {
+ con = con->parent;
+ }
+ }
+
+ if (con->scratchpad) {
+ return cmd_results_new(CMD_INVALID, "move",
+ "Container is already in the scratchpad");
+ }
+ scratchpad_add_container(con);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
+
struct cmd_results *cmd_move(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
@@ -317,10 +345,9 @@ struct cmd_results *cmd_move(int argc, char **argv) {
} else if (strcasecmp(argv[0], "workspace") == 0) {
return cmd_move_workspace(current, argc, argv);
} else if (strcasecmp(argv[0], "scratchpad") == 0
- || (strcasecmp(argv[0], "to") == 0
+ || (strcasecmp(argv[0], "to") == 0 && argc == 2
&& strcasecmp(argv[1], "scratchpad") == 0)) {
- // TODO: scratchpad
- return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+ return move_to_scratchpad(current);
} else if (strcasecmp(argv[0], "position") == 0) {
return move_to_position(current, argc, argv);
} else if (strcasecmp(argv[0], "absolute") == 0) {
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 0959ea6b..3492061e 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,5 +1,6 @@
#include "sway/commands.h"
#include "sway/config.h"
+#include "util.h"
struct cmd_results *output_cmd_dpms(int argc, char **argv) {
if (!config->handler_context.output_config) {
@@ -9,13 +10,10 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument.");
}
- if (strcmp(*argv, "on") == 0) {
+ if (parse_boolean(argv[0], true)) {
config->handler_context.output_config->dpms_state = DPMS_ON;
- } else if (strcmp(*argv, "off") == 0) {
- config->handler_context.output_config->dpms_state = DPMS_OFF;
} else {
- return cmd_results_new(CMD_INVALID, "output",
- "Invalid dpms state, valid states are on/off.");
+ config->handler_context.output_config->dpms_state = DPMS_OFF;
}
config->handler_context.leftovers.argc = argc - 1;
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index cea6a94b..5c1b19b4 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,17 +1,46 @@
+#define _XOPEN_SOURCE 500
+#include <string.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "sway/ipc-server.h"
#include "sway/tree/arrange.h"
+#include "list.h"
struct cmd_results *cmd_reload(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
return error;
}
+
+ // store bar ids to check against new bars for barconfig_update events
+ list_t *bar_ids = create_list();
+ for (int i = 0; i < config->bars->length; ++i) {
+ struct bar_config *bar = config->bars->items[i];
+ list_add(bar_ids, strdup(bar->id));
+ }
+
if (!load_main_config(config->current_config_path, true)) {
return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
}
+ ipc_event_workspace(NULL, NULL, "reload");
load_swaybars();
+
+ for (int i = 0; i < config->bars->length; ++i) {
+ struct bar_config *bar = config->bars->items[i];
+ for (int j = 0; j < bar_ids->length; ++j) {
+ if (strcmp(bar->id, bar_ids->items[j]) == 0) {
+ ipc_event_barconfig_update(bar);
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < bar_ids->length; ++i) {
+ free(bar_ids->items[i]);
+ }
+ list_free(bar_ids);
+
arrange_windows(&root_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
new file mode 100644
index 00000000..01a91d65
--- /dev/null
+++ b/sway/commands/scratchpad.c
@@ -0,0 +1,44 @@
+#include "log.h"
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "sway/scratchpad.h"
+#include "sway/tree/container.h"
+
+struct cmd_results *cmd_scratchpad(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ if (strcmp(argv[0], "show") != 0) {
+ return cmd_results_new(CMD_INVALID, "scratchpad",
+ "Expected 'scratchpad show'");
+ }
+ if (!root_container.sway_root->scratchpad->length) {
+ return cmd_results_new(CMD_INVALID, "scratchpad",
+ "Scratchpad is empty");
+ }
+
+ if (config->handler_context.using_criteria) {
+ struct sway_container *con = config->handler_context.current_container;
+
+ // If the container is in a floating split container,
+ // operate on the split container instead of the child.
+ if (container_is_floating_or_child(con)) {
+ while (con->parent->layout != L_FLOATING) {
+ con = con->parent;
+ }
+ }
+
+ // If using criteria, this command is executed for every container which
+ // matches the criteria. If this container isn't in the scratchpad,
+ // we'll just silently return a success.
+ if (!con->scratchpad) {
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ }
+ scratchpad_toggle_container(con);
+ } else {
+ scratchpad_toggle_auto();
+ }
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index c7fdc538..434a0e27 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -7,6 +7,7 @@
#include "list.h"
#include "log.h"
#include "stringop.h"
+#include "util.h"
static void rebuild_marks_iterator(struct sway_container *con, void *data) {
if (con->type == C_VIEW) {
@@ -20,14 +21,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
return error;
}
- if (strcmp(*argv, "yes") == 0) {
- config->show_marks = true;
- } else if (strcmp(*argv, "no") == 0) {
- config->show_marks = false;
- } else {
- return cmd_results_new(CMD_INVALID, "show_marks",
- "Expected 'show_marks <yes|no>'");
- }
+ config->show_marks = parse_boolean(argv[0], config->show_marks);
if (config->show_marks) {
container_for_each_descendant_dfs(&root_container,
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 313799da..a8eddf54 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -10,10 +10,6 @@
static struct cmd_results *do_split(int layout) {
struct sway_container *con = config->handler_context.current_container;
- if (container_is_floating(con)) {
- return cmd_results_new(CMD_FAILURE, "split",
- "Can't split a floating view");
- }
struct sway_container *parent = container_split(con, layout);
container_create_notify(parent);
arrange_windows(parent->parent);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 2fc88308..4e3a9cce 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,5 +1,6 @@
#include <strings.h>
#include <wlr/util/log.h>
+#include "config.h"
#include "sway/commands.h"
#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
@@ -14,10 +15,14 @@ static bool test_con_id(struct sway_container *container, void *con_id) {
}
static bool test_id(struct sway_container *container, void *id) {
+#ifdef HAVE_XWAYLAND
xcb_window_t *wid = id;
return (container->type == C_VIEW
&& container->sway_view->type == SWAY_VIEW_XWAYLAND
&& container->sway_view->wlr_xwayland_surface->window_id == *wid);
+#else
+ return false;
+#endif
}
static bool test_mark(struct sway_container *container, void *mark) {
@@ -43,8 +48,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
char *value = join_args(argv + 3, argc - 3);
if (strcasecmp(argv[2], "id") == 0) {
+#ifdef HAVE_XWAYLAND
xcb_window_t id = strtol(value, NULL, 0);
other = container_find(&root_container, test_id, (void *)&id);
+#endif
} else if (strcasecmp(argv[2], "con_id") == 0) {
size_t con_id = atoi(value);
other = container_find(&root_container, test_con_id, (void *)con_id);
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c
index d199858a..51c497c4 100644
--- a/sway/commands/urgent.c
+++ b/sway/commands/urgent.c
@@ -5,6 +5,7 @@
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/layout.h"
+#include "util.h"
struct cmd_results *cmd_urgent(int argc, char **argv) {
struct cmd_results *error = NULL;
@@ -19,17 +20,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) {
}
struct sway_view *view = container->sway_view;
- if (strcmp(argv[0], "enable") == 0) {
- view_set_urgent(view, true);
- } else if (strcmp(argv[0], "disable") == 0) {
- view_set_urgent(view, false);
- } else if (strcmp(argv[0], "allow") == 0) {
+ if (strcmp(argv[0], "allow") == 0) {
view->allow_request_urgent = true;
} else if (strcmp(argv[0], "deny") == 0) {
view->allow_request_urgent = false;
} else {
- return cmd_results_new(CMD_INVALID, "urgent",
- "Expected 'urgent <enable|disable|allow|deny>'");
+ view_set_urgent(view, parse_boolean(argv[0], view_is_urgent(view)));
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/config.c b/sway/config.c
index ed624bfa..2afffab1 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) {
}
list_free(mode->keycode_bindings);
}
+ if (mode->mouse_bindings) {
+ for (i = 0; i < mode->mouse_bindings->length; i++) {
+ free_sway_binding(mode->mouse_bindings->items[i]);
+ }
+ list_free(mode->mouse_bindings);
+ }
free(mode);
}
@@ -87,7 +93,6 @@ void free_config(struct sway_config *config) {
}
list_free(config->cmd_queue);
list_free(config->workspace_outputs);
- list_free(config->pid_workspaces);
if (config->output_configs) {
for (int i = 0; i < config->output_configs->length; i++) {
free_output_config(config->output_configs->items[i]);
@@ -157,7 +162,6 @@ static void config_defaults(struct sway_config *config) {
if (!(config->modes = create_list())) goto cleanup;
if (!(config->bars = create_list())) goto cleanup;
if (!(config->workspace_outputs = create_list())) goto cleanup;
- if (!(config->pid_workspaces = create_list())) goto cleanup;
if (!(config->criteria = create_list())) goto cleanup;
if (!(config->no_focus = create_list())) goto cleanup;
if (!(config->input_configs = create_list())) goto cleanup;
@@ -172,9 +176,11 @@ static void config_defaults(struct sway_config *config) {
strcpy(config->current_mode->name, "default");
if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
+ if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
list_add(config->modes, config->current_mode);
config->floating_mod = 0;
+ config->floating_mod_inverse = false;
config->dragging_key = BTN_LEFT;
config->resizing_key = BTN_RIGHT;
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 3a74331e..ae9383d6 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -10,6 +10,7 @@
#include <sys/stat.h>
#include <signal.h>
#include <strings.h>
+#include <signal.h>
#include "sway/config.h"
#include "stringop.h"
#include "list.h"
@@ -175,6 +176,9 @@ void invoke_swaybar(struct bar_config *bar) {
if (bar->pid == 0) {
setpgid(0, 0);
close(filedes[0]);
+ sigset_t set;
+ sigemptyset(&set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
// run custom swaybar
size_t len = snprintf(NULL, 0, "%s -b %s",
diff --git a/sway/config/input.c b/sway/config/input.c
index 8d687a6d..9885e85c 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -33,6 +33,8 @@ struct input_config *new_input_config(const char* identifier) {
input->left_handed = INT_MIN;
input->repeat_delay = INT_MIN;
input->repeat_rate = INT_MIN;
+ input->xkb_numlock = INT_MIN;
+ input->xkb_capslock = INT_MIN;
return input;
}
@@ -104,6 +106,12 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
free(dst->xkb_variant);
dst->xkb_variant = strdup(src->xkb_variant);
}
+ if (src->xkb_numlock != INT_MIN) {
+ dst->xkb_numlock = src->xkb_numlock;
+ }
+ if (src->xkb_capslock != INT_MIN) {
+ dst->xkb_capslock = src->xkb_capslock;
+ }
if (src->mapped_from_region) {
free(dst->mapped_from_region);
dst->mapped_from_region =
diff --git a/sway/criteria.c b/sway/criteria.c
index e2b248de..39d300ea 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -10,6 +10,7 @@
#include "stringop.h"
#include "list.h"
#include "log.h"
+#include "config.h"
bool criteria_is_empty(struct criteria *criteria) {
return !criteria->title
@@ -19,7 +20,9 @@ bool criteria_is_empty(struct criteria *criteria) {
&& !criteria->instance
&& !criteria->con_mark
&& !criteria->con_id
+#ifdef HAVE_XWAYLAND
&& !criteria->id
+#endif
&& !criteria->window_role
&& !criteria->window_type
&& !criteria->floating
@@ -127,12 +130,14 @@ static bool criteria_matches_view(struct criteria *criteria,
}
}
+#ifdef HAVE_XWAYLAND
if (criteria->id) { // X11 window ID
uint32_t x11_window_id = view_get_x11_window_id(view);
if (!x11_window_id || x11_window_id != criteria->id) {
return false;
}
}
+#endif
if (criteria->window_role) {
// TODO
@@ -225,6 +230,15 @@ list_t *criteria_get_views(struct criteria *criteria) {
};
container_for_each_descendant_dfs(&root_container,
criteria_get_views_iterator, &data);
+
+ // Scratchpad items which are hidden are not in the tree.
+ for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) {
+ struct sway_container *con =
+ root_container.sway_root->scratchpad->items[i];
+ if (!con->parent) {
+ criteria_get_views_iterator(con, &data);
+ }
+ }
return matches;
}
@@ -256,7 +270,9 @@ enum criteria_token {
T_CON_ID,
T_CON_MARK,
T_FLOATING,
+#ifdef HAVE_XWAYLAND
T_ID,
+#endif
T_INSTANCE,
T_SHELL,
T_TILING,
@@ -278,8 +294,10 @@ static enum criteria_token token_from_name(char *name) {
return T_CON_ID;
} else if (strcmp(name, "con_mark") == 0) {
return T_CON_MARK;
+#ifdef HAVE_XWAYLAND
} else if (strcmp(name, "id") == 0) {
return T_ID;
+#endif
} else if (strcmp(name, "instance") == 0) {
return T_INSTANCE;
} else if (strcmp(name, "shell") == 0) {
@@ -346,7 +364,9 @@ static char *get_focused_prop(enum criteria_token token) {
case T_CON_ID: // These do not support __focused__
case T_CON_MARK:
case T_FLOATING:
+#ifdef HAVE_XWAYLAND
case T_ID:
+#endif
case T_TILING:
case T_URGENT:
case T_WINDOW_TYPE:
@@ -417,12 +437,14 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
case T_WINDOW_TYPE:
// TODO: This is a string but will be stored as an enum or integer
break;
+#ifdef HAVE_XWAYLAND
case T_ID:
criteria->id = strtoul(effective_value, &endptr, 10);
if (*endptr != 0) {
error = strdup("The value for 'id' should be numeric");
}
break;
+#endif
case T_FLOATING:
criteria->floating = true;
break;
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index a7d96717..a2935883 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -7,11 +7,13 @@
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
+#include "sway/desktop/transaction.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
+#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
#include "log.h"
@@ -245,6 +247,9 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
output_damage_surface(output, layer->geo.x, layer->geo.y,
layer_surface->surface, false);
}
+
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
static void unmap(struct sway_layer_surface *sway_layer) {
@@ -282,6 +287,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output = sway_layer->layer_surface->output->data;
if (output != NULL && output->swayc != NULL) {
arrange_layers(output);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
wl_list_remove(&sway_layer->output_destroy.link);
sway_layer->layer_surface->output = NULL;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a9808406..66747a3f 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -14,6 +14,7 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/util/region.h>
#include "log.h"
+#include "config.h"
#include "sway/config.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
@@ -56,9 +57,21 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh,
*sy = ry + ph/2 - sh/2;
}
-bool output_get_surface_box(struct root_geometry *geo,
- struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
+struct surface_iterator_data {
+ sway_surface_iterator_func_t user_iterator;
+ void *user_data;
+
+ struct sway_output *output;
+ double ox, oy;
+ int width, height;
+ float rotation;
+};
+
+static bool get_surface_box(struct surface_iterator_data *data,
+ struct wlr_surface *surface, int sx, int sy,
struct wlr_box *surface_box) {
+ struct sway_output *output = data->output;
+
if (!wlr_surface_has_buffer(surface)) {
return false;
}
@@ -67,12 +80,12 @@ bool output_get_surface_box(struct root_geometry *geo,
int sh = surface->current.height;
double _sx = sx, _sy = sy;
- rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
- geo->rotation);
+ rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height,
+ data->rotation);
struct wlr_box box = {
- .x = geo->x + _sx,
- .y = geo->y + _sy,
+ .x = data->ox + _sx,
+ .y = data->oy + _sy,
.width = sw,
.height = sh,
};
@@ -81,7 +94,7 @@ bool output_get_surface_box(struct root_geometry *geo,
}
struct wlr_box rotated_box;
- wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
+ wlr_box_rotated_bounds(&box, data->rotation, &rotated_box);
struct wlr_box output_box = {
.width = output->swayc->current.swayc_width,
@@ -92,46 +105,90 @@ bool output_get_surface_box(struct root_geometry *geo,
return wlr_box_intersection(&output_box, &rotated_box, &intersection);
}
-void output_surface_for_each_surface(struct wlr_surface *surface,
- double ox, double oy, struct root_geometry *geo,
- wlr_surface_iterator_func_t iterator, void *user_data) {
- geo->x = ox;
- geo->y = oy;
- geo->width = surface->current.width;
- geo->height = surface->current.height;
- geo->rotation = 0;
+static void output_for_each_surface_iterator(struct wlr_surface *surface,
+ int sx, int sy, void *_data) {
+ struct surface_iterator_data *data = _data;
- wlr_surface_for_each_surface(surface, iterator, user_data);
+ struct wlr_box box;
+ bool intersects = get_surface_box(data, surface, sx, sy, &box);
+ if (!intersects) {
+ return;
+ }
+
+ data->user_iterator(data->output, surface, &box, data->rotation,
+ data->user_data);
+}
+
+void output_surface_for_each_surface(struct sway_output *output,
+ struct wlr_surface *surface, double ox, double oy,
+ sway_surface_iterator_func_t iterator, void *user_data) {
+ struct surface_iterator_data data = {
+ .user_iterator = iterator,
+ .user_data = user_data,
+ .output = output,
+ .ox = ox,
+ .oy = oy,
+ .width = surface->current.width,
+ .height = surface->current.height,
+ .rotation = 0,
+ };
+
+ wlr_surface_for_each_surface(surface,
+ output_for_each_surface_iterator, &data);
}
-void output_view_for_each_surface(struct sway_view *view,
- struct sway_output *output, struct root_geometry *geo,
- wlr_surface_iterator_func_t iterator, void *user_data) {
- geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x;
- geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y;
- geo->width = view->swayc->current.view_width;
- geo->height = view->swayc->current.view_height;
- geo->rotation = 0; // TODO
+void output_view_for_each_surface(struct sway_output *output,
+ struct sway_view *view, sway_surface_iterator_func_t iterator,
+ void *user_data) {
+ struct surface_iterator_data data = {
+ .user_iterator = iterator,
+ .user_data = user_data,
+ .output = output,
+ .ox = view->swayc->current.view_x - output->swayc->current.swayc_x,
+ .oy = view->swayc->current.view_y - output->swayc->current.swayc_y,
+ .width = view->swayc->current.view_width,
+ .height = view->swayc->current.view_height,
+ .rotation = 0, // TODO
+ };
- view_for_each_surface(view, iterator, user_data);
+ view_for_each_surface(view,
+ output_for_each_surface_iterator, &data);
}
-void output_layer_for_each_surface(struct wl_list *layer_surfaces,
- struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
+void output_view_for_each_popup(struct sway_output *output,
+ struct sway_view *view, sway_surface_iterator_func_t iterator,
+ void *user_data) {
+ struct surface_iterator_data data = {
+ .user_iterator = iterator,
+ .user_data = user_data,
+ .output = output,
+ .ox = view->swayc->current.view_x - output->swayc->current.swayc_x,
+ .oy = view->swayc->current.view_y - output->swayc->current.swayc_y,
+ .width = view->swayc->current.view_width,
+ .height = view->swayc->current.view_height,
+ .rotation = 0, // TODO
+ };
+
+ view_for_each_popup(view, output_for_each_surface_iterator, &data);
+}
+
+void output_layer_for_each_surface(struct sway_output *output,
+ struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
void *user_data) {
struct sway_layer_surface *layer_surface;
wl_list_for_each(layer_surface, layer_surfaces, link) {
struct wlr_layer_surface *wlr_layer_surface =
layer_surface->layer_surface;
- output_surface_for_each_surface(wlr_layer_surface->surface,
- layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
+ output_surface_for_each_surface(output, wlr_layer_surface->surface,
+ layer_surface->geo.x, layer_surface->geo.y, iterator,
user_data);
}
}
-void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
- struct sway_output *output, struct root_geometry *geo,
- wlr_surface_iterator_func_t iterator, void *user_data) {
+#ifdef 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) {
struct sway_xwayland_unmanaged *unmanaged_surface;
wl_list_for_each(unmanaged_surface, unmanaged, link) {
struct wlr_xwayland_surface *xsurface =
@@ -139,22 +196,24 @@ void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
double ox = unmanaged_surface->lx - output->swayc->current.swayc_x;
double oy = unmanaged_surface->ly - output->swayc->current.swayc_y;
- output_surface_for_each_surface(xsurface->surface, ox, oy, geo,
+ output_surface_for_each_surface(output, xsurface->surface, ox, oy,
iterator, user_data);
}
}
+#endif
-void output_drag_icons_for_each_surface(struct wl_list *drag_icons,
- struct sway_output *output, struct root_geometry *geo,
- wlr_surface_iterator_func_t iterator, void *user_data) {
+void output_drag_icons_for_each_surface(struct sway_output *output,
+ struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
+ void *user_data) {
struct sway_drag_icon *drag_icon;
wl_list_for_each(drag_icon, drag_icons, link) {
double ox = drag_icon->x - output->swayc->x;
double oy = drag_icon->y - output->swayc->y;
if (drag_icon->wlr_drag_icon->mapped) {
- output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
- ox, oy, geo, iterator, user_data);
+ output_surface_for_each_surface(output,
+ drag_icon->wlr_drag_icon->surface, ox, oy,
+ iterator, user_data);
}
}
}
@@ -181,21 +240,14 @@ struct sway_container *output_get_active_workspace(struct sway_output *output) {
return workspace;
}
-bool output_has_opaque_lockscreen(struct sway_output *output,
- struct sway_seat *seat) {
- if (!seat->exclusive_client) {
- return false;
- }
-
+bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
struct wlr_layer_surface *wlr_layer_surface;
wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) {
- if (wlr_layer_surface->output != output->wlr_output) {
+ if (wlr_layer_surface->output != output->wlr_output ||
+ wlr_layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) {
continue;
}
struct wlr_surface *wlr_surface = wlr_layer_surface->surface;
- if (wlr_surface->resource->client != seat->exclusive_client) {
- continue;
- }
struct sway_layer_surface *sway_layer_surface =
layer_from_wlr_layer_surface(wlr_layer_surface);
pixman_box32_t output_box = {
@@ -217,46 +269,38 @@ bool output_has_opaque_lockscreen(struct sway_output *output,
return false;
}
-struct send_frame_done_data {
- struct root_geometry root_geo;
- struct sway_output *output;
- struct timespec *when;
- struct wl_client *exclusive_client;
-};
-
-static void send_frame_done_iterator(struct wlr_surface *surface,
- int sx, int sy, void *_data) {
- struct send_frame_done_data *data = _data;
- if (data->exclusive_client &&
- data->exclusive_client != surface->resource->client) {
- return;
- }
-
- bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
- sx, sy, NULL);
- if (intersects) {
- wlr_surface_send_frame_done(surface, data->when);
- }
+static void send_frame_done_iterator(struct sway_output *output,
+ struct wlr_surface *surface, struct wlr_box *box, float rotation,
+ void *_data) {
+ struct timespec *when = _data;
+ wlr_surface_send_frame_done(surface, when);
}
-static void send_frame_done_layer(struct send_frame_done_data *data,
- struct wl_list *layer_surfaces) {
- output_layer_for_each_surface(layer_surfaces, &data->root_geo,
- send_frame_done_iterator, data);
+static void send_frame_done_layer(struct sway_output *output,
+ struct wl_list *layer_surfaces, struct timespec *when) {
+ output_layer_for_each_surface(output, layer_surfaces,
+ send_frame_done_iterator, when);
}
-static void send_frame_done_unmanaged(struct send_frame_done_data *data,
- struct wl_list *unmanaged) {
- output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
- send_frame_done_iterator, data);
+#ifdef HAVE_XWAYLAND
+static void send_frame_done_unmanaged(struct sway_output *output,
+ struct wl_list *unmanaged, struct timespec *when) {
+ output_unmanaged_for_each_surface(output, unmanaged,
+ send_frame_done_iterator, when);
}
+#endif
-static void send_frame_done_drag_icons(struct send_frame_done_data *data,
- struct wl_list *drag_icons) {
- output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo,
- send_frame_done_iterator, data);
+static void send_frame_done_drag_icons(struct sway_output *output,
+ struct wl_list *drag_icons, struct timespec *when) {
+ output_drag_icons_for_each_surface(output, drag_icons,
+ send_frame_done_iterator, when);
}
+struct send_frame_done_data {
+ struct sway_output *output;
+ struct timespec *when;
+};
+
static void send_frame_done_container_iterator(struct sway_container *con,
void *_data) {
struct send_frame_done_data *data = _data;
@@ -268,52 +312,62 @@ static void send_frame_done_container_iterator(struct sway_container *con,
return;
}
- output_view_for_each_surface(con->sway_view, data->output, &data->root_geo,
- send_frame_done_iterator, data);
-}
-
-static void send_frame_done_container(struct send_frame_done_data *data,
- struct sway_container *con) {
- container_descendants(con, C_VIEW,
- send_frame_done_container_iterator, data);
+ output_view_for_each_surface(data->output, con->sway_view,
+ send_frame_done_iterator, data->when);
}
-static void send_frame_done(struct sway_output *output, struct timespec *when) {
- struct sway_seat *seat = input_manager_current_seat(input_manager);
+static void send_frame_done_container(struct sway_output *output,
+ struct sway_container *con, struct timespec *when) {
struct send_frame_done_data data = {
.output = output,
.when = when,
- .exclusive_client = output_has_opaque_lockscreen(output, seat) ?
- seat->exclusive_client : NULL,
};
+ container_descendants(con, C_VIEW,
+ send_frame_done_container_iterator, &data);
+}
+
+static void send_frame_done(struct sway_output *output, struct timespec *when) {
+ if (output_has_opaque_overlay_layer_surface(output)) {
+ goto send_frame_overlay;
+ }
struct sway_container *workspace = output_get_active_workspace(output);
if (workspace->current.ws_fullscreen) {
- send_frame_done_container_iterator(
- workspace->current.ws_fullscreen->swayc, &data);
-
- if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) {
- send_frame_done_unmanaged(&data,
- &root_container.sway_root->xwayland_unmanaged);
+ if (workspace->current.ws_fullscreen->type == C_VIEW) {
+ output_view_for_each_surface(output,
+ workspace->current.ws_fullscreen->sway_view,
+ send_frame_done_iterator, when);
+ } else {
+ send_frame_done_container(output, workspace->current.ws_fullscreen,
+ when);
}
+#ifdef HAVE_XWAYLAND
+ send_frame_done_unmanaged(output,
+ &root_container.sway_root->xwayland_unmanaged, when);
+#endif
} else {
- send_frame_done_layer(&data,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
- send_frame_done_layer(&data,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
-
- send_frame_done_container(&data, workspace);
- send_frame_done_container(&data, workspace->sway_workspace->floating);
-
- send_frame_done_unmanaged(&data,
- &root_container.sway_root->xwayland_unmanaged);
- send_frame_done_layer(&data,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+ send_frame_done_layer(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], when);
+ send_frame_done_layer(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], when);
+
+ send_frame_done_container(output, workspace, when);
+ send_frame_done_container(output, workspace->sway_workspace->floating,
+ when);
+
+#ifdef HAVE_XWAYLAND
+ send_frame_done_unmanaged(output,
+ &root_container.sway_root->xwayland_unmanaged, when);
+#endif
+ send_frame_done_layer(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when);
}
- send_frame_done_layer(&data,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
- send_frame_done_drag_icons(&data, &root_container.sway_root->drag_icons);
+send_frame_overlay:
+ send_frame_done_layer(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when);
+ send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons,
+ when);
}
static void damage_handle_frame(struct wl_listener *listener, void *data) {
@@ -348,26 +402,13 @@ void output_damage_whole(struct sway_output *output) {
wlr_output_damage_add_whole(output->damage);
}
-struct damage_data {
- struct root_geometry root_geo;
- struct sway_output *output;
- bool whole;
-};
-
-static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+static void damage_surface_iterator(struct sway_output *output,
+ struct wlr_surface *surface, struct wlr_box *_box, float rotation,
void *_data) {
- struct damage_data *data = _data;
- struct sway_output *output = data->output;
- float rotation = data->root_geo.rotation;
- bool whole = data->whole;
-
- struct wlr_box box;
- bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
- sx, sy, &box);
- if (!intersects) {
- return;
- }
+ bool *data = _data;
+ bool whole = *data;
+ struct wlr_box box = *_box;
scale_box(&box, output->wlr_output->scale);
int center_x = box.x + box.width/2;
@@ -407,13 +448,8 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
void output_damage_surface(struct sway_output *output, double ox, double oy,
struct wlr_surface *surface, bool whole) {
- struct damage_data data = {
- .output = output,
- .whole = whole,
- };
-
- output_surface_for_each_surface(surface, ox, oy, &data.root_geo,
- damage_surface_iterator, &data);
+ output_surface_for_each_surface(output, surface, ox, oy,
+ damage_surface_iterator, &whole);
}
static void output_damage_view(struct sway_output *output,
@@ -426,13 +462,7 @@ static void output_damage_view(struct sway_output *output,
return;
}
- struct damage_data data = {
- .output = output,
- .whole = whole,
- };
-
- output_view_for_each_surface(view, output, &data.root_geo,
- damage_surface_iterator, &data);
+ output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
}
void output_damage_from_view(struct sway_output *output,
@@ -463,11 +493,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
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.swayc_x - output->wlr_output->lx,
- .y = con->current.swayc_y - output->wlr_output->ly,
- .width = con->current.swayc_width,
- .height = con->current.swayc_height,
+ .x = con->current.swayc_x - output->wlr_output->lx - 1,
+ .y = con->current.swayc_y - output->wlr_output->ly - 1,
+ .width = con->current.swayc_width + 2,
+ .height = con->current.swayc_height + 2,
};
scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box);
@@ -509,22 +540,14 @@ static void handle_transform(struct wl_listener *listener, void *data) {
transaction_commit_dirty();
}
-static void handle_scale_iterator(struct sway_container *view, void *data) {
- view_update_marks_textures(view->sway_view);
-}
-
static void handle_scale(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, scale);
arrange_layers(output);
- container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
+ container_update_textures_recursive(output->swayc);
arrange_windows(output->swayc);
transaction_commit_dirty();
}
-struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
- return wlr_output->data;
-}
-
void handle_new_output(struct wl_listener *listener, void *data) {
struct sway_server *server = wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 4c85e516..cdac9c72 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -14,6 +14,7 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/util/region.h>
#include "log.h"
+#include "config.h"
#include "sway/config.h"
#include "sway/debug.h"
#include "sway/input/input-manager.h"
@@ -28,10 +29,7 @@
#include "sway/tree/workspace.h"
struct render_data {
- struct root_geometry root_geo;
- struct sway_output *output;
pixman_region32_t *damage;
- struct sway_view *view;
float alpha;
};
@@ -91,11 +89,11 @@ damage_finish:
pixman_region32_fini(&damage);
}
-static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+static void render_surface_iterator(struct sway_output *output,
+ struct wlr_surface *surface, struct wlr_box *_box, float rotation,
void *_data) {
struct render_data *data = _data;
- struct wlr_output *wlr_output = data->output->wlr_output;
- float rotation = data->root_geo.rotation;
+ struct wlr_output *wlr_output = output->wlr_output;
pixman_region32_t *output_damage = data->damage;
float alpha = data->alpha;
@@ -104,13 +102,7 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
return;
}
- struct wlr_box box;
- bool intersects = output_get_surface_box(&data->root_geo, data->output,
- surface, sx, sy, &box);
- if (!intersects) {
- return;
- }
-
+ struct wlr_box box = *_box;
scale_box(&box, wlr_output->scale);
float matrix[9];
@@ -125,33 +117,32 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
static void render_layer(struct sway_output *output,
pixman_region32_t *damage, struct wl_list *layer_surfaces) {
struct render_data data = {
- .output = output,
.damage = damage,
.alpha = 1.0f,
};
- output_layer_for_each_surface(layer_surfaces, &data.root_geo,
+ output_layer_for_each_surface(output, layer_surfaces,
render_surface_iterator, &data);
}
+#ifdef HAVE_XWAYLAND
static void render_unmanaged(struct sway_output *output,
pixman_region32_t *damage, struct wl_list *unmanaged) {
struct render_data data = {
- .output = output,
.damage = damage,
.alpha = 1.0f,
};
- output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
+ output_unmanaged_for_each_surface(output, unmanaged,
render_surface_iterator, &data);
}
+#endif
static void render_drag_icons(struct sway_output *output,
pixman_region32_t *damage, struct wl_list *drag_icons) {
struct render_data data = {
- .output = output,
.damage = damage,
.alpha = 1.0f,
};
- output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
+ output_drag_icons_for_each_surface(output, drag_icons,
render_surface_iterator, &data);
}
@@ -195,33 +186,51 @@ static void premultiply_alpha(float color[4], float opacity) {
color[2] *= color[3];
}
-static void render_view_surfaces(struct sway_view *view,
+static void render_view_toplevels(struct sway_view *view,
struct sway_output *output, pixman_region32_t *damage, float alpha) {
struct render_data data = {
- .output = output,
.damage = damage,
- .view = view,
.alpha = alpha,
};
- output_view_for_each_surface(view, output, &data.root_geo,
- render_surface_iterator, &data);
+ // Render all toplevels without descending into popups
+ output_surface_for_each_surface(output, view->surface,
+ view->swayc->current.view_x - output->wlr_output->lx,
+ view->swayc->current.view_y - output->wlr_output->ly,
+ render_surface_iterator, &data);
+}
+
+static void render_popup_iterator(struct sway_output *output,
+ struct wlr_surface *surface, struct wlr_box *box, float rotation,
+ void *data) {
+ // Render this popup's surface
+ render_surface_iterator(output, surface, box, rotation, data);
+
+ // Render this popup's child toplevels
+ output_surface_for_each_surface(output, surface, box->x, box->y,
+ render_surface_iterator, data);
+}
+
+static void render_view_popups(struct sway_view *view,
+ struct sway_output *output, pixman_region32_t *damage, float alpha) {
+ struct render_data data = {
+ .damage = damage,
+ .alpha = alpha,
+ };
+ output_view_for_each_popup(output, view, render_popup_iterator, &data);
}
static void render_saved_view(struct sway_view *view,
struct sway_output *output, pixman_region32_t *damage, float alpha) {
struct wlr_output *wlr_output = output->wlr_output;
- int width, height;
- struct wlr_texture *texture =
- transaction_get_saved_texture(view, &width, &height);
- if (!texture) {
+ if (!view->saved_buffer || !view->saved_buffer->texture) {
return;
}
struct wlr_box box = {
.x = view->swayc->current.view_x - output->swayc->current.swayc_x,
.y = view->swayc->current.view_y - output->swayc->current.swayc_y,
- .width = width,
- .height = height,
+ .width = view->saved_buffer_width,
+ .height = view->saved_buffer_height,
};
struct wlr_box output_box = {
@@ -241,7 +250,8 @@ static void render_saved_view(struct sway_view *view,
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
wlr_output->transform_matrix);
- render_texture(wlr_output, damage, texture, &box, matrix, alpha);
+ render_texture(wlr_output, damage, view->saved_buffer->texture,
+ &box, matrix, alpha);
}
/**
@@ -250,10 +260,10 @@ static void render_saved_view(struct sway_view *view,
static void render_view(struct sway_output *output, pixman_region32_t *damage,
struct sway_container *con, struct border_colors *colors) {
struct sway_view *view = con->sway_view;
- if (view->swayc->instructions->length) {
+ if (view->saved_buffer) {
render_saved_view(view, output, damage, view->swayc->alpha);
} else {
- render_view_surfaces(view, output, damage, view->swayc->alpha);
+ render_view_toplevels(view, output, damage, view->swayc->alpha);
}
if (view->using_csd) {
@@ -778,7 +788,7 @@ static void render_floating_container(struct sway_output *soutput,
}
render_view(soutput, damage, con, colors);
} else {
- render_container(soutput, damage, con, false);
+ render_container(soutput, damage, con, con->current.focused);
}
}
@@ -835,22 +845,13 @@ void output_render(struct sway_output *output, struct timespec *when,
}
struct sway_container *workspace = output_get_active_workspace(output);
- struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
- struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *fullscreen_con = workspace->current.ws_fullscreen;
+
+ if (output_has_opaque_overlay_layer_surface(output)) {
+ goto render_overlay;
+ }
- if (output_has_opaque_lockscreen(output, seat)) {
- struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer;
- struct sway_layer_surface *sway_layer_surface =
- layer_from_wlr_layer_surface(seat->focused_layer);
- struct render_data data = {
- .output = output,
- .damage = damage,
- .alpha = 1.0f,
- };
- output_surface_for_each_surface(wlr_layer_surface->surface,
- sway_layer_surface->geo.x, sway_layer_surface->geo.y,
- &data.root_geo, render_surface_iterator, &data);
- } else if (fullscreen_view) {
+ if (fullscreen_con) {
float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
int nrects;
@@ -861,16 +862,22 @@ void output_render(struct sway_output *output, struct timespec *when,
}
// TODO: handle views smaller than the output
- if (fullscreen_view->swayc->instructions->length) {
- render_saved_view(fullscreen_view, output, damage, 1.0f);
+ if (fullscreen_con->type == C_VIEW) {
+ if (fullscreen_con->sway_view->saved_buffer) {
+ render_saved_view(fullscreen_con->sway_view,
+ output, damage, 1.0f);
+ } else {
+ render_view_toplevels(fullscreen_con->sway_view,
+ output, damage, 1.0f);
+ }
} else {
- render_view_surfaces(fullscreen_view, output, damage, 1.0f);
- }
-
- if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
- render_unmanaged(output, damage,
- &root_container.sway_root->xwayland_unmanaged);
+ render_container(output, damage, fullscreen_con,
+ fullscreen_con->current.focused);
}
+#ifdef HAVE_XWAYLAND
+ render_unmanaged(output, damage,
+ &root_container.sway_root->xwayland_unmanaged);
+#endif
} else {
float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
@@ -888,12 +895,21 @@ void output_render(struct sway_output *output, struct timespec *when,
render_container(output, damage, workspace, workspace->current.focused);
render_floating(output, damage);
-
+#ifdef HAVE_XWAYLAND
render_unmanaged(output, damage,
&root_container.sway_root->xwayland_unmanaged);
+#endif
render_layer(output, damage,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
}
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus && focus->type == C_VIEW) {
+ render_view_popups(focus->sway_view, output, damage, focus->alpha);
+ }
+
+render_overlay:
render_layer(output, damage,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 19f41efc..4e6af86a 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 200809L
+#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -40,8 +41,6 @@ struct sway_transaction_instruction {
struct sway_transaction *transaction;
struct sway_container *container;
struct sway_container_state state;
- struct wlr_buffer *saved_buffer;
- int saved_buffer_width, saved_buffer_height;
uint32_t serial;
bool ready;
};
@@ -56,27 +55,6 @@ static struct sway_transaction *transaction_create() {
return transaction;
}
-static void remove_saved_view_buffer(
- struct sway_transaction_instruction *instruction) {
- if (instruction->saved_buffer) {
- wlr_buffer_unref(instruction->saved_buffer);
- instruction->saved_buffer = NULL;
- }
-}
-
-static void save_view_buffer(struct sway_view *view,
- struct sway_transaction_instruction *instruction) {
- if (!sway_assert(instruction->saved_buffer == NULL,
- "Didn't expect instruction to have a saved buffer already")) {
- remove_saved_view_buffer(instruction);
- }
- if (view->surface && wlr_surface_has_buffer(view->surface)) {
- instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer);
- instruction->saved_buffer_width = view->surface->current.width;
- instruction->saved_buffer_height = view->surface->current.height;
- }
-}
-
static void transaction_destroy(struct sway_transaction *transaction) {
// Free instructions
for (int i = 0; i < transaction->instructions->length; ++i) {
@@ -92,7 +70,6 @@ static void transaction_destroy(struct sway_transaction *transaction) {
if (con->destroying && !con->instructions->length) {
container_free(con);
}
- remove_saved_view_buffer(instruction);
free(instruction);
}
list_free(transaction->instructions);
@@ -110,6 +87,7 @@ static void copy_pending_state(struct sway_container *container,
state->swayc_y = container->y;
state->swayc_width = container->width;
state->swayc_height = container->height;
+ state->is_fullscreen = container->is_fullscreen;
state->has_gaps = container->has_gaps;
state->current_gaps = container->current_gaps;
state->gaps_inner = container->gaps_inner;
@@ -122,7 +100,6 @@ static void copy_pending_state(struct sway_container *container,
state->view_y = view->y;
state->view_width = view->width;
state->view_height = view->height;
- state->is_fullscreen = view->is_fullscreen;
state->border = view->border;
state->border_thickness = view->border_thickness;
state->border_top = view->border_top;
@@ -157,9 +134,6 @@ static void transaction_add_container(struct sway_transaction *transaction,
copy_pending_state(container, &instruction->state);
- if (container->type == C_VIEW) {
- save_view_buffer(container->sway_view, instruction);
- }
list_add(transaction->instructions, instruction);
}
@@ -219,27 +193,35 @@ static void transaction_apply(struct sway_transaction *transaction) {
memcpy(&container->current, &instruction->state,
sizeof(struct sway_container_state));
+
+ if (container->type == C_VIEW) {
+ if (container->destroying) {
+ if (container->instructions->length == 1 &&
+ container->sway_view->saved_buffer) {
+ view_remove_saved_buffer(container->sway_view);
+ }
+ } else {
+ if (container->sway_view->saved_buffer) {
+ view_remove_saved_buffer(container->sway_view);
+ }
+ if (container->instructions->length > 1) {
+ view_save_buffer(container->sway_view);
+ }
+ }
+ }
}
}
-/**
- * For simplicity, we only progress the queue if it can be completely flushed.
- */
static void transaction_progress_queue() {
- // We iterate this list in reverse because we're more likely to find a
- // waiting transactions at the end of the list.
- for (int i = server.transactions->length - 1; i >= 0; --i) {
- struct sway_transaction *transaction = server.transactions->items[i];
+ while (server.transactions->length) {
+ struct sway_transaction *transaction = server.transactions->items[0];
if (transaction->num_waiting) {
return;
}
- }
- for (int i = 0; i < server.transactions->length; ++i) {
- struct sway_transaction *transaction = server.transactions->items[i];
transaction_apply(transaction);
transaction_destroy(transaction);
+ list_del(server.transactions, 0);
}
- server.transactions->length = 0;
idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
}
@@ -302,6 +284,9 @@ static void transaction_commit(struct sway_transaction *transaction) {
struct timespec when;
wlr_surface_send_frame_done(con->sway_view->surface, &when);
}
+ if (con->type == C_VIEW && !con->sway_view->saved_buffer) {
+ view_save_buffer(con->sway_view);
+ }
list_add(con->instructions, instruction);
}
transaction->num_configures = transaction->num_waiting;
@@ -324,7 +309,14 @@ static void transaction_commit(struct sway_transaction *transaction) {
// Set up a timer which the views must respond within
transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
handle_timeout, transaction);
- wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
+ if (transaction->timer) {
+ wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
+ } else {
+ wlr_log(WLR_ERROR, "Unable to create transaction timer (%s). "
+ "Some imperfect frames might be rendered.",
+ strerror(errno));
+ handle_timeout(transaction);
+ }
}
// The debug tree shows the pending/live tree. Here is a good place to
@@ -352,13 +344,11 @@ static void set_instruction_ready(
}
- // If all views are ready, apply the transaction.
// If the transaction has timed out then its num_waiting will be 0 already.
if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
if (!txn_debug) {
wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
wl_event_source_timer_update(transaction->timer, 0);
- transaction_progress_queue();
}
}
}
@@ -375,6 +365,7 @@ static void set_instructions_ready(struct sway_view *view, int index) {
set_instruction_ready(instruction);
}
}
+ transaction_progress_queue();
}
void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) {
@@ -401,18 +392,6 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
}
}
-struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
- int *width, int *height) {
- struct sway_transaction_instruction *instruction =
- view->swayc->instructions->items[0];
- if (!instruction->saved_buffer) {
- return NULL;
- }
- *width = instruction->saved_buffer_width;
- *height = instruction->saved_buffer_height;
- return instruction->saved_buffer->texture;
-}
-
void transaction_commit_dirty(void) {
if (!server.dirty_containers->length) {
return;
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 98c16faf..b364663d 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 199309L
+#include <float.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
@@ -95,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
return (struct sway_xdg_shell_view *)view;
}
+static void get_constraints(struct sway_view *view, double *min_width,
+ double *max_width, double *min_height, double *max_height) {
+ struct wlr_xdg_toplevel_state *state =
+ &view->wlr_xdg_surface->toplevel->current;
+ *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
+ *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
+ *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
+ *max_height = state->max_height > 0 ? state->max_height : DBL_MAX;
+}
+
static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {
if (xdg_shell_view_from_view(view) == NULL) {
return NULL;
@@ -168,6 +179,14 @@ static void for_each_surface(struct sway_view *view,
user_data);
}
+static void for_each_popup(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (xdg_shell_view_from_view(view) == NULL) {
+ return;
+ }
+ wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data);
+}
+
static void _close(struct sway_view *view) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
@@ -178,6 +197,18 @@ static void _close(struct sway_view *view) {
}
}
+static void close_popups_iterator(struct wlr_surface *surface,
+ int sx, int sy, void *data) {
+ struct wlr_xdg_surface *xdg_surface =
+ wlr_xdg_surface_from_wlr_surface(surface);
+ wlr_xdg_surface_send_close(xdg_surface);
+}
+
+static void close_popups(struct sway_view *view) {
+ wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface,
+ close_popups_iterator, NULL);
+}
+
static void destroy(struct sway_view *view) {
struct sway_xdg_shell_view *xdg_shell_view =
xdg_shell_view_from_view(view);
@@ -188,6 +219,7 @@ static void destroy(struct sway_view *view) {
}
static const struct sway_view_impl view_impl = {
+ .get_constraints = get_constraints,
.get_string_prop = get_string_prop,
.configure = configure,
.set_activated = set_activated,
@@ -195,7 +227,9 @@ static const struct sway_view_impl view_impl = {
.set_fullscreen = set_fullscreen,
.wants_floating = wants_floating,
.for_each_surface = for_each_surface,
+ .for_each_popup = for_each_popup,
.close = _close,
+ .close_popups = close_popups,
.destroy = destroy,
};
@@ -213,10 +247,24 @@ static void handle_commit(struct wl_listener *listener, void *data) {
transaction_notify_view_ready(view, xdg_surface->configure_serial);
}
- view_update_title(view, false);
view_damage_from(view);
}
+static void handle_set_title(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, set_title);
+ struct sway_view *view = &xdg_shell_view->view;
+ view_update_title(view, false);
+ view_execute_criteria(view);
+}
+
+static void handle_set_app_id(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, set_app_id);
+ struct sway_view *view = &xdg_shell_view->view;
+ view_execute_criteria(view);
+}
+
static void handle_new_popup(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, new_popup);
@@ -241,13 +289,41 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
return;
}
- view_set_fullscreen(view, e->fullscreen);
+ container_set_fullscreen(view->swayc, e->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
arrange_windows(output);
transaction_commit_dirty();
}
+static void handle_request_move(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, request_move);
+ struct sway_view *view = &xdg_shell_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_move_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_move(seat, view->swayc, seat->last_button);
+ }
+}
+
+static void handle_request_resize(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, request_resize);
+ struct sway_view *view = &xdg_shell_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_resize_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
+ }
+}
+
static void handle_unmap(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, unmap);
@@ -262,6 +338,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->new_popup.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
+ wl_list_remove(&xdg_shell_view->request_move.link);
+ wl_list_remove(&xdg_shell_view->request_resize.link);
+ wl_list_remove(&xdg_shell_view->set_title.link);
+ wl_list_remove(&xdg_shell_view->set_app_id.link);
}
static void handle_map(struct wl_listener *listener, void *data) {
@@ -280,7 +360,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
view_map(view, view->wlr_xdg_surface->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) {
- view_set_fullscreen(view, true);
+ container_set_fullscreen(view->swayc, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
arrange_windows(ws);
} else {
@@ -299,6 +379,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
&xdg_shell_view->request_fullscreen);
+
+ xdg_shell_view->request_move.notify = handle_request_move;
+ wl_signal_add(&xdg_surface->toplevel->events.request_move,
+ &xdg_shell_view->request_move);
+
+ xdg_shell_view->request_resize.notify = handle_request_resize;
+ wl_signal_add(&xdg_surface->toplevel->events.request_resize,
+ &xdg_shell_view->request_resize);
+
+ xdg_shell_view->set_title.notify = handle_set_title;
+ wl_signal_add(&xdg_surface->toplevel->events.set_title,
+ &xdg_shell_view->set_title);
+
+ xdg_shell_view->set_app_id.notify = handle_set_app_id;
+ wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
+ &xdg_shell_view->set_app_id);
}
static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -344,9 +440,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
xdg_shell_view->view.wlr_xdg_surface = xdg_surface;
- // TODO:
- // - Look up pid and open on appropriate workspace
-
xdg_shell_view->map.notify = handle_map;
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 4d76f0a7..ffea03ad 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 199309L
+#include <float.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
@@ -94,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
return (struct sway_xdg_shell_v6_view *)view;
}
+static void get_constraints(struct sway_view *view, double *min_width,
+ double *max_width, double *min_height, double *max_height) {
+ struct wlr_xdg_toplevel_v6_state *state =
+ &view->wlr_xdg_surface_v6->toplevel->current;
+ *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
+ *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
+ *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
+ *max_height = state->max_height > 0 ? state->max_height : DBL_MAX;
+}
+
static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {
if (xdg_shell_v6_view_from_view(view) == NULL) {
return NULL;
@@ -164,6 +175,15 @@ static void for_each_surface(struct sway_view *view,
user_data);
}
+static void for_each_popup(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator,
+ user_data);
+}
+
static void _close(struct sway_view *view) {
if (xdg_shell_v6_view_from_view(view) == NULL) {
return;
@@ -174,6 +194,18 @@ static void _close(struct sway_view *view) {
}
}
+static void close_popups_iterator(struct wlr_surface *surface,
+ int sx, int sy, void *data) {
+ struct wlr_xdg_surface_v6 *xdg_surface_v6 =
+ wlr_xdg_surface_v6_from_wlr_surface(surface);
+ wlr_xdg_surface_v6_send_close(xdg_surface_v6);
+}
+
+static void close_popups(struct sway_view *view) {
+ wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6,
+ close_popups_iterator, NULL);
+}
+
static void destroy(struct sway_view *view) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
xdg_shell_v6_view_from_view(view);
@@ -184,6 +216,7 @@ static void destroy(struct sway_view *view) {
}
static const struct sway_view_impl view_impl = {
+ .get_constraints = get_constraints,
.get_string_prop = get_string_prop,
.configure = configure,
.set_activated = set_activated,
@@ -191,7 +224,9 @@ static const struct sway_view_impl view_impl = {
.set_fullscreen = set_fullscreen,
.wants_floating = wants_floating,
.for_each_surface = for_each_surface,
+ .for_each_popup = for_each_popup,
.close = _close,
+ .close_popups = close_popups,
.destroy = destroy,
};
@@ -208,10 +243,24 @@ static void handle_commit(struct wl_listener *listener, void *data) {
transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
}
- view_update_title(view, false);
view_damage_from(view);
}
+static void handle_set_title(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, set_title);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ view_update_title(view, false);
+ view_execute_criteria(view);
+}
+
+static void handle_set_app_id(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, set_app_id);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ view_execute_criteria(view);
+}
+
static void handle_new_popup(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, new_popup);
@@ -236,13 +285,41 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
return;
}
- view_set_fullscreen(view, e->fullscreen);
+ container_set_fullscreen(view->swayc, e->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
arrange_windows(output);
transaction_commit_dirty();
}
+static void handle_request_move(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, request_move);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_v6_move_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_move(seat, view->swayc, seat->last_button);
+ }
+}
+
+static void handle_request_resize(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, request_resize);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_v6_resize_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
+ }
+}
+
static void handle_unmap(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, unmap);
@@ -257,6 +334,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_v6_view->commit.link);
wl_list_remove(&xdg_shell_v6_view->new_popup.link);
wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
+ wl_list_remove(&xdg_shell_v6_view->request_move.link);
+ wl_list_remove(&xdg_shell_v6_view->request_resize.link);
+ wl_list_remove(&xdg_shell_v6_view->set_title.link);
+ wl_list_remove(&xdg_shell_v6_view->set_app_id.link);
}
static void handle_map(struct wl_listener *listener, void *data) {
@@ -275,7 +356,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
view_map(view, view->wlr_xdg_surface_v6->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) {
- view_set_fullscreen(view, true);
+ container_set_fullscreen(view->swayc, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
arrange_windows(ws);
} else {
@@ -294,6 +375,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
&xdg_shell_v6_view->request_fullscreen);
+
+ xdg_shell_v6_view->request_move.notify = handle_request_move;
+ wl_signal_add(&xdg_surface->toplevel->events.request_move,
+ &xdg_shell_v6_view->request_move);
+
+ xdg_shell_v6_view->request_resize.notify = handle_request_resize;
+ wl_signal_add(&xdg_surface->toplevel->events.request_resize,
+ &xdg_shell_v6_view->request_resize);
+
+ xdg_shell_v6_view->set_title.notify = handle_set_title;
+ wl_signal_add(&xdg_surface->toplevel->events.set_title,
+ &xdg_shell_v6_view->set_title);
+
+ xdg_shell_v6_view->set_app_id.notify = handle_set_app_id;
+ wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
+ &xdg_shell_v6_view->set_app_id);
}
static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -335,9 +432,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
- // TODO:
- // - Look up pid and open on appropriate workspace
-
xdg_shell_v6_view->map.notify = handle_map;
wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index bce0a37b..398446f8 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -69,11 +69,13 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
surface->ly = xsurface->y;
desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct wlr_xwayland *xwayland =
- seat->input->server->xwayland.wlr_xwayland;
- wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
- seat_set_focus_surface(seat, xsurface->surface, false);
+ if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct wlr_xwayland *xwayland =
+ seat->input->server->xwayland.wlr_xwayland;
+ wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
+ seat_set_focus_surface(seat, xsurface->surface, false);
+ }
}
static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
@@ -305,6 +307,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&xwayland_view->destroy.link);
wl_list_remove(&xwayland_view->request_configure.link);
wl_list_remove(&xwayland_view->request_fullscreen.link);
+ wl_list_remove(&xwayland_view->request_move.link);
+ wl_list_remove(&xwayland_view->request_resize.link);
wl_list_remove(&xwayland_view->set_title.link);
wl_list_remove(&xwayland_view->set_class.link);
wl_list_remove(&xwayland_view->set_window_type.link);
@@ -355,7 +359,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
view_map(view, xsurface->surface);
if (xsurface->fullscreen) {
- view_set_fullscreen(view, true);
+ container_set_fullscreen(view->swayc, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
arrange_windows(ws);
} else {
@@ -393,13 +397,44 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
if (!xsurface->mapped) {
return;
}
- view_set_fullscreen(view, xsurface->fullscreen);
+ container_set_fullscreen(view->swayc, xsurface->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
arrange_windows(output);
transaction_commit_dirty();
}
+static void handle_request_move(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, request_move);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (!xsurface->mapped) {
+ return;
+ }
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ seat_begin_move(seat, view->swayc, seat->last_button);
+}
+
+static void handle_request_resize(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, request_resize);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (!xsurface->mapped) {
+ return;
+ }
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xwayland_resize_event *e = data;
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
+}
+
static void handle_set_title(struct wl_listener *listener, void *data) {
struct sway_xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, set_title);
@@ -481,9 +516,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
xwayland_view->view.wlr_xwayland_surface = xsurface;
- // TODO:
- // - Look up pid and open on appropriate workspace
-
wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
xwayland_view->destroy.notify = handle_destroy;
@@ -495,6 +527,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
&xwayland_view->request_fullscreen);
xwayland_view->request_fullscreen.notify = handle_request_fullscreen;
+ wl_signal_add(&xsurface->events.request_move,
+ &xwayland_view->request_move);
+ xwayland_view->request_move.notify = handle_request_move;
+
+ wl_signal_add(&xsurface->events.request_resize,
+ &xwayland_view->request_resize);
+ xwayland_view->request_resize.notify = handle_request_resize;
+
wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title);
xwayland_view->set_title.notify = handle_set_title;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index c76c20b3..c2fc4e9e 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -5,15 +5,20 @@
#elif __FreeBSD__
#include <dev/evdev/input-event-codes.h>
#endif
+#include <limits.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_idle.h>
#include "list.h"
#include "log.h"
+#include "config.h"
+#include "sway/desktop.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
+#include "sway/input/keyboard.h"
#include "sway/layers.h"
#include "sway/output.h"
+#include "sway/tree/arrange.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "wlr-layer-shell-unstable-v1-protocol.h"
@@ -50,6 +55,7 @@ static struct sway_container *container_at_coords(
struct sway_seat *seat, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
// check for unmanaged views first
+#ifdef HAVE_XWAYLAND
struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
struct sway_xwayland_unmanaged *unmanaged_surface;
wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
@@ -65,7 +71,7 @@ static struct sway_container *container_at_coords(
return NULL;
}
}
-
+#endif
// find the output the cursor is on
struct wlr_output_layout *output_layout =
root_container.sway_root->output_layout;
@@ -93,14 +99,8 @@ static struct sway_container *container_at_coords(
return ws;
}
if (ws->sway_workspace->fullscreen) {
- struct wlr_surface *wlr_surface = ws->sway_workspace->fullscreen->surface;
- if (wlr_surface_point_accepts_input(wlr_surface, ox, oy)) {
- *sx = ox;
- *sy = oy;
- *surface = wlr_surface;
- return ws->sway_workspace->fullscreen->swayc;
- }
- return NULL;
+ return container_at_view(ws->sway_workspace->fullscreen, lx, ly,
+ surface, sx, sy);
}
if ((*surface = layer_surface_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
@@ -109,9 +109,6 @@ static struct sway_container *container_at_coords(
}
struct sway_container *c;
- if ((c = floating_container_at(lx, ly, surface, sx, sy))) {
- return c;
- }
if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
return c;
}
@@ -127,7 +124,7 @@ static struct sway_container *container_at_coords(
return ws;
}
- c = seat_get_focus_inactive(seat, output->swayc);
+ c = seat_get_active_child(seat, output->swayc);
if (c) {
return c;
}
@@ -139,6 +136,171 @@ static struct sway_container *container_at_coords(
return output->swayc;
}
+static enum wlr_edges find_resize_edge(struct sway_container *cont,
+ struct sway_cursor *cursor) {
+ if (cont->type != C_VIEW) {
+ return WLR_EDGE_NONE;
+ }
+ struct sway_view *view = cont->sway_view;
+ if (view->border == B_NONE || !view->border_thickness || view->using_csd) {
+ return WLR_EDGE_NONE;
+ }
+
+ enum wlr_edges edge = 0;
+ if (cursor->cursor->x < cont->x + view->border_thickness) {
+ edge |= WLR_EDGE_LEFT;
+ }
+ if (cursor->cursor->y < cont->y + view->border_thickness) {
+ edge |= WLR_EDGE_TOP;
+ }
+ if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) {
+ edge |= WLR_EDGE_RIGHT;
+ }
+ if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) {
+ edge |= WLR_EDGE_BOTTOM;
+ }
+ return edge;
+}
+
+static void handle_move_motion(struct sway_seat *seat,
+ struct sway_cursor *cursor) {
+ struct sway_container *con = seat->op_container;
+ desktop_damage_whole_container(con);
+ container_floating_translate(con,
+ cursor->cursor->x - cursor->previous.x,
+ cursor->cursor->y - cursor->previous.y);
+ desktop_damage_whole_container(con);
+}
+
+static void calculate_floating_constraints(struct sway_container *con,
+ int *min_width, int *max_width, int *min_height, int *max_height) {
+ if (config->floating_minimum_width == -1) { // no minimum
+ *min_width = 0;
+ } else if (config->floating_minimum_width == 0) { // automatic
+ *min_width = 75;
+ } else {
+ *min_width = config->floating_minimum_width;
+ }
+
+ if (config->floating_minimum_height == -1) { // no minimum
+ *min_height = 0;
+ } else if (config->floating_minimum_height == 0) { // automatic
+ *min_height = 50;
+ } else {
+ *min_height = config->floating_minimum_height;
+ }
+
+ if (config->floating_maximum_width == -1) { // no maximum
+ *max_width = INT_MAX;
+ } else if (config->floating_maximum_width == 0) { // automatic
+ struct sway_container *ws = container_parent(con, C_WORKSPACE);
+ *max_width = ws->width;
+ } else {
+ *max_width = config->floating_maximum_width;
+ }
+
+ if (config->floating_maximum_height == -1) { // no maximum
+ *max_height = INT_MAX;
+ } else if (config->floating_maximum_height == 0) { // automatic
+ struct sway_container *ws = container_parent(con, C_WORKSPACE);
+ *max_height = ws->height;
+ } else {
+ *max_height = config->floating_maximum_height;
+ }
+}
+
+static void handle_resize_motion(struct sway_seat *seat,
+ struct sway_cursor *cursor) {
+ struct sway_container *con = seat->op_container;
+ enum wlr_edges edge = seat->op_resize_edge;
+
+ // The amount the mouse has moved since the start of the resize operation
+ // Positive is down/right
+ double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
+ double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
+
+ if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
+ mouse_move_x = 0;
+ }
+ if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
+ mouse_move_y = 0;
+ }
+
+ double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
+ double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
+
+ if (seat->op_resize_preserve_ratio) {
+ double x_multiplier = grow_width / seat->op_ref_width;
+ double y_multiplier = grow_height / seat->op_ref_height;
+ double max_multiplier = fmax(x_multiplier, y_multiplier);
+ grow_width = seat->op_ref_width * max_multiplier;
+ grow_height = seat->op_ref_height * max_multiplier;
+ }
+
+ // Determine new width/height, and accommodate for floating min/max values
+ double width = seat->op_ref_width + grow_width;
+ double height = seat->op_ref_height + grow_height;
+ int min_width, max_width, min_height, max_height;
+ calculate_floating_constraints(con, &min_width, &max_width,
+ &min_height, &max_height);
+ width = fmax(min_width, fmin(width, max_width));
+ height = fmax(min_height, fmin(height, max_height));
+
+ // Apply the view's min/max size
+ if (con->type == C_VIEW) {
+ double view_min_width, view_max_width, view_min_height, view_max_height;
+ view_get_constraints(con->sway_view, &view_min_width, &view_max_width,
+ &view_min_height, &view_max_height);
+ width = fmax(view_min_width, fmin(width, view_max_width));
+ height = fmax(view_min_height, fmin(height, view_max_height));
+ }
+
+ // Recalculate these, in case we hit a min/max limit
+ grow_width = width - seat->op_ref_width;
+ grow_height = height - seat->op_ref_height;
+
+ // Determine grow x/y values - these are relative to the container's x/y at
+ // the start of the resize operation.
+ double grow_x = 0, grow_y = 0;
+ if (edge & WLR_EDGE_LEFT) {
+ grow_x = -grow_width;
+ } else if (edge & WLR_EDGE_RIGHT) {
+ grow_x = 0;
+ } else {
+ grow_x = -grow_width / 2;
+ }
+ if (edge & WLR_EDGE_TOP) {
+ grow_y = -grow_height;
+ } else if (edge & WLR_EDGE_BOTTOM) {
+ grow_y = 0;
+ } else {
+ grow_y = -grow_height / 2;
+ }
+
+ // Determine the amounts we need to bump everything relative to the current
+ // size.
+ int relative_grow_width = width - con->width;
+ int relative_grow_height = height - con->height;
+ int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
+ int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
+
+ // Actually resize stuff
+ con->x += relative_grow_x;
+ con->y += relative_grow_y;
+ con->width += relative_grow_width;
+ con->height += relative_grow_height;
+
+ if (con->type == C_VIEW) {
+ struct sway_view *view = con->sway_view;
+ view->x += relative_grow_x;
+ view->y += relative_grow_y;
+ view->width += relative_grow_width;
+ view->height += relative_grow_height;
+ }
+
+ arrange_windows(con);
+}
+
void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
bool allow_refocusing) {
if (time_msec == 0) {
@@ -146,6 +308,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
}
struct sway_seat *seat = cursor->seat;
+
+ if (seat->operation != OP_NONE) {
+ if (seat->operation == OP_MOVE) {
+ handle_move_motion(seat, cursor);
+ } else {
+ handle_resize_motion(seat, cursor);
+ }
+ cursor->previous.x = cursor->cursor->x;
+ cursor->previous.y = cursor->cursor->y;
+ return;
+ }
+
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_surface *surface = NULL;
double sx, sy;
@@ -172,7 +346,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
output = container_parent(c, C_OUTPUT);
}
if (output != focus) {
- seat_set_focus_warp(seat, c, false);
+ seat_set_focus_warp(seat, c, false, true);
}
} else if (c->type == C_VIEW) {
// Focus c if the following are true:
@@ -182,27 +356,33 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
c != prev_c &&
view_is_visible(c->sway_view)) {
- seat_set_focus_warp(seat, c, false);
+ seat_set_focus_warp(seat, c, false, true);
} else {
struct sway_container *next_focus =
seat_get_focus_inactive(seat, &root_container);
if (next_focus && next_focus->type == C_VIEW &&
view_is_visible(next_focus->sway_view)) {
- seat_set_focus_warp(seat, next_focus, false);
+ seat_set_focus_warp(seat, next_focus, false, true);
}
}
}
}
- // reset cursor if switching between clients
- struct wl_client *client = NULL;
- if (surface != NULL) {
- client = wl_resource_get_client(surface->resource);
- }
- if (client != cursor->image_client) {
- wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
- "left_ptr", cursor->cursor);
- cursor->image_client = client;
+ // Handle cursor image
+ if (surface) {
+ // Reset cursor if switching between clients
+ struct wl_client *client = wl_resource_get_client(surface->resource);
+ if (client != cursor->image_client) {
+ cursor_set_image(cursor, "left_ptr", client);
+ }
+ } else if (c && container_is_floating(c)) {
+ // Try a floating container's resize edge
+ enum wlr_edges edge = find_resize_edge(c, cursor);
+ const char *image = edge == WLR_EDGE_NONE ?
+ "left_ptr" : wlr_xcursor_get_resize_name(edge);
+ cursor_set_image(cursor, image, NULL);
+ } else {
+ cursor_set_image(cursor, "left_ptr", NULL);
}
// send pointer enter/leave
@@ -243,8 +423,142 @@ static void handle_cursor_motion_absolute(
transaction_commit_dirty();
}
+static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
+ uint32_t time_msec, uint32_t button, enum wlr_button_state state,
+ struct wlr_surface *surface, double sx, double sy,
+ struct sway_container *cont) {
+ struct sway_seat *seat = cursor->seat;
+
+ // Deny moving or resizing a fullscreen container
+ if (container_is_fullscreen_or_child(cont)) {
+ seat_pointer_notify_button(seat, time_msec, button, state);
+ return;
+ }
+ struct sway_container *floater = cont;
+ while (floater->parent->layout != L_FLOATING) {
+ floater = floater->parent;
+ }
+
+ struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
+ bool mod_pressed = keyboard &&
+ (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod);
+ enum wlr_edges edge = find_resize_edge(floater, cursor);
+ bool over_title = edge == WLR_EDGE_NONE && !surface;
+
+ // Check for beginning move
+ uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
+ if (button == btn_move && state == WLR_BUTTON_PRESSED &&
+ (mod_pressed || over_title)) {
+ seat_begin_move(seat, floater, button);
+ return;
+ }
+
+ // Check for beginning resize
+ bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE;
+ uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT;
+ bool resizing_via_mod = button == btn_resize && mod_pressed;
+ if ((resizing_via_border || resizing_via_mod) &&
+ state == WLR_BUTTON_PRESSED) {
+ if (edge == WLR_EDGE_NONE) {
+ edge |= cursor->cursor->x > floater->x + floater->width / 2 ?
+ WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
+ edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
+ WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
+ }
+ seat_begin_resize(seat, floater, button, edge);
+ return;
+ }
+
+ // Send event to surface
+ seat_set_focus(seat, cont);
+ seat_pointer_notify_button(seat, time_msec, button, state);
+}
+
+/**
+ * Remove a button (and duplicates) to the sorted list of currently pressed buttons
+ */
+static void state_erase_button(struct sway_cursor *cursor, uint32_t button) {
+ size_t j = 0;
+ for (size_t i = 0; i < cursor->pressed_button_count; ++i) {
+ if (i > j) {
+ cursor->pressed_buttons[j] = cursor->pressed_buttons[i];
+ }
+ if (cursor->pressed_buttons[i] != button) {
+ ++j;
+ }
+ }
+ while (cursor->pressed_button_count > j) {
+ --cursor->pressed_button_count;
+ cursor->pressed_buttons[cursor->pressed_button_count] = 0;
+ }
+}
+
+/**
+ * Add a button to the sorted list of currently pressed buttons, if there
+ * is space.
+ */
+static void state_add_button(struct sway_cursor *cursor, uint32_t button) {
+ if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) {
+ return;
+ }
+ size_t i = 0;
+ while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) {
+ ++i;
+ }
+ size_t j = cursor->pressed_button_count;
+ while (j > i) {
+ cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1];
+ --j;
+ }
+ cursor->pressed_buttons[i] = button;
+ cursor->pressed_button_count++;
+}
+
+/**
+ * Return the mouse binding which matches modifier, click location, release,
+ * and pressed button state, otherwise return null.
+ */
+static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor,
+ list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar,
+ bool on_border, bool on_content) {
+ uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) |
+ (on_border ? BINDING_BORDER : 0) |
+ (on_content ? BINDING_CONTENTS : 0);
+
+ for (int i = 0; i < bindings->length; ++i) {
+ struct sway_binding *binding = bindings->items[i];
+ if (modifiers ^ binding->modifiers ||
+ cursor->pressed_button_count != (size_t)binding->keys->length ||
+ release != (binding->flags & BINDING_RELEASE) ||
+ !(click_region & binding->flags)) {
+ continue;
+ }
+
+ bool match = true;
+ for (size_t j = 0; j < cursor->pressed_button_count; j++) {
+ uint32_t key = *(uint32_t *)binding->keys->items[j];
+ if (key != cursor->pressed_buttons[j]) {
+ match = false;
+ break;
+ }
+ }
+ if (!match) {
+ continue;
+ }
+
+ return binding;
+ }
+ return NULL;
+}
+
void dispatch_cursor_button(struct sway_cursor *cursor,
uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
+ if (cursor->seat->operation != OP_NONE &&
+ button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) {
+ seat_end_mouse_operation(cursor->seat);
+ seat_pointer_notify_button(cursor->seat, time_msec, button, state);
+ return;
+ }
if (time_msec == 0) {
time_msec = get_current_time_msec();
}
@@ -253,12 +567,41 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
double sx, sy;
struct sway_container *cont = container_at_coords(cursor->seat,
cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
+
+ // Handle mouse bindings
+ bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE);
+ bool on_contents = !on_border && surface;
+ bool on_titlebar = !on_border && !surface;
+ struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
+ uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
+
+ struct sway_binding *binding = NULL;
+ if (state == WLR_BUTTON_PRESSED) {
+ state_add_button(cursor, button);
+ binding = get_active_mouse_binding(cursor,
+ config->current_mode->mouse_bindings, modifiers, false,
+ on_titlebar, on_border, on_contents);
+ } else {
+ binding = get_active_mouse_binding(cursor,
+ config->current_mode->mouse_bindings, modifiers, true,
+ on_titlebar, on_border, on_contents);
+ state_erase_button(cursor, button);
+ }
+ if (binding) {
+ seat_execute_command(cursor->seat, binding);
+ // TODO: do we want to pass on the event?
+ }
+
if (surface && wlr_surface_is_layer_surface(surface)) {
struct wlr_layer_surface *layer =
wlr_layer_surface_from_wlr_surface(surface);
if (layer->current.keyboard_interactive) {
seat_set_focus_layer(cursor->seat, layer);
}
+ seat_pointer_notify_button(cursor->seat, time_msec, button, state);
+ } else if (cont && container_is_floating_or_child(cont)) {
+ dispatch_cursor_button_floating(cursor, time_msec, button, state,
+ surface, sx, sy, cont);
} else if (surface && cont && cont->type != C_VIEW) {
// Avoid moving keyboard focus from a surface that accepts it to one
// that does not unless the change would move us to a new workspace.
@@ -275,12 +618,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
if (new_ws != old_ws) {
seat_set_focus(cursor->seat, cont);
}
+ seat_pointer_notify_button(cursor->seat, time_msec, button, state);
} else if (cont) {
seat_set_focus(cursor->seat, cont);
+ seat_pointer_notify_button(cursor->seat, time_msec, button, state);
+ } else {
+ seat_pointer_notify_button(cursor->seat, time_msec, button, state);
}
- wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
- time_msec, button, state);
transaction_commit_dirty();
}
@@ -467,6 +812,9 @@ static void handle_request_set_cursor(struct wl_listener *listener,
void *data) {
struct sway_cursor *cursor =
wl_container_of(listener, cursor, request_set_cursor);
+ if (cursor->seat->operation != OP_NONE) {
+ return;
+ }
struct wlr_seat_pointer_request_set_cursor_event *event = data;
struct wl_client *focused_client = NULL;
@@ -485,9 +833,20 @@ static void handle_request_set_cursor(struct wl_listener *listener,
wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
event->hotspot_y);
+ cursor->image = NULL;
cursor->image_client = focused_client;
}
+void cursor_set_image(struct sway_cursor *cursor, const char *image,
+ struct wl_client *client) {
+ if (!cursor->image || strcmp(cursor->image, image) != 0) {
+ wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
+ cursor->cursor);
+ cursor->image = image;
+ }
+ cursor->image_client = client;
+}
+
void sway_cursor_destroy(struct sway_cursor *cursor) {
if (!cursor) {
return;
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 0b7cb766..c820e032 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -8,6 +8,7 @@
#include <math.h>
#include <wlr/backend/libinput.h>
#include <wlr/types/wlr_input_inhibitor.h>
+#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include "sway/config.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
@@ -303,6 +304,35 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
}
}
+void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager =
+ wl_container_of(listener, input_manager, virtual_keyboard_new);
+ struct wlr_virtual_keyboard_v1 *keyboard = data;
+ struct wlr_input_device *device = &keyboard->input_device;
+
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+
+ // TODO: The user might want this on a different seat
+ struct sway_input_device *input_device =
+ calloc(1, sizeof(struct sway_input_device));
+ if (!sway_assert(input_device, "could not allocate input device")) {
+ return;
+ }
+ device->data = input_device;
+
+ input_device->wlr_device = device;
+ input_device->identifier = get_device_identifier(device);
+ wl_list_insert(&input_manager->devices, &input_device->link);
+
+ wlr_log(WLR_DEBUG, "adding virtual keyboard: '%s'",
+ input_device->identifier);
+
+ wl_signal_add(&device->events.destroy, &input_device->device_destroy);
+ input_device->device_destroy.notify = handle_device_destroy;
+
+ seat_add_device(seat, input_device);
+}
+
struct sway_input_manager *input_manager_create(
struct sway_server *server) {
struct sway_input_manager *input =
@@ -321,6 +351,12 @@ struct sway_input_manager *input_manager_create(
input->new_input.notify = handle_new_input;
wl_signal_add(&server->backend->events.new_input, &input->new_input);
+ input->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
+ server->wl_display);
+ wl_signal_add(&input->virtual_keyboard->events.new_virtual_keyboard,
+ &input->virtual_keyboard_new);
+ input->virtual_keyboard_new.notify = handle_virtual_keyboard;
+
input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
input->inhibit_activate.notify = handle_inhibit_activate;
wl_signal_add(&input->inhibit->events.activate,
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index ede38519..160ef10b 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -3,11 +3,12 @@
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_idle.h>
+#include <wlr/interfaces/wlr_keyboard.h>
+#include "sway/commands.h"
#include "sway/desktop/transaction.h"
-#include "sway/input/seat.h"
-#include "sway/input/keyboard.h"
#include "sway/input/input-manager.h"
-#include "sway/commands.h"
+#include "sway/input/keyboard.h"
+#include "sway/input/seat.h"
#include "log.h"
/**
@@ -88,11 +89,13 @@ static void get_active_binding(const struct sway_shortcut_state *state,
uint32_t modifiers, bool release, bool locked) {
for (int i = 0; i < bindings->length; ++i) {
struct sway_binding *binding = bindings->items[i];
+ bool binding_locked = binding->flags & BINDING_LOCKED;
+ bool binding_release = binding->flags & BINDING_RELEASE;
if (modifiers ^ binding->modifiers ||
state->npressed != (size_t)binding->keys->length ||
- locked > binding->locked ||
- release != binding->release) {
+ release != binding_release ||
+ locked > binding_locked) {
continue;
}
@@ -119,23 +122,6 @@ static void get_active_binding(const struct sway_shortcut_state *state,
}
/**
- * Execute the command associated to a binding
- */
-static void keyboard_execute_command(struct sway_keyboard *keyboard,
- struct sway_binding *binding) {
- wlr_log(WLR_DEBUG, "running command for binding: %s",
- binding->command);
- config->handler_context.seat = keyboard->seat_device->sway_seat;
- struct cmd_results *results = execute_command(binding->command, NULL);
- transaction_commit_dirty();
- if (results->status != CMD_SUCCESS) {
- wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
- binding->command, results->error);
- }
- free_cmd_results(results);
-}
-
-/**
* Execute a built-in, hardcoded compositor binding. These are triggered from a
* single keysym.
*
@@ -211,12 +197,13 @@ static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct sway_keyboard *keyboard =
wl_container_of(listener, keyboard, keyboard_key);
- struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+ struct sway_seat* seat = keyboard->seat_device->sway_seat;
+ struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
- wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat);
+ wlr_idle_notify_activity(seat->input->server->idle, wlr_seat);
struct wlr_event_keyboard_key *event = data;
- bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL;
+ bool input_inhibited = seat->exclusive_client != NULL;
// Identify new keycode, raw keysym(s), and translated keysym(s)
xkb_keycode_t keycode = event->keycode + 8;
@@ -266,7 +253,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
// Execute stored release binding once no longer active
if (keyboard->held_binding && binding_released != keyboard->held_binding &&
event->state == WLR_KEY_RELEASED) {
- keyboard_execute_command(keyboard, keyboard->held_binding);
+ seat_execute_command(seat, keyboard->held_binding);
handled = true;
}
if (binding_released != keyboard->held_binding) {
@@ -277,6 +264,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
}
// Identify and execute active pressed binding
+ struct sway_binding *next_repeat_binding = NULL;
if (event->state == WLR_KEY_PRESSED) {
struct sway_binding *binding_pressed = NULL;
get_active_binding(&keyboard->state_keycodes,
@@ -290,8 +278,23 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
raw_modifiers, false, input_inhibited);
if (binding_pressed) {
- keyboard_execute_command(keyboard, binding_pressed);
+ seat_execute_command(seat, binding_pressed);
handled = true;
+ next_repeat_binding = binding_pressed;
+ }
+ }
+
+ // Set up (or clear) keyboard repeat for a pressed binding
+ if (next_repeat_binding && wlr_device->keyboard->repeat_info.delay > 0) {
+ keyboard->repeat_binding = next_repeat_binding;
+ if (wl_event_source_timer_update(keyboard->key_repeat_source,
+ wlr_device->keyboard->repeat_info.delay) < 0) {
+ wlr_log(WLR_DEBUG, "failed to set key repeat timer");
+ }
+ } else if (keyboard->repeat_binding) {
+ keyboard->repeat_binding = NULL;
+ if (wl_event_source_timer_update(keyboard->key_repeat_source, 0) < 0) {
+ wlr_log(WLR_DEBUG, "failed to disarm key repeat timer");
}
}
@@ -312,6 +315,28 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state);
}
+
+ transaction_commit_dirty();
+}
+
+static int handle_keyboard_repeat(void *data) {
+ struct sway_keyboard *keyboard = (struct sway_keyboard *)data;
+ struct wlr_keyboard *wlr_device =
+ keyboard->seat_device->input_device->wlr_device->keyboard;
+ if (keyboard->repeat_binding) {
+ if (wlr_device->repeat_info.rate > 0) {
+ // We queue the next event first, as the command might cancel it
+ if (wl_event_source_timer_update(keyboard->key_repeat_source,
+ 1000 / wlr_device->repeat_info.rate) < 0) {
+ wlr_log(WLR_DEBUG, "failed to update key repeat timer");
+ }
+ }
+
+ seat_execute_command(keyboard->seat_device->sway_seat,
+ keyboard->repeat_binding);
+ transaction_commit_dirty();
+ }
+ return 0;
}
static void handle_keyboard_modifiers(struct wl_listener *listener,
@@ -339,6 +364,9 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
wl_list_init(&keyboard->keyboard_key.link);
wl_list_init(&keyboard->keyboard_modifiers.link);
+ keyboard->key_repeat_source = wl_event_loop_add_timer(server.wl_event_loop,
+ handle_keyboard_repeat, keyboard);
+
return keyboard;
}
@@ -397,6 +425,31 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
keyboard->keymap = keymap;
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+ xkb_mod_mask_t locked_mods = 0;
+ if (input_config && input_config->xkb_numlock > 0) {
+ xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_NUM);
+ if (mod_index != XKB_MOD_INVALID) {
+ locked_mods |= (uint32_t)1 << mod_index;
+ }
+ }
+ if (input_config && input_config->xkb_capslock > 0) {
+ xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
+ if (mod_index != XKB_MOD_INVALID) {
+ locked_mods |= (uint32_t)1 << mod_index;
+ }
+ }
+ if (locked_mods) {
+ wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, locked_mods, 0);
+ uint32_t leds = 0;
+ for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
+ if (xkb_state_led_index_is_active(wlr_device->keyboard->xkb_state,
+ wlr_device->keyboard->led_indexes[i])) {
+ leds |= (1 << i);
+ }
+ }
+ wlr_keyboard_led_update(wlr_device->keyboard, leds);
+ }
+
if (input_config && input_config->repeat_delay != INT_MIN
&& input_config->repeat_rate != INT_MIN) {
wlr_keyboard_set_repeat_info(wlr_device->keyboard,
@@ -427,5 +480,6 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
}
wl_list_remove(&keyboard->keyboard_key.link);
wl_list_remove(&keyboard->keyboard_modifiers.link);
+ wl_event_source_remove(keyboard->key_repeat_source);
free(keyboard);
}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index e77d88a8..dd4d5c3b 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,12 +1,19 @@
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 199309L
#include <assert.h>
+#include <errno.h>
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
#include <strings.h>
#include <time.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include "log.h"
+#include "config.h"
#include "sway/debug.h"
#include "sway/desktop.h"
#include "sway/input/cursor.h"
@@ -98,11 +105,13 @@ static void seat_send_focus(struct sway_container *con,
if (con->type == C_VIEW
&& seat_is_input_allowed(seat, con->sway_view->surface)) {
+#ifdef HAVE_XWAYLAND
if (con->sway_view->type == SWAY_VIEW_XWAYLAND) {
struct wlr_xwayland *xwayland =
seat->input->server->xwayland.wlr_xwayland;
wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
}
+#endif
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
if (keyboard) {
wlr_seat_keyboard_notify_enter(seat->wlr_seat,
@@ -116,12 +125,14 @@ static void seat_send_focus(struct sway_container *con,
}
static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
- struct sway_container *container, enum sway_container_type type) {
+ struct sway_container *container, enum sway_container_type type,
+ bool only_tiling) {
if (container->type == C_VIEW) {
return container;
}
- struct sway_container *floating = container->type == C_WORKSPACE ?
+ struct sway_container *floating =
+ container->type == C_WORKSPACE && !only_tiling ?
container->sway_workspace->floating : NULL;
if (container->children->length == 0 &&
(!floating || floating->children->length == 0)) {
@@ -135,6 +146,10 @@ static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
}
if (container_has_child(container, current->container)) {
+ if (only_tiling &&
+ container_is_floating_or_child(current->container)) {
+ continue;
+ }
return current->container;
}
if (floating && container_has_child(floating, current->container)) {
@@ -161,7 +176,7 @@ void seat_focus_inactive_children_for_each(struct sway_seat *seat,
struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
struct sway_container *container) {
- return seat_get_focus_by_type(seat, container, C_VIEW);
+ return seat_get_focus_by_type(seat, container, C_VIEW, false);
}
static void handle_seat_container_destroy(struct wl_listener *listener,
@@ -183,7 +198,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener,
if (set_focus) {
struct sway_container *next_focus = NULL;
while (next_focus == NULL) {
- next_focus = seat_get_focus_by_type(seat, parent, C_VIEW);
+ next_focus = seat_get_focus_by_type(seat, parent, C_VIEW, false);
if (next_focus == NULL && parent->type == C_WORKSPACE) {
next_focus = parent;
@@ -348,6 +363,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
free(seat);
return NULL;
}
+ seat->wlr_seat->data = seat;
seat->cursor = sway_cursor_create(seat);
if (!seat->cursor) {
@@ -377,7 +393,6 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
WL_SEAT_CAPABILITY_POINTER |
WL_SEAT_CAPABILITY_TOUCH);
- seat_configure_xcursor(seat);
wl_list_insert(&input->seats, &seat->link);
@@ -422,6 +437,7 @@ static void seat_apply_input_config(struct sway_seat *seat,
static void seat_configure_pointer(struct sway_seat *seat,
struct sway_seat_device *sway_device) {
+ seat_configure_xcursor(seat);
wlr_cursor_attach_input_device(seat->cursor->cursor,
sway_device->input_device->wlr_device);
seat_apply_input_config(seat, sway_device);
@@ -601,7 +617,7 @@ static int handle_urgent_timeout(void *data) {
}
void seat_set_focus_warp(struct sway_seat *seat,
- struct sway_container *container, bool warp) {
+ struct sway_container *container, bool warp, bool notify) {
if (seat->focused_layer) {
return;
}
@@ -622,7 +638,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
if (last_workspace && last_workspace == new_workspace
&& last_workspace->sway_workspace->fullscreen
- && !container->sway_view->is_fullscreen) {
+ && container && !container_is_fullscreen_or_child(container)) {
return;
}
@@ -639,7 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
struct sway_container *new_output_last_ws = NULL;
if (last_output && new_output && last_output != new_output) {
new_output_last_ws =
- seat_get_focus_by_type(seat, new_output, C_WORKSPACE);
+ seat_get_focus_by_type(seat, new_output, C_WORKSPACE, false);
}
if (container && container->parent) {
@@ -686,8 +702,14 @@ void seat_set_focus_warp(struct sway_seat *seat,
config->urgent_timeout > 0) {
view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop,
handle_urgent_timeout, view);
- wl_event_source_timer_update(view->urgent_timer,
- config->urgent_timeout);
+ if (view->urgent_timer) {
+ wl_event_source_timer_update(view->urgent_timer,
+ config->urgent_timeout);
+ } else {
+ wlr_log(WLR_ERROR, "Unable to create urgency timer (%s)",
+ strerror(errno));
+ handle_urgent_timeout(view);
+ }
} else {
view_set_urgent(view, false);
}
@@ -715,9 +737,18 @@ void seat_set_focus_warp(struct sway_seat *seat,
}
}
+ // Close any popups on the old focus
+ if (last_focus && last_focus != container) {
+ if (last_focus->type == C_VIEW) {
+ view_close_popups(last_focus->sway_view);
+ }
+ }
+
if (last_focus) {
if (last_workspace) {
- ipc_event_workspace(last_workspace, container, "focus");
+ if (notify && last_workspace != new_workspace) {
+ ipc_event_workspace(last_workspace, new_workspace, "focus");
+ }
if (!workspace_is_visible(last_workspace)
&& workspace_is_empty(last_workspace)) {
if (last_workspace == last_focus) {
@@ -744,8 +775,12 @@ void seat_set_focus_warp(struct sway_seat *seat,
}
}
- if (last_focus != NULL) {
- cursor_send_pointer_motion(seat->cursor, 0, true);
+ if (container) {
+ if (container->type == C_VIEW) {
+ ipc_event_window(container, "focus");
+ } else if (container->type == C_WORKSPACE) {
+ ipc_event_workspace(NULL, container, "focus");
+ }
}
seat->has_focus = (container != NULL);
@@ -755,7 +790,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
void seat_set_focus(struct sway_seat *seat,
struct sway_container *container) {
- seat_set_focus_warp(seat, container, true);
+ seat_set_focus_warp(seat, container, true, true);
}
void seat_set_focus_surface(struct sway_seat *seat,
@@ -848,7 +883,12 @@ void seat_set_exclusive_client(struct sway_seat *seat,
struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_container *container) {
- return seat_get_focus_by_type(seat, container, C_TYPES);
+ return seat_get_focus_by_type(seat, container, C_TYPES, false);
+}
+
+struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
+ struct sway_container *container) {
+ return seat_get_focus_by_type(seat, container, C_TYPES, true);
}
struct sway_container *seat_get_active_child(struct sway_seat *seat,
@@ -894,3 +934,68 @@ struct seat_config *seat_get_config(struct sway_seat *seat) {
return NULL;
}
+
+void seat_begin_move(struct sway_seat *seat, struct sway_container *con,
+ uint32_t button) {
+ if (!seat->cursor) {
+ wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device");
+ return;
+ }
+ seat->operation = OP_MOVE;
+ seat->op_container = con;
+ seat->op_button = button;
+ cursor_set_image(seat->cursor, "grab", NULL);
+}
+
+void seat_begin_resize(struct sway_seat *seat, struct sway_container *con,
+ uint32_t button, enum wlr_edges edge) {
+ if (!seat->cursor) {
+ wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device");
+ return;
+ }
+ struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
+ seat->operation = OP_RESIZE;
+ seat->op_container = con;
+ seat->op_resize_preserve_ratio = keyboard &&
+ (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
+ seat->op_resize_edge = edge == WLR_EDGE_NONE ?
+ RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge;
+ seat->op_button = button;
+ seat->op_ref_lx = seat->cursor->cursor->x;
+ seat->op_ref_ly = seat->cursor->cursor->y;
+ seat->op_ref_con_lx = con->x;
+ seat->op_ref_con_ly = con->y;
+ seat->op_ref_width = con->width;
+ seat->op_ref_height = con->height;
+
+ const char *image = edge == WLR_EDGE_NONE ?
+ "se-resize" : wlr_xcursor_get_resize_name(edge);
+ cursor_set_image(seat->cursor, image, NULL);
+}
+
+void seat_end_mouse_operation(struct sway_seat *seat) {
+ switch (seat->operation) {
+ case OP_MOVE:
+ {
+ // We "move" the container to its own location so it discovers its
+ // output again.
+ struct sway_container *con = seat->op_container;
+ container_floating_move_to(con, con->x, con->y);
+ }
+ case OP_RESIZE:
+ // Don't need to do anything here.
+ break;
+ case OP_NONE:
+ break;
+ }
+ seat->operation = OP_NONE;
+ seat->op_container = NULL;
+ cursor_set_image(seat->cursor, "left_ptr", NULL);
+}
+
+void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
+ uint32_t button, enum wlr_button_state state) {
+ seat->last_button = button;
+ seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat,
+ time_msec, button, state);
+}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index c49ea47e..4c2bcc98 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -201,6 +201,15 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
bool urgent = c->type == C_VIEW ?
view_is_urgent(c->sway_view) : container_has_urgent_child(c);
json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
+
+ if (c->type == C_VIEW) {
+ json_object *marks = json_object_new_array();
+ list_t *view_marks = c->sway_view->marks;
+ for (int i = 0; i < view_marks->length; ++i) {
+ json_object_array_add(marks, json_object_new_string(view_marks->items[i]));
+ }
+ json_object_object_add(object, "marks", marks);
+ }
}
static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index be703915..7d2d8969 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -3,12 +3,18 @@
// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
#define _XOPEN_SOURCE 700
#endif
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <json-c/json.h>
#include <stdbool.h>
#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -28,6 +34,7 @@
#include "sway/tree/view.h"
#include "list.h"
#include "log.h"
+#include "util.h"
static int ipc_socket = -1;
static struct wl_event_source *ipc_event_source = NULL;
@@ -291,13 +298,11 @@ void ipc_event_workspace(struct sway_container *old,
wlr_log(WLR_DEBUG, "Sending workspace::%s event", change);
json_object *obj = json_object_new_object();
json_object_object_add(obj, "change", json_object_new_string(change));
- if (strcmp("focus", change) == 0) {
- if (old) {
- json_object_object_add(obj, "old",
- ipc_json_describe_container_recursive(old));
- } else {
- json_object_object_add(obj, "old", NULL);
- }
+ if (old) {
+ json_object_object_add(obj, "old",
+ ipc_json_describe_container_recursive(old));
+ } else {
+ json_object_object_add(obj, "old", NULL);
}
if (new) {
@@ -353,6 +358,104 @@ void ipc_event_mode(const char *mode, bool pango) {
json_object_put(obj);
}
+void ipc_event_shutdown(const char *reason) {
+ if (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) {
+ return;
+ }
+ wlr_log(WLR_DEBUG, "Sending shutdown::%s event", reason);
+
+ json_object *json = json_object_new_object();
+ json_object_object_add(json, "change", json_object_new_string(reason));
+
+ const char *json_string = json_object_to_json_string(json);
+ ipc_send_event(json_string, IPC_EVENT_SHUTDOWN);
+ json_object_put(json);
+}
+
+void ipc_event_binding(struct sway_binding *binding) {
+ if (!ipc_has_event_listeners(IPC_EVENT_BINDING)) {
+ return;
+ }
+ wlr_log(WLR_DEBUG, "Sending binding event");
+
+ json_object *json_binding = json_object_new_object();
+ json_object_object_add(json_binding, "command", json_object_new_string(binding->command));
+
+ const char *names[10];
+ int len = get_modifier_names(names, binding->modifiers);
+ json_object *modifiers = json_object_new_array();
+ for (int i = 0; i < len; ++i) {
+ json_object_array_add(modifiers, json_object_new_string(names[i]));
+ }
+ json_object_object_add(json_binding, "event_state_mask", modifiers);
+
+ json_object *input_codes = json_object_new_array();
+ int input_code = 0;
+ json_object *symbols = json_object_new_array();
+ json_object *symbol = NULL;
+
+ if (binding->type == BINDING_KEYCODE) { // bindcode: populate input_codes
+ uint32_t keycode;
+ for (int i = 0; i < binding->keys->length; ++i) {
+ keycode = *(uint32_t *)binding->keys->items[i];
+ json_object_array_add(input_codes, json_object_new_int(keycode));
+ if (i == 0) {
+ input_code = keycode;
+ }
+ }
+ } else { // bindsym/mouse: populate symbols
+ uint32_t keysym;
+ char buffer[64];
+ for (int i = 0; i < binding->keys->length; ++i) {
+ keysym = *(uint32_t *)binding->keys->items[i];
+ if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) {
+ snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1);
+ } else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) {
+ continue;
+ }
+
+ json_object *str = json_object_new_string(buffer);
+ if (i == 0) {
+ // str is owned by both symbol and symbols. Make sure
+ // to bump the ref count.
+ json_object_array_add(symbols, json_object_get(str));
+ symbol = str;
+ } else {
+ json_object_array_add(symbols, str);
+ }
+ }
+ }
+
+ json_object_object_add(json_binding, "input_codes", input_codes);
+ json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
+ json_object_object_add(json_binding, "symbols", symbols);
+ json_object_object_add(json_binding, "symbol", symbol);
+ json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ?
+ json_object_new_string("mouse") : json_object_new_string("keyboard"));
+
+ json_object *json = json_object_new_object();
+ json_object_object_add(json, "change", json_object_new_string("run"));
+ json_object_object_add(json, "binding", json_binding);
+ const char *json_string = json_object_to_json_string(json);
+ ipc_send_event(json_string, IPC_EVENT_BINDING);
+ json_object_put(json);
+}
+
+static void ipc_event_tick(const char *payload) {
+ if (!ipc_has_event_listeners(IPC_EVENT_TICK)) {
+ return;
+ }
+ wlr_log(WLR_DEBUG, "Sending tick event");
+
+ json_object *json = json_object_new_object();
+ json_object_object_add(json, "first", json_object_new_boolean(false));
+ json_object_object_add(json, "payload", json_object_new_string(payload));
+
+ const char *json_string = json_object_to_json_string(json);
+ ipc_send_event(json_string, IPC_EVENT_TICK);
+ json_object_put(json);
+}
+
int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
@@ -494,6 +597,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
+ case IPC_SEND_TICK:
+ {
+ ipc_event_tick(buf);
+ ipc_send_reply(client, "{\"success\": true}", 17);
+ goto exit_cleanup;
+ }
+
case IPC_GET_OUTPUTS:
{
json_object *outputs = json_object_new_array();
@@ -540,6 +650,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
+ bool is_tick = false;
// parse requested event types
for (size_t i = 0; i < json_object_array_length(request); i++) {
const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
@@ -549,12 +660,15 @@ void ipc_client_handle_command(struct ipc_client *client) {
client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
} else if (strcmp(event_type, "mode") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_MODE);
+ } else if (strcmp(event_type, "shutdown") == 0) {
+ client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
} else if (strcmp(event_type, "window") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
- } else if (strcmp(event_type, "modifier") == 0) {
- client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
} else if (strcmp(event_type, "binding") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
+ } else if (strcmp(event_type, "tick") == 0) {
+ client->subscribed_events |= event_mask(IPC_EVENT_TICK);
+ is_tick = true;
} else {
client_valid =
ipc_send_reply(client, "{\"success\": false}", 18);
@@ -566,6 +680,10 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_put(request);
client_valid = ipc_send_reply(client, "{\"success\": true}", 17);
+ if (is_tick) {
+ client->current_command = IPC_EVENT_TICK;
+ ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30);
+ }
goto exit_cleanup;
}
diff --git a/sway/main.c b/sway/main.c
index a20f1dac..477ffa5a 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -36,6 +36,7 @@ struct sway_server server;
void sway_terminate(int exit_code) {
terminate_request = true;
exit_value = exit_code;
+ ipc_event_shutdown("exit");
wl_display_terminate(server.wl_display);
}
diff --git a/sway/meson.build b/sway/meson.build
index 09bc40b8..a9503c3b 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -7,6 +7,7 @@ sway_sources = files(
'debug-tree.c',
'ipc-json.c',
'ipc-server.c',
+ 'scratchpad.c',
'security.c',
'desktop/desktop.c',
@@ -17,7 +18,6 @@ sway_sources = files(
'desktop/transaction.c',
'desktop/xdg_shell_v6.c',
'desktop/xdg_shell.c',
- 'desktop/xwayland.c',
'input/input-manager.c',
'input/seat.c',
@@ -42,6 +42,7 @@ sway_sources = files(
'commands/exec_always.c',
'commands/floating.c',
'commands/floating_minmax_size.c',
+ 'commands/floating_modifier.c',
'commands/focus.c',
'commands/focus_follows_mouse.c',
'commands/focus_wrapping.c',
@@ -66,6 +67,7 @@ sway_sources = files(
'commands/reload.c',
'commands/rename.c',
'commands/resize.c',
+ 'commands/scratchpad.c',
'commands/seat.c',
'commands/seat/attach.c',
'commands/seat/cursor.c',
@@ -126,8 +128,10 @@ sway_sources = files(
'commands/input/scroll_method.c',
'commands/input/tap.c',
'commands/input/tap_button_map.c',
+ 'commands/input/xkb_capslock.c',
'commands/input/xkb_layout.c',
'commands/input/xkb_model.c',
+ 'commands/input/xkb_numlock.c',
'commands/input/xkb_options.c',
'commands/input/xkb_rules.c',
'commands/input/xkb_variant.c',
@@ -162,10 +166,14 @@ sway_deps = [
server_protos,
wayland_server,
wlroots,
- xcb,
xkbcommon,
]
+if get_option('enable-xwayland')
+ sway_sources += 'desktop/xwayland.c'
+ sway_deps += xcb
+endif
+
executable(
'sway',
sway_sources,
diff --git a/sway/scratchpad.c b/sway/scratchpad.c
new file mode 100644
index 00000000..b7d6fd99
--- /dev/null
+++ b/sway/scratchpad.c
@@ -0,0 +1,181 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "sway/scratchpad.h"
+#include "sway/input/seat.h"
+#include "sway/tree/arrange.h"
+#include "sway/tree/container.h"
+#include "sway/tree/view.h"
+#include "sway/tree/workspace.h"
+#include "list.h"
+#include "log.h"
+
+void scratchpad_add_container(struct sway_container *con) {
+ if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
+ return;
+ }
+ con->scratchpad = true;
+ list_add(root_container.sway_root->scratchpad, con);
+
+ struct sway_container *parent = con->parent;
+ container_set_floating(con, true);
+ container_remove_child(con);
+ arrange_windows(parent);
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ seat_set_focus(seat, seat_get_focus_inactive(seat, parent));
+}
+
+void scratchpad_remove_container(struct sway_container *con) {
+ if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) {
+ return;
+ }
+ con->scratchpad = false;
+ for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) {
+ if (root_container.sway_root->scratchpad->items[i] == con) {
+ list_del(root_container.sway_root->scratchpad, i);
+ break;
+ }
+ }
+}
+
+/**
+ * Show a single scratchpad container.
+ * The container might be visible on another workspace already.
+ */
+static void scratchpad_show(struct sway_container *con) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *ws = seat_get_focus(seat);
+ if (ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+
+ // If the current con or any of its parents are in fullscreen mode, we
+ // first need to disable it before showing the scratchpad con.
+ if (ws->sway_workspace->fullscreen) {
+ container_set_fullscreen(ws->sway_workspace->fullscreen, false);
+ }
+
+ // Show the container
+ if (con->parent) {
+ container_remove_child(con);
+ }
+ container_add_child(ws->sway_workspace->floating, con);
+
+ // Make sure the container's center point overlaps this workspace
+ double center_lx = con->x + con->width / 2;
+ double center_ly = con->y + con->height / 2;
+
+ struct wlr_box workspace_box;
+ container_get_box(ws, &workspace_box);
+ if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
+ // Maybe resize it
+ if (con->width > ws->width || con->height > ws->height) {
+ container_init_floating(con);
+ }
+
+ // Center it
+ double new_lx = ws->x + (ws->width - con->width) / 2;
+ double new_ly = ws->y + (ws->height - con->height) / 2;
+ container_floating_move_to(con, new_lx, new_ly);
+ }
+
+ arrange_windows(ws);
+ seat_set_focus(seat, seat_get_focus_inactive(seat, con));
+
+ container_set_dirty(con->parent);
+}
+
+/**
+ * Hide a single scratchpad container.
+ * The container might not be the focused container (eg. when using criteria).
+ */
+static void scratchpad_hide(struct sway_container *con) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus(seat);
+ struct sway_container *ws = container_parent(con, C_WORKSPACE);
+
+ container_remove_child(con);
+ arrange_windows(ws);
+ if (con == focus) {
+ seat_set_focus(seat, seat_get_focus_inactive(seat, ws));
+ }
+ list_move_to_end(root_container.sway_root->scratchpad, con);
+}
+
+void scratchpad_toggle_auto(void) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus(seat);
+ struct sway_container *ws = focus->type == C_WORKSPACE ?
+ focus : container_parent(focus, C_WORKSPACE);
+
+ // If the focus is in a floating split container,
+ // operate on the split container instead of the child.
+ if (container_is_floating_or_child(focus)) {
+ while (focus->parent->layout != L_FLOATING) {
+ focus = focus->parent;
+ }
+ }
+
+
+ // Check if the currently focused window is a scratchpad window and should
+ // be hidden again.
+ if (focus->scratchpad) {
+ wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s",
+ focus->name);
+ scratchpad_hide(focus);
+ return;
+ }
+
+ // Check if there is an unfocused scratchpad window on the current workspace
+ // and focus it.
+ for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) {
+ struct sway_container *floater =
+ ws->sway_workspace->floating->children->items[i];
+ if (floater->scratchpad && focus != floater) {
+ wlr_log(WLR_DEBUG,
+ "Focusing other scratchpad window (%s) in this workspace",
+ floater->name);
+ scratchpad_show(floater);
+ return;
+ }
+ }
+
+ // Check if there is a visible scratchpad window on another workspace.
+ // In this case we move it to the current workspace.
+ for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) {
+ struct sway_container *con =
+ root_container.sway_root->scratchpad->items[i];
+ if (con->parent) {
+ wlr_log(WLR_DEBUG,
+ "Moving a visible scratchpad window (%s) to this workspace",
+ con->name);
+ scratchpad_show(con);
+ return;
+ }
+ }
+
+ // Take the container at the bottom of the scratchpad list
+ if (!sway_assert(root_container.sway_root->scratchpad->length,
+ "Scratchpad is empty")) {
+ return;
+ }
+ struct sway_container *con = root_container.sway_root->scratchpad->items[0];
+ wlr_log(WLR_DEBUG, "Showing %s from list", con->name);
+ scratchpad_show(con);
+}
+
+void scratchpad_toggle_container(struct sway_container *con) {
+ if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) {
+ return;
+ }
+
+ // Check if it matches a currently visible scratchpad window and hide it.
+ if (con->parent) {
+ scratchpad_hide(con);
+ return;
+ }
+
+ scratchpad_show(con);
+}
diff --git a/sway/server.c b/sway/server.c
index 91ae7c97..e8755360 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -26,7 +26,10 @@
#include "sway/input/input-manager.h"
#include "sway/server.h"
#include "sway/tree/layout.h"
+#include "config.h"
+#ifdef HAVE_XWAYLAND
#include "sway/xwayland.h"
+#endif
bool server_privileged_prepare(struct sway_server *server) {
wlr_log(WLR_DEBUG, "Preparing Wayland server initialization");
@@ -83,6 +86,7 @@ bool server_init(struct sway_server *server) {
server->xdg_shell_surface.notify = handle_xdg_shell_surface;
// TODO make xwayland optional
+#ifdef HAVE_XWAYLAND
server->xwayland.wlr_xwayland =
wlr_xwayland_create(server->wl_display, server->compositor, true);
wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,
@@ -103,6 +107,7 @@ bool server_init(struct sway_server *server) {
image->width * 4, image->width, image->height, image->hotspot_x,
image->hotspot_y);
}
+#endif
// TODO: Integration with sway borders
struct wlr_server_decoration_manager *deco_manager =
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index b6391431..707c36af 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -33,6 +33,14 @@ For more information on these xkb configuration options, see
*input* <identifier> xkb\_variant <variant>
Sets the variant of the keyboard like _dvorak_ or _colemak_.
+The following commands may only be used in the configuration file.
+
+*input* <identifier> xkb\_capslock enabled|disabled
+ Initially enables or disables CapsLock, the default is disabled.
+
+*input* <identifier> xkb\_numlock enabled|disabled
+ Initially enables or disables NumLock, the default is disabled.
+
## MAPPING CONFIGURATION
*input* <identifier> map\_to\_output <identifier>
@@ -100,7 +108,7 @@ For more information on these xkb configuration options, see
*input* <identifier> tap enabled|disabled
Enables or disables tap for specified input device.
-*input* <identifier> tap_button_map lrm|lmr
+*input* <identifier> tap\_button\_map lrm|lmr
Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as
left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_
treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as
diff --git a/sway/sway.1.scd b/sway/sway.1.scd
index 5b770cce..0c2ee974 100644
--- a/sway/sway.1.scd
+++ b/sway/sway.1.scd
@@ -92,4 +92,4 @@ source contributors. For more information about sway development, see
# SEE ALSO
-*sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5)
+*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5)
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 533cf71c..5452b13c 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -220,8 +220,22 @@ static void arrange_workspace(struct sway_container *workspace) {
container_set_dirty(workspace);
wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
workspace->x, workspace->y);
- arrange_floating(workspace->sway_workspace->floating);
- arrange_children_of(workspace);
+ if (workspace->sway_workspace->fullscreen) {
+ struct sway_container *fs = workspace->sway_workspace->fullscreen;
+ fs->x = workspace->parent->x;
+ fs->y = workspace->parent->y;
+ fs->width = workspace->parent->width;
+ fs->height = workspace->parent->height;
+ if (fs->type == C_VIEW) {
+ view_autoconfigure(fs->sway_view);
+ } else {
+ arrange_children_of(fs);
+ }
+ container_set_dirty(fs);
+ } else {
+ arrange_floating(workspace->sway_workspace->floating);
+ arrange_children_of(workspace);
+ }
}
static void arrange_output(struct sway_container *output) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 4dbfbb29..46c54e2d 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -17,6 +17,7 @@
#include "sway/input/seat.h"
#include "sway/ipc-server.h"
#include "sway/output.h"
+#include "sway/scratchpad.h"
#include "sway/server.h"
#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
@@ -61,13 +62,17 @@ void container_create_notify(struct sway_container *container) {
// TODO send ipc event type based on the container type
wl_signal_emit(&root_container.sway_root->events.new_container, container);
- if (container->type == C_VIEW || container->type == C_CONTAINER) {
+ if (container->type == C_VIEW) {
ipc_event_window(container, "new");
+ } else if (container->type == C_WORKSPACE) {
+ ipc_event_workspace(NULL, container, "init");
}
}
-static void container_update_textures_recursive(struct sway_container *con) {
- container_update_title_textures(con);
+void container_update_textures_recursive(struct sway_container *con) {
+ if (con->type == C_CONTAINER || con->type == C_VIEW) {
+ container_update_title_textures(con);
+ }
if (con->type == C_VIEW) {
view_update_marks_textures(con->sway_view);
@@ -76,6 +81,10 @@ static void container_update_textures_recursive(struct sway_container *con) {
struct sway_container *child = con->children->items[i];
container_update_textures_recursive(child);
}
+
+ if (con->type == C_WORKSPACE) {
+ container_update_textures_recursive(con->sway_workspace->floating);
+ }
}
}
@@ -139,8 +148,6 @@ struct sway_container *container_create(enum sway_container_type type) {
static void container_workspace_free(struct sway_workspace *ws) {
list_foreach(ws->output_priority, free);
list_free(ws->output_priority);
- ws->floating->destroying = true;
- container_free(ws->floating);
free(ws);
}
@@ -193,6 +200,9 @@ void container_free(struct sway_container *cont) {
free(cont);
}
+static struct sway_container *container_destroy_noreaping(
+ struct sway_container *con);
+
static struct sway_container *container_workspace_destroy(
struct sway_container *workspace) {
if (!sway_assert(workspace, "cannot destroy null workspace")) {
@@ -237,6 +247,8 @@ static struct sway_container *container_workspace_destroy(
}
}
+ container_destroy_noreaping(workspace->sway_workspace->floating);
+
return output;
}
@@ -271,7 +283,7 @@ static struct sway_container *container_output_destroy(
container_remove_child(workspace);
if (!workspace_is_empty(workspace)) {
container_add_child(new_output, workspace);
- ipc_event_workspace(workspace, NULL, "move");
+ ipc_event_workspace(NULL, workspace, "move");
} else {
container_destroy(workspace);
}
@@ -309,7 +321,13 @@ static struct sway_container *container_destroy_noreaping(
}
wl_signal_emit(&con->events.destroy, con);
- ipc_event_window(con, "close");
+
+ // emit IPC event
+ if (con->type == C_VIEW) {
+ ipc_event_window(con, "close");
+ } else if (con->type == C_WORKSPACE) {
+ ipc_event_workspace(NULL, con, "empty");
+ }
// The below functions move their children to somewhere else.
if (con->type == C_OUTPUT) {
@@ -323,9 +341,15 @@ static struct sway_container *container_destroy_noreaping(
}
}
+ container_end_mouse_operation(con);
+
con->destroying = true;
container_set_dirty(con);
+ if (con->scratchpad) {
+ scratchpad_remove_container(con);
+ }
+
if (!con->parent) {
return NULL;
}
@@ -398,6 +422,10 @@ struct sway_container *container_flatten(struct sway_container *container) {
* This function just wraps container_destroy_noreaping(), then does reaping.
*/
struct sway_container *container_destroy(struct sway_container *con) {
+ if (con->is_fullscreen) {
+ struct sway_container *ws = container_parent(con, C_WORKSPACE);
+ ws->sway_workspace->fullscreen = NULL;
+ }
struct sway_container *parent = container_destroy_noreaping(con);
if (!parent) {
@@ -507,7 +535,7 @@ struct sway_container *container_parent(struct sway_container *container,
return container;
}
-static struct sway_container *container_at_view(struct sway_container *swayc,
+struct sway_container *container_at_view(struct sway_container *swayc,
double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) {
@@ -520,10 +548,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc,
double _sx, _sy;
struct wlr_surface *_surface = NULL;
switch (sview->type) {
+#ifdef HAVE_XWAYLAND
case SWAY_VIEW_XWAYLAND:
_surface = wlr_surface_surface_at(sview->surface,
view_sx, view_sy, &_sx, &_sy);
break;
+#endif
case SWAY_VIEW_XDG_SHELL_V6:
_surface = wlr_xdg_surface_v6_surface_at(
sview->wlr_xdg_surface_v6,
@@ -539,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc,
*sx = _sx;
*sy = _sy;
*surface = _surface;
+ return swayc;
}
- return swayc;
+ return NULL;
}
+static struct sway_container *tiling_container_at(
+ struct sway_container *con, double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy);
+
/**
* container_at for a container with layout L_TABBED.
*/
@@ -569,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent,
// Surfaces
struct sway_container *current = seat_get_active_child(seat, parent);
- return container_at(current, lx, ly, surface, sx, sy);
+ return tiling_container_at(current, lx, ly, surface, sx, sy);
}
/**
@@ -594,7 +629,7 @@ static struct sway_container *container_at_stacked(
// Surfaces
struct sway_container *current = seat_get_active_child(seat, parent);
- return container_at(current, lx, ly, surface, sx, sy);
+ return tiling_container_at(current, lx, ly, surface, sx, sy);
}
/**
@@ -612,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
.height = child->height,
};
if (wlr_box_contains_point(&box, lx, ly)) {
- return container_at(child, lx, ly, surface, sx, sy);
+ return tiling_container_at(child, lx, ly, surface, sx, sy);
}
}
return NULL;
}
-struct sway_container *container_at(struct sway_container *parent,
- double lx, double ly,
- struct wlr_surface **surface, double *sx, double *sy) {
- if (!sway_assert(parent->type >= C_WORKSPACE,
- "Expected workspace or deeper")) {
- return NULL;
- }
- if (parent->type == C_VIEW) {
- return container_at_view(parent, lx, ly, surface, sx, sy);
- }
- if (!parent->children->length) {
- return NULL;
- }
-
- switch (parent->layout) {
- case L_HORIZ:
- case L_VERT:
- return container_at_linear(parent, lx, ly, surface, sx, sy);
- case L_TABBED:
- return container_at_tabbed(parent, lx, ly, surface, sx, sy);
- case L_STACKED:
- return container_at_stacked(parent, lx, ly, surface, sx, sy);
- case L_FLOATING:
- sway_assert(false, "Didn't expect to see floating here");
- return NULL;
- case L_NONE:
- return NULL;
- }
-
- return NULL;
-}
-
-struct sway_container *floating_container_at(double lx, double ly,
+static struct sway_container *floating_container_at(double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *output = root_container.children->items[i];
@@ -672,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly,
.height = floater->height,
};
if (wlr_box_contains_point(&box, lx, ly)) {
- return container_at(floater, lx, ly, surface, sx, sy);
+ return tiling_container_at(floater, lx, ly,
+ surface, sx, sy);
}
}
}
@@ -680,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly,
return NULL;
}
+static struct sway_container *tiling_container_at(
+ struct sway_container *con, double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ if (con->type == C_VIEW) {
+ return container_at_view(con, lx, ly, surface, sx, sy);
+ }
+ if (!con->children->length) {
+ return NULL;
+ }
+
+ switch (con->layout) {
+ case L_HORIZ:
+ case L_VERT:
+ return container_at_linear(con, lx, ly, surface, sx, sy);
+ case L_TABBED:
+ return container_at_tabbed(con, lx, ly, surface, sx, sy);
+ case L_STACKED:
+ return container_at_stacked(con, lx, ly, surface, sx, sy);
+ case L_FLOATING:
+ sway_assert(false, "Didn't expect to see floating here");
+ return NULL;
+ case L_NONE:
+ return NULL;
+ }
+ return NULL;
+}
+
+static bool surface_is_popup(struct wlr_surface *surface) {
+ if (wlr_surface_is_xdg_surface(surface)) {
+ struct wlr_xdg_surface *xdg_surface =
+ wlr_xdg_surface_from_wlr_surface(surface);
+ while (xdg_surface) {
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
+ return true;
+ }
+ xdg_surface = xdg_surface->toplevel->parent;
+ }
+ return false;
+ }
+
+ if (wlr_surface_is_xdg_surface_v6(surface)) {
+ struct wlr_xdg_surface_v6 *xdg_surface_v6 =
+ wlr_xdg_surface_v6_from_wlr_surface(surface);
+ while (xdg_surface_v6) {
+ if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
+ return true;
+ }
+ xdg_surface_v6 = xdg_surface_v6->toplevel->parent;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+struct sway_container *container_at(struct sway_container *workspace,
+ double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
+ return NULL;
+ }
+ struct sway_container *c;
+ // Focused view's popups
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, &root_container);
+ if (focus && focus->type == C_VIEW) {
+ container_at_view(focus, lx, ly, surface, sx, sy);
+ if (*surface && surface_is_popup(*surface)) {
+ return focus;
+ }
+ *surface = NULL;
+ }
+ // Floating
+ if ((c = floating_container_at(lx, ly, surface, sx, sy))) {
+ return c;
+ }
+ // Tiling
+ if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) {
+ return c;
+ }
+ return NULL;
+}
+
void container_for_each_descendant_dfs(struct sway_container *container,
void (*f)(struct sway_container *container, void *data),
void *data) {
@@ -934,36 +1022,104 @@ size_t container_titlebar_height() {
return config->font_height + TITLEBAR_V_PADDING * 2;
}
+void container_init_floating(struct sway_container *con) {
+ if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER,
+ "Expected a view or container")) {
+ return;
+ }
+ struct sway_container *ws = container_parent(con, C_WORKSPACE);
+ int min_width, min_height;
+ int max_width, max_height;
+
+ if (config->floating_minimum_width == -1) { // no minimum
+ min_width = 0;
+ } else if (config->floating_minimum_width == 0) { // automatic
+ min_width = 75;
+ } else {
+ min_width = config->floating_minimum_width;
+ }
+
+ if (config->floating_minimum_height == -1) { // no minimum
+ min_height = 0;
+ } else if (config->floating_minimum_height == 0) { // automatic
+ min_height = 50;
+ } else {
+ min_height = config->floating_minimum_height;
+ }
+
+ if (config->floating_maximum_width == -1) { // no maximum
+ max_width = INT_MAX;
+ } else if (config->floating_maximum_width == 0) { // automatic
+ max_width = ws->width * 0.6666;
+ } else {
+ max_width = config->floating_maximum_width;
+ }
+
+ if (config->floating_maximum_height == -1) { // no maximum
+ max_height = INT_MAX;
+ } else if (config->floating_maximum_height == 0) { // automatic
+ max_height = ws->height * 0.6666;
+ } else {
+ max_height = config->floating_maximum_height;
+ }
+
+ if (con->type == C_CONTAINER) {
+ con->width = max_width;
+ con->height = max_height;
+ con->x = ws->x + (ws->width - con->width) / 2;
+ con->y = ws->y + (ws->height - con->height) / 2;
+ } else {
+ struct sway_view *view = con->sway_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;
+
+ // If the view's border is B_NONE then these properties are ignored.
+ view->border_top = view->border_bottom = true;
+ view->border_left = view->border_right = true;
+
+ container_set_geometry_from_floating_view(view->swayc);
+ }
+}
+
void container_set_floating(struct sway_container *container, bool enable) {
if (container_is_floating(container) == enable) {
return;
}
- struct sway_container *workspace = container_parent(container, C_WORKSPACE);
struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *workspace = container_parent(container, C_WORKSPACE);
if (enable) {
container_remove_child(container);
container_add_child(workspace->sway_workspace->floating, container);
+ container_init_floating(container);
if (container->type == C_VIEW) {
- view_init_floating(container->sway_view);
view_set_tiled(container->sway_view, false);
}
- seat_set_focus(seat, seat_get_focus_inactive(seat, container));
- container_reap_empty_recursive(workspace);
} else {
// Returning to tiled
+ if (container->scratchpad) {
+ scratchpad_remove_container(container);
+ }
container_remove_child(container);
- container_add_child(workspace, container);
+ struct sway_container *reference =
+ seat_get_focus_inactive_tiling(seat, workspace);
+ if (reference->type == C_VIEW) {
+ reference = reference->parent;
+ }
+ container_add_child(reference, container);
container->width = container->parent->width;
container->height = container->parent->height;
if (container->type == C_VIEW) {
view_set_tiled(container->sway_view, true);
}
container->is_sticky = false;
- container_reap_empty_recursive(workspace->sway_workspace->floating);
}
+ container_end_mouse_operation(container);
+
ipc_event_window(container, "floating");
}
@@ -1009,7 +1165,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
/**
* Translate the container's position as well as all children.
*/
-static void container_floating_translate(struct sway_container *con,
+void container_floating_translate(struct sway_container *con,
double x_amount, double y_amount) {
con->x += x_amount;
con->y += y_amount;
@@ -1105,3 +1261,110 @@ static bool find_urgent_iterator(struct sway_container *con,
bool container_has_urgent_child(struct sway_container *container) {
return container_find(container, find_urgent_iterator, NULL);
}
+
+void container_end_mouse_operation(struct sway_container *container) {
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ if (seat->op_container == container) {
+ seat_end_mouse_operation(seat);
+ }
+ }
+}
+
+static void set_fullscreen_iterator(struct sway_container *con, void *data) {
+ if (con->type != C_VIEW) {
+ return;
+ }
+ if (con->sway_view->impl->set_fullscreen) {
+ bool *enable = data;
+ con->sway_view->impl->set_fullscreen(con->sway_view, *enable);
+ }
+}
+
+void container_set_fullscreen(struct sway_container *container, bool enable) {
+ if (container->is_fullscreen == enable) {
+ return;
+ }
+
+ struct sway_container *workspace = container_parent(container, C_WORKSPACE);
+ if (enable && workspace->sway_workspace->fullscreen) {
+ container_set_fullscreen(workspace->sway_workspace->fullscreen, false);
+ }
+
+ container_for_each_descendant_dfs(container,
+ set_fullscreen_iterator, &enable);
+
+ container->is_fullscreen = enable;
+
+ if (enable) {
+ workspace->sway_workspace->fullscreen = container;
+ container->saved_x = container->x;
+ container->saved_y = container->y;
+ container->saved_width = container->width;
+ container->saved_height = container->height;
+
+ struct sway_seat *seat;
+ struct sway_container *focus, *focus_ws;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ focus = seat_get_focus(seat);
+ if (focus) {
+ focus_ws = focus;
+ if (focus_ws->type != C_WORKSPACE) {
+ focus_ws = container_parent(focus_ws, C_WORKSPACE);
+ }
+ if (focus_ws == workspace) {
+ seat_set_focus(seat, container);
+ }
+ }
+ }
+ } else {
+ workspace->sway_workspace->fullscreen = NULL;
+ if (container_is_floating(container)) {
+ container->x = container->saved_x;
+ container->y = container->saved_y;
+ container->width = container->saved_width;
+ container->height = container->saved_height;
+ } else {
+ container->width = container->saved_width;
+ container->height = container->saved_height;
+ }
+ }
+
+ container_end_mouse_operation(container);
+
+ ipc_event_window(container, "fullscreen_mode");
+}
+
+bool container_is_floating_or_child(struct sway_container *container) {
+ do {
+ if (container->parent && container->parent->layout == L_FLOATING) {
+ return true;
+ }
+ container = container->parent;
+ } while (container && container->type != C_WORKSPACE);
+
+ return false;
+}
+
+bool container_is_fullscreen_or_child(struct sway_container *container) {
+ do {
+ if (container->is_fullscreen) {
+ return true;
+ }
+ container = container->parent;
+ } while (container && container->type != C_WORKSPACE);
+
+ return false;
+}
+
+struct sway_container *container_wrap_children(struct sway_container *parent) {
+ struct sway_container *middle = container_create(C_CONTAINER);
+ middle->layout = parent->layout;
+ while (parent->children->length) {
+ struct sway_container *child = parent->children->items[0];
+ container_remove_child(child);
+ container_add_child(middle, child);
+ }
+ container_add_child(parent, middle);
+ return middle;
+}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 1f898f8a..1f82e534 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
+#include "config.h"
#include "sway/debug.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
@@ -39,9 +40,12 @@ void layout_init(void) {
root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
root_container.sway_root->output_layout = wlr_output_layout_create();
wl_list_init(&root_container.sway_root->outputs);
+#ifdef HAVE_XWAYLAND
wl_list_init(&root_container.sway_root->xwayland_unmanaged);
+#endif
wl_list_init(&root_container.sway_root->drag_icons);
wl_signal_init(&root_container.sway_root->events.new_container);
+ root_container.sway_root->scratchpad = create_list();
root_container.sway_root->output_layout_change.notify =
output_layout_handle_change;
@@ -62,10 +66,9 @@ static int index_child(const struct sway_container *child) {
static void container_handle_fullscreen_reparent(struct sway_container *con,
struct sway_container *old_parent) {
- if (con->type != C_VIEW || !con->sway_view->is_fullscreen) {
+ if (!con->is_fullscreen) {
return;
}
- struct sway_view *view = con->sway_view;
struct sway_container *old_workspace = old_parent;
if (old_workspace && old_workspace->type != C_WORKSPACE) {
old_workspace = container_parent(old_workspace, C_WORKSPACE);
@@ -81,19 +84,27 @@ static void container_handle_fullscreen_reparent(struct sway_container *con,
// Mark the new workspace as fullscreen
if (new_workspace->sway_workspace->fullscreen) {
- view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false);
+ container_set_fullscreen(
+ new_workspace->sway_workspace->fullscreen, false);
}
- new_workspace->sway_workspace->fullscreen = view;
- // Resize view to new output dimensions
+ new_workspace->sway_workspace->fullscreen = con;
+
+ // Resize container to new output dimensions
struct sway_container *output = new_workspace->parent;
- view->x = output->x;
- view->y = output->y;
- view->width = output->width;
- view->height = output->height;
con->x = output->x;
con->y = output->y;
con->width = output->width;
con->height = output->height;
+
+ if (con->type == C_VIEW) {
+ struct sway_view *view = con->sway_view;
+ view->x = output->x;
+ view->y = output->y;
+ view->width = output->width;
+ view->height = output->height;
+ } else {
+ arrange_windows(new_workspace);
+ }
}
void container_insert_child(struct sway_container *parent,
@@ -135,10 +146,14 @@ void container_add_child(struct sway_container *parent,
list_add(parent->children, child);
child->parent = parent;
container_handle_fullscreen_reparent(child, old_parent);
+ if (old_parent) {
+ container_set_dirty(old_parent);
+ }
+ container_set_dirty(child);
}
struct sway_container *container_remove_child(struct sway_container *child) {
- if (child->type == C_VIEW && child->sway_view->is_fullscreen) {
+ if (child->is_fullscreen) {
struct sway_container *workspace = container_parent(child, C_WORKSPACE);
workspace->sway_workspace->fullscreen = NULL;
}
@@ -153,6 +168,9 @@ struct sway_container *container_remove_child(struct sway_container *child) {
child->parent = NULL;
container_notify_subtree_changed(parent);
+ container_set_dirty(parent);
+ container_set_dirty(child);
+
return parent;
}
@@ -199,7 +217,9 @@ void container_move_to(struct sway_container *container,
container_sort_workspaces(new_parent);
seat_set_focus(seat, new_parent);
workspace_output_raise_priority(container, old_parent, new_parent);
- ipc_event_workspace(container, NULL, "move");
+ ipc_event_workspace(NULL, container, "move");
+ } else if (container->type == C_VIEW) {
+ ipc_event_window(container, "move");
}
container_notify_subtree_changed(old_parent);
container_notify_subtree_changed(new_parent);
@@ -218,10 +238,10 @@ void container_move_to(struct sway_container *container,
if (focus_ws->type != C_WORKSPACE) {
focus_ws = container_parent(focus_ws, C_WORKSPACE);
}
- seat_set_focus(seat,
- new_workspace->sway_workspace->fullscreen->swayc);
- if (focus_ws != new_workspace) {
- seat_set_focus(seat, focus);
+ if (focus_ws == new_workspace) {
+ struct sway_container *new_focus = seat_get_focus_inactive(seat,
+ new_workspace->sway_workspace->fullscreen);
+ seat_set_focus(seat, new_focus);
}
}
}
@@ -364,10 +384,18 @@ void container_move(struct sway_container *container,
struct sway_container *sibling = NULL;
struct sway_container *current = container;
struct sway_container *parent = current->parent;
+ struct sway_container *top = &root_container;
// If moving a fullscreen view, only consider outputs
- if (container->type == C_VIEW && container->sway_view->is_fullscreen) {
+ if (container->is_fullscreen) {
current = container_parent(container, C_OUTPUT);
+ } else if (container_is_fullscreen_or_child(container) ||
+ container_is_floating_or_child(container)) {
+ // If we've fullscreened a split container, only allow the child to move
+ // around within the fullscreen parent.
+ // Same with floating a split container.
+ struct sway_container *ws = container_parent(container, C_WORKSPACE);
+ top = ws->sway_workspace->fullscreen;
}
struct sway_container *new_parent = container_flatten(parent);
@@ -377,7 +405,7 @@ void container_move(struct sway_container *container,
}
while (!sibling) {
- if (current->type == C_ROOT) {
+ if (current == top) {
return;
}
@@ -441,8 +469,12 @@ void container_move(struct sway_container *container,
if ((index == parent->children->length - 1 && offs > 0)
|| (index == 0 && offs < 0)) {
if (current->parent == container->parent) {
- if (parent->layout == L_TABBED
- || parent->layout == L_STACKED) {
+ if (parent->parent->layout == L_FLOATING) {
+ return;
+ }
+ if (!parent->is_fullscreen &&
+ (parent->layout == L_TABBED ||
+ parent->layout == L_STACKED)) {
move_out_of_tabs_stacks(container, current,
move_dir, offs);
return;
@@ -463,10 +495,14 @@ void container_move(struct sway_container *container,
sibling = parent->children->items[index + offs];
wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
}
- } else if (parent->layout == L_TABBED
- || parent->layout == L_STACKED) {
+ } else if (!parent->is_fullscreen &&
+ parent->parent->layout != L_FLOATING &&
+ (parent->layout == L_TABBED ||
+ parent->layout == L_STACKED)) {
move_out_of_tabs_stacks(container, current, move_dir, offs);
return;
+ } else if (parent->parent->layout == L_FLOATING) {
+ return;
} else {
wlr_log(WLR_DEBUG, "Moving up to find a parallel container");
current = current->parent;
@@ -544,6 +580,10 @@ void container_move(struct sway_container *container,
container_notify_subtree_changed(old_parent);
container_notify_subtree_changed(container->parent);
+ if (container->type == C_VIEW) {
+ ipc_event_window(container, "move");
+ }
+
if (old_parent) {
seat_set_focus(config->handler_context.seat, old_parent);
seat_set_focus(config->handler_context.seat, container);
@@ -558,10 +598,11 @@ void container_move(struct sway_container *container,
next_ws = container_parent(next_ws, C_WORKSPACE);
}
if (last_ws && next_ws && last_ws != next_ws) {
- ipc_event_workspace(last_ws, container, "focus");
+ ipc_event_workspace(last_ws, next_ws, "focus");
workspace_detect_urgent(last_ws);
workspace_detect_urgent(next_ws);
}
+ container_end_mouse_operation(container);
}
enum sway_container_layout container_get_default_layout(
@@ -691,22 +732,18 @@ struct sway_container *container_get_in_direction(
enum movement_direction dir) {
struct sway_container *parent = container->parent;
- if (container_is_floating(container)) {
- return NULL;
+ if (dir == MOVE_CHILD) {
+ return seat_get_focus_inactive(seat, container);
}
-
- if (container->type == C_VIEW && container->sway_view->is_fullscreen) {
- if (dir == MOVE_PARENT || dir == MOVE_CHILD) {
+ if (container->is_fullscreen) {
+ if (dir == MOVE_PARENT) {
return NULL;
}
container = container_parent(container, C_OUTPUT);
parent = container->parent;
} else {
- if (dir == MOVE_CHILD) {
- return seat_get_focus_inactive(seat, container);
- }
if (dir == MOVE_PARENT) {
- if (parent->type == C_OUTPUT) {
+ if (parent->type == C_OUTPUT || container_is_floating(container)) {
return NULL;
} else {
return parent;
@@ -755,7 +792,8 @@ struct sway_container *container_get_in_direction(
}
sway_assert(next_workspace, "Next container has no workspace");
if (next_workspace->sway_workspace->fullscreen) {
- return next_workspace->sway_workspace->fullscreen->swayc;
+ return seat_get_focus_inactive(seat,
+ next_workspace->sway_workspace->fullscreen);
}
if (next->children && next->children->length) {
// TODO consider floating children as well
@@ -963,13 +1001,13 @@ static void swap_focus(struct sway_container *con1,
if (focus == con1 && (con2->parent->layout == L_TABBED
|| con2->parent->layout == L_STACKED)) {
if (workspace_is_visible(ws2)) {
- seat_set_focus_warp(seat, con2, false);
+ seat_set_focus_warp(seat, con2, false, true);
}
seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
} else if (focus == con2 && (con1->parent->layout == L_TABBED
|| con1->parent->layout == L_STACKED)) {
if (workspace_is_visible(ws1)) {
- seat_set_focus_warp(seat, con1, false);
+ seat_set_focus_warp(seat, con1, false, true);
}
seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
} else if (ws1 != ws2) {
@@ -1002,13 +1040,13 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);
- int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen;
- int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen;
+ int fs1 = con1->is_fullscreen;
+ int fs2 = con2->is_fullscreen;
if (fs1) {
- view_set_fullscreen(con1->sway_view, false);
+ container_set_fullscreen(con1, false);
}
if (fs2) {
- view_set_fullscreen(con2->sway_view, false);
+ container_set_fullscreen(con2, false);
}
struct sway_seat *seat = input_manager_get_default_seat(input_manager);
@@ -1041,10 +1079,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
prev_workspace_name = stored_prev_name;
}
- if (fs1 && con2->type == C_VIEW) {
- view_set_fullscreen(con2->sway_view, true);
+ if (fs1) {
+ container_set_fullscreen(con2, true);
}
- if (fs2 && con1->type == C_VIEW) {
- view_set_fullscreen(con1->sway_view, true);
+ if (fs2) {
+ container_set_fullscreen(con1, true);
}
}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index da535c18..31e3bf9b 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) {
if (highest == output) {
container_remove_child(ws);
container_add_child(output, ws);
- ipc_event_workspace(ws, NULL, "move");
+ ipc_event_workspace(NULL, ws, "move");
j--;
}
}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7881e6d7..97318daa 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -2,7 +2,12 @@
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_output_layout.h>
+#include "config.h"
+#ifdef HAVE_XWAYLAND
+#include <wlr/xwayland.h>
+#endif
#include "list.h"
#include "log.h"
#include "sway/criteria.h"
@@ -107,14 +112,14 @@ const char *view_get_instance(struct sway_view *view) {
}
return NULL;
}
-
+#ifdef HAVE_XWAYLAND
uint32_t view_get_x11_window_id(struct sway_view *view) {
if (view->impl->get_int_prop) {
return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
}
return 0;
}
-
+#endif
const char *view_get_window_role(struct sway_view *view) {
if (view->impl->get_string_prop) {
return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE);
@@ -135,12 +140,27 @@ const char *view_get_shell(struct sway_view *view) {
return "xdg_shell_v6";
case SWAY_VIEW_XDG_SHELL:
return "xdg_shell";
+#ifdef HAVE_XWAYLAND
case SWAY_VIEW_XWAYLAND:
return "xwayland";
+#endif
}
return "unknown";
}
+void view_get_constraints(struct sway_view *view, double *min_width,
+ double *max_width, double *min_height, double *max_height) {
+ if (view->impl->get_constraints) {
+ view->impl->get_constraints(view,
+ min_width, max_width, min_height, max_height);
+ } else {
+ *min_width = DBL_MIN;
+ *max_width = DBL_MAX;
+ *min_height = DBL_MIN;
+ *max_height = DBL_MAX;
+ }
+}
+
uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
int height) {
if (view->impl->configure) {
@@ -149,55 +169,6 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
return 0;
}
-void view_init_floating(struct sway_view *view) {
- struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- int min_width, min_height;
- int max_width, max_height;
-
- if (config->floating_minimum_width == -1) { // no minimum
- min_width = 0;
- } else if (config->floating_minimum_width == 0) { // automatic
- min_width = 75;
- } else {
- min_width = config->floating_minimum_width;
- }
-
- if (config->floating_minimum_height == -1) { // no minimum
- min_height = 0;
- } else if (config->floating_minimum_height == 0) { // automatic
- min_height = 50;
- } else {
- min_height = config->floating_minimum_height;
- }
-
- if (config->floating_maximum_width == -1) { // no maximum
- max_width = INT_MAX;
- } else if (config->floating_maximum_width == 0) { // automatic
- max_width = ws->width * 0.6666;
- } else {
- max_width = config->floating_maximum_width;
- }
-
- if (config->floating_maximum_height == -1) { // no maximum
- max_height = INT_MAX;
- } else if (config->floating_maximum_height == 0) { // automatic
- max_height = ws->height * 0.6666;
- } else {
- max_height = config->floating_maximum_height;
- }
-
- 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;
-
- // If the view's border is B_NONE then these properties are ignored.
- view->border_top = view->border_bottom = true;
- view->border_left = view->border_right = true;
-
- container_set_geometry_from_floating_view(view->swayc);
-}
-
void view_autoconfigure(struct sway_view *view) {
if (!sway_assert(view->swayc,
"Called view_autoconfigure() on a view without a swayc")) {
@@ -206,7 +177,7 @@ void view_autoconfigure(struct sway_view *view) {
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
- if (view->is_fullscreen) {
+ if (view->swayc->is_fullscreen) {
view->x = output->x;
view->y = output->y;
view->width = output->width;
@@ -214,10 +185,6 @@ void view_autoconfigure(struct sway_view *view) {
return;
}
- if (container_is_floating(view->swayc)) {
- return;
- }
-
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
int other_views = 0;
@@ -330,72 +297,18 @@ void view_set_tiled(struct sway_view *view, bool tiled) {
}
}
-void view_set_fullscreen(struct sway_view *view, bool fullscreen) {
- if (view->is_fullscreen == fullscreen) {
- return;
- }
-
- struct sway_container *workspace =
- container_parent(view->swayc, C_WORKSPACE);
-
- if (view->impl->set_fullscreen) {
- view->impl->set_fullscreen(view, fullscreen);
- }
-
- view->is_fullscreen = fullscreen;
-
- if (fullscreen) {
- if (workspace->sway_workspace->fullscreen) {
- view_set_fullscreen(workspace->sway_workspace->fullscreen, false);
- }
- workspace->sway_workspace->fullscreen = view;
- view->saved_x = view->x;
- view->saved_y = view->y;
- view->saved_width = view->width;
- view->saved_height = view->height;
- view->swayc->saved_x = view->swayc->x;
- view->swayc->saved_y = view->swayc->y;
- view->swayc->saved_width = view->swayc->width;
- view->swayc->saved_height = view->swayc->height;
-
- struct sway_seat *seat;
- struct sway_container *focus, *focus_ws;
- wl_list_for_each(seat, &input_manager->seats, link) {
- focus = seat_get_focus(seat);
- if (focus) {
- focus_ws = focus;
- if (focus && focus_ws->type != C_WORKSPACE) {
- focus_ws = container_parent(focus_ws, C_WORKSPACE);
- }
- seat_set_focus(seat, view->swayc);
- if (focus_ws != workspace) {
- seat_set_focus(seat, focus);
- }
- }
- }
- } else {
- workspace->sway_workspace->fullscreen = NULL;
- if (container_is_floating(view->swayc)) {
- view->x = view->saved_x;
- view->y = view->saved_y;
- view->width = view->saved_width;
- view->height = view->saved_height;
- container_set_geometry_from_floating_view(view->swayc);
- } else {
- view->swayc->width = view->swayc->saved_width;
- view->swayc->height = view->swayc->saved_height;
- }
- }
-
- ipc_event_window(view->swayc, "fullscreen_mode");
-}
-
void view_close(struct sway_view *view) {
if (view->impl->close) {
view->impl->close(view);
}
}
+void view_close_popups(struct sway_view *view) {
+ if (view->impl->close_popups) {
+ view->impl->close_popups(view);
+ }
+}
+
void view_damage_from(struct sway_view *view) {
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *cont = root_container.children->items[i];
@@ -426,6 +339,16 @@ void view_for_each_surface(struct sway_view *view,
}
}
+void view_for_each_popup(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (!view->surface) {
+ return;
+ }
+ if (view->impl->for_each_popup) {
+ view->impl->for_each_popup(view, iterator, user_data);
+ }
+}
+
static void view_subsurface_create(struct sway_view *view,
struct wlr_subsurface *subsurface);
@@ -521,12 +444,82 @@ void view_execute_criteria(struct sway_view *view) {
seat_set_focus(seat, prior_focus);
}
+static struct sway_container *select_workspace(struct sway_view *view) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+
+ // Check if there's any `assign` criteria for the view
+ list_t *criterias = criteria_for_view(view,
+ CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT);
+ struct sway_container *ws = NULL;
+ for (int i = 0; i < criterias->length; ++i) {
+ struct criteria *criteria = criterias->items[i];
+ if (criteria->type == CT_ASSIGN_WORKSPACE) {
+ ws = workspace_by_name(criteria->target);
+ if (!ws) {
+ ws = workspace_create(NULL, criteria->target);
+ }
+ break;
+ } else {
+ // CT_ASSIGN_OUTPUT
+ struct sway_container *output = output_by_name(criteria->target);
+ if (output) {
+ ws = seat_get_active_child(seat, output);
+ break;
+ }
+ }
+ }
+ list_free(criterias);
+ if (ws) {
+ return ws;
+ }
+
+ // Check if there's a PID mapping
+ pid_t pid;
+#ifdef HAVE_XWAYLAND
+ if (view->type == SWAY_VIEW_XWAYLAND) {
+ struct wlr_xwayland_surface *surf =
+ wlr_xwayland_surface_from_wlr_surface(view->surface);
+ pid = surf->pid;
+ } else {
+ struct wl_client *client =
+ wl_resource_get_client(view->surface->resource);
+ wl_client_get_credentials(client, &pid, NULL, NULL);
+ }
+#else
+ struct wl_client *client =
+ wl_resource_get_client(view->surface->resource);
+ wl_client_get_credentials(client, &pid, NULL, NULL);
+#endif
+ ws = workspace_for_pid(pid);
+ if (ws) {
+ return ws;
+ }
+
+ // Use the focused workspace
+ ws = seat_get_focus_inactive(seat, &root_container);
+ if (ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+ return ws;
+}
+
static bool should_focus(struct sway_view *view) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *prev_focus =
+ seat_get_focus_inactive(seat, &root_container);
+ struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ?
+ prev_focus : container_parent(prev_focus, C_WORKSPACE);
+ struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE);
+
+ // Views can only take focus if they are mapped into the active workspace
+ if (prev_ws != map_ws) {
+ return false;
+ }
+
// If the view is the only one in the focused workspace, it'll get focus
// regardless of any no_focus criteria.
struct sway_container *parent = view->swayc->parent;
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) {
+ if (parent->type == C_WORKSPACE && prev_focus == parent) {
size_t num_children = parent->children->length +
parent->sway_workspace->floating->children->length;
if (num_children == 1) {
@@ -545,42 +538,19 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
return;
}
+ view->surface = wlr_surface;
struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus =
- seat_get_focus_inactive(seat, &root_container);
- struct sway_container *cont = NULL;
+ struct sway_container *ws = select_workspace(view);
+ struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws);
- // Check if there's any `assign` criteria for the view
- list_t *criterias = criteria_for_view(view,
- CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT);
- struct sway_container *workspace = NULL;
- if (criterias->length) {
- struct criteria *criteria = criterias->items[0];
- if (criteria->type == CT_ASSIGN_WORKSPACE) {
- workspace = workspace_by_name(criteria->target);
- if (!workspace) {
- workspace = workspace_create(NULL, criteria->target);
- }
- focus = seat_get_focus_inactive(seat, workspace);
- } else {
- // CT_ASSIGN_OUTPUT
- struct sway_container *output = output_by_name(criteria->target);
- if (output) {
- focus = seat_get_focus_inactive(seat, output);
- }
- }
- }
// If we're about to launch the view into the floating container, then
// launch it as a tiled view in the root of the workspace instead.
- if (container_is_floating(focus)) {
- focus = focus->parent->parent;
+ if (container_is_floating(target_sibling)) {
+ target_sibling = target_sibling->parent->parent;
}
- list_free(criterias);
- cont = container_view_create(focus, view);
- view->surface = wlr_surface;
- view->swayc = cont;
+ view->swayc = container_view_create(target_sibling, view);
view_init_subsurfaces(view, wlr_surface);
wl_signal_add(&wlr_surface->events.new_subsurface,
@@ -601,10 +571,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
}
if (should_focus(view)) {
- input_manager_set_focus(input_manager, cont);
- if (workspace) {
- workspace_switch(workspace);
- }
+ input_manager_set_focus(input_manager, view->swayc);
}
view_update_title(view, false);
@@ -628,10 +595,8 @@ void view_unmap(struct sway_view *view) {
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
struct sway_container *parent;
- if (view->is_fullscreen) {
- ws->sway_workspace->fullscreen = NULL;
+ if (container_is_fullscreen_or_child(view->swayc)) {
parent = container_destroy(view->swayc);
-
arrange_windows(ws->parent);
} else {
parent = container_destroy(view->swayc);
@@ -784,11 +749,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
wlr_xdg_surface_v6_from_wlr_surface(wlr_surface);
return view_from_wlr_xdg_surface_v6(xdg_surface_v6);
}
+#ifdef HAVE_XWAYLAND
if (wlr_surface_is_xwayland_surface(wlr_surface)) {
struct wlr_xwayland_surface *xsurface =
wlr_xwayland_surface_from_wlr_surface(wlr_surface);
return view_from_wlr_xwayland_surface(xsurface);
}
+#endif
if (wlr_surface_is_subsurface(wlr_surface)) {
struct wlr_subsurface *subsurface =
wlr_subsurface_from_wlr_surface(wlr_surface);
@@ -915,6 +882,8 @@ void view_update_title(struct sway_view *view, bool force) {
// Update title after the global font height is updated
container_update_title_textures(view->swayc);
+
+ ipc_event_window(view->swayc, "title");
}
static bool find_by_mark_iterator(struct sway_container *con,
@@ -937,6 +906,7 @@ bool view_find_and_unmark(char *mark) {
free(view_mark);
list_del(view->marks, i);
view_update_marks_textures(view);
+ ipc_event_window(container, "mark");
return true;
}
}
@@ -944,11 +914,10 @@ bool view_find_and_unmark(char *mark) {
}
void view_clear_marks(struct sway_view *view) {
- for (int i = 0; i < view->marks->length; ++i) {
- free(view->marks->items[i]);
+ while (view->marks->length) {
+ list_del(view->marks, 0);
+ ipc_event_window(view->swayc, "mark");
}
- list_free(view->marks);
- view->marks = create_list();
}
bool view_has_mark(struct sway_view *view, char *mark) {
@@ -961,6 +930,11 @@ bool view_has_mark(struct sway_view *view, char *mark) {
return false;
}
+void view_add_mark(struct sway_view *view, char *mark) {
+ list_add(view->marks, strdup(mark));
+ ipc_event_window(view->swayc, "mark");
+}
+
static void update_marks_texture(struct sway_view *view,
struct wlr_texture **texture, struct border_colors *class) {
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
@@ -1055,6 +1029,9 @@ bool view_is_visible(struct sway_view *view) {
}
struct sway_container *workspace =
container_parent(view->swayc, C_WORKSPACE);
+ if (!workspace) {
+ return false;
+ }
// Determine if view is nested inside a floating container which is sticky.
// A simple floating view will have this ancestry:
// C_VIEW -> floating -> workspace
@@ -1079,7 +1056,8 @@ bool view_is_visible(struct sway_view *view) {
container = container->parent;
}
// Check view isn't hidden by another fullscreen view
- if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
+ if (workspace->sway_workspace->fullscreen &&
+ !container_is_fullscreen_or_child(view->swayc)) {
return false;
}
// Check the workspace is visible
@@ -1117,3 +1095,22 @@ void view_set_urgent(struct sway_view *view, bool enable) {
bool view_is_urgent(struct sway_view *view) {
return view->urgent.tv_sec || view->urgent.tv_nsec;
}
+
+void view_remove_saved_buffer(struct sway_view *view) {
+ if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) {
+ return;
+ }
+ wlr_buffer_unref(view->saved_buffer);
+ view->saved_buffer = NULL;
+}
+
+void view_save_buffer(struct sway_view *view) {
+ if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) {
+ view_remove_saved_buffer(view);
+ }
+ if (view->surface && wlr_surface_has_buffer(view->surface)) {
+ view->saved_buffer = wlr_buffer_ref(view->surface->buffer);
+ view->saved_buffer_width = view->surface->current.width;
+ view->saved_buffer_height = view->surface->current.height;
+ }
+}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 622f01ec..588e2aae 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -9,6 +9,7 @@
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/ipc-server.h"
+#include "sway/output.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
@@ -107,96 +108,100 @@ static bool workspace_valid_on_output(const char *output_name,
return true;
}
-char *workspace_next_name(const char *output_name) {
- wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s",
- output_name);
- // Scan all workspace bindings to find the next available workspace name,
- // if none are found/available then default to a number
- struct sway_mode *mode = config->current_mode;
+static void workspace_name_from_binding(const struct sway_binding * binding,
+ const char* output_name, int *min_order, char **earliest_name) {
+ char *cmdlist = strdup(binding->command);
+ char *dup = cmdlist;
+ char *name = NULL;
- // TODO: iterate over keycode bindings too
- int order = INT_MAX;
- char *target = NULL;
- for (int i = 0; i < mode->keysym_bindings->length; ++i) {
- struct sway_binding *binding = mode->keysym_bindings->items[i];
- char *cmdlist = strdup(binding->command);
- char *dup = cmdlist;
- char *name = NULL;
-
- // workspace n
- char *cmd = argsep(&cmdlist, " ");
- if (cmdlist) {
- name = argsep(&cmdlist, ",;");
- }
+ // workspace n
+ char *cmd = argsep(&cmdlist, " ");
+ if (cmdlist) {
+ name = argsep(&cmdlist, ",;");
+ }
- if (strcmp("workspace", cmd) == 0 && name) {
- char *_target = strdup(name);
- _target = do_var_replacement(_target);
- strip_quotes(_target);
- while (isspace(*_target)) {
- memmove(_target, _target+1, strlen(_target+1));
- }
- wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",
- _target);
+ if (strcmp("workspace", cmd) == 0 && name) {
+ char *_target = strdup(name);
+ _target = do_var_replacement(_target);
+ strip_quotes(_target);
+ wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",
+ _target);
- // Make sure that the command references an actual workspace
- // not a command about workspaces
- if (strcmp(_target, "next") == 0 ||
+ // Make sure that the command references an actual workspace
+ // not a command about workspaces
+ if (strcmp(_target, "next") == 0 ||
strcmp(_target, "prev") == 0 ||
strcmp(_target, "next_on_output") == 0 ||
strcmp(_target, "prev_on_output") == 0 ||
strcmp(_target, "number") == 0 ||
strcmp(_target, "back_and_forth") == 0 ||
- strcmp(_target, "current") == 0)
- {
- free(_target);
- free(dup);
- continue;
- }
-
- // If the command is workspace number <name>, isolate the name
- if (strncmp(_target, "number ", strlen("number ")) == 0) {
- size_t length = strlen(_target) - strlen("number ") + 1;
- char *temp = malloc(length);
- strncpy(temp, _target + strlen("number "), length - 1);
- temp[length - 1] = '\0';
- free(_target);
- _target = temp;
- wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);
-
- // Make sure the workspace number doesn't already exist
- if (workspace_by_number(_target)) {
- free(_target);
- free(dup);
- continue;
- }
- }
+ strcmp(_target, "current") == 0) {
+ free(_target);
+ free(dup);
+ return;
+ }
- // Make sure that the workspace doesn't already exist
- if (workspace_by_name(_target)) {
+ // If the command is workspace number <name>, isolate the name
+ if (strncmp(_target, "number ", strlen("number ")) == 0) {
+ size_t length = strlen(_target) - strlen("number ") + 1;
+ char *temp = malloc(length);
+ strncpy(temp, _target + strlen("number "), length - 1);
+ temp[length - 1] = '\0';
+ free(_target);
+ _target = temp;
+ wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);
+
+ // Make sure the workspace number doesn't already exist
+ if (workspace_by_number(_target)) {
free(_target);
free(dup);
- continue;
+ return;
}
+ }
- // make sure that the workspace can appear on the given
- // output
- if (!workspace_valid_on_output(output_name, _target)) {
- free(_target);
- free(dup);
- continue;
- }
+ // Make sure that the workspace doesn't already exist
+ if (workspace_by_name(_target)) {
+ free(_target);
+ free(dup);
+ return;
+ }
- if (binding->order < order) {
- order = binding->order;
- free(target);
- target = _target;
- wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target);
- } else {
- free(_target);
- }
+ // make sure that the workspace can appear on the given
+ // output
+ if (!workspace_valid_on_output(output_name, _target)) {
+ free(_target);
+ free(dup);
+ return;
}
- free(dup);
+
+ if (binding->order < *min_order) {
+ *min_order = binding->order;
+ free(*earliest_name);
+ *earliest_name = _target;
+ wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target);
+ } else {
+ free(_target);
+ }
+ }
+ free(dup);
+}
+
+char *workspace_next_name(const char *output_name) {
+ wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s",
+ output_name);
+ // Scan all workspace bindings to find the next available workspace name,
+ // if none are found/available then default to a number
+ struct sway_mode *mode = config->current_mode;
+
+ int order = INT_MAX;
+ char *target = NULL;
+ for (int i = 0; i < mode->keysym_bindings->length; ++i) {
+ workspace_name_from_binding(mode->keysym_bindings->items[i],
+ output_name, &order, &target);
+ }
+ for (int i = 0; i < mode->keycode_bindings->length; ++i) {
+ workspace_name_from_binding(mode->keycode_bindings->items[i],
+ output_name, &order, &target);
}
if (target != NULL) {
return target;
@@ -529,3 +534,116 @@ void workspace_detect_urgent(struct sway_container *workspace) {
container_damage_whole(workspace);
}
}
+
+struct pid_workspace {
+ pid_t pid;
+ char *workspace;
+ struct timespec time_added;
+
+ struct sway_container *output;
+ struct wl_listener output_destroy;
+
+ struct wl_list link;
+};
+
+static struct wl_list pid_workspaces;
+
+struct sway_container *workspace_for_pid(pid_t pid) {
+ if (!pid_workspaces.prev && !pid_workspaces.next) {
+ wl_list_init(&pid_workspaces);
+ return NULL;
+ }
+
+ struct sway_container *ws = NULL;
+ struct pid_workspace *pw = NULL;
+
+ wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid);
+
+ do {
+ struct pid_workspace *_pw = NULL;
+ wl_list_for_each(_pw, &pid_workspaces, link) {
+ if (pid == _pw->pid) {
+ pw = _pw;
+ wlr_log(WLR_DEBUG,
+ "found pid_workspace for pid %d, workspace %s",
+ pid, pw->workspace);
+ goto found;
+ }
+ }
+ pid = get_parent_pid(pid);
+ } while (pid > 1);
+found:
+
+ if (pw && pw->workspace) {
+ ws = workspace_by_name(pw->workspace);
+
+ if (!ws) {
+ wlr_log(WLR_DEBUG,
+ "Creating workspace %s for pid %d because it disappeared",
+ pw->workspace, pid);
+ ws = workspace_create(pw->output, pw->workspace);
+ }
+
+ wl_list_remove(&pw->output_destroy.link);
+ wl_list_remove(&pw->link);
+ free(pw->workspace);
+ free(pw);
+ }
+
+ return ws;
+}
+
+static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
+ struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
+ pw->output = NULL;
+ wl_list_remove(&pw->output_destroy.link);
+ wl_list_init(&pw->output_destroy.link);
+}
+
+void workspace_record_pid(pid_t pid) {
+ wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid);
+ if (!pid_workspaces.prev && !pid_workspaces.next) {
+ wl_list_init(&pid_workspaces);
+ }
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *ws =
+ seat_get_focus_inactive(seat, &root_container);
+ if (ws && ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+ if (!ws) {
+ wlr_log(WLR_DEBUG, "Bailing out, no workspace");
+ return;
+ }
+ struct sway_container *output = ws->parent;
+ if (!output) {
+ wlr_log(WLR_DEBUG, "Bailing out, no output");
+ return;
+ }
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ // Remove expired entries
+ static const int timeout = 60;
+ struct pid_workspace *old, *_old;
+ wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
+ if (now.tv_sec - old->time_added.tv_sec >= timeout) {
+ wl_list_remove(&old->output_destroy.link);
+ wl_list_remove(&old->link);
+ free(old->workspace);
+ free(old);
+ }
+ }
+
+ struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
+ pw->workspace = strdup(ws->name);
+ pw->output = output;
+ pw->pid = pid;
+ memcpy(&pw->time_added, &now, sizeof(struct timespec));
+ pw->output_destroy.notify = pw_handle_output_destroy;
+ wl_signal_add(&output->sway_output->wlr_output->events.destroy,
+ &pw->output_destroy);
+ wl_list_insert(&pid_workspaces, &pw->link);
+}