diff --git a/README.de.md b/README.de.md index a872e888..9d8a41ce 100644 --- a/README.de.md +++ b/README.de.md @@ -76,10 +76,6 @@ Führe diese Befehle aus: ninja -C build sudo ninja -C build install -In Systemen mit logind musst du `sway` einige Capabilities geben: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - In Systemen ohne logind musst du `sway` das suid-Flag geben: sudo chmod a+s /usr/local/bin/sway diff --git a/README.el.md b/README.el.md index 6887fe8e..53fdd2d9 100644 --- a/README.el.md +++ b/README.el.md @@ -69,10 +69,6 @@ _\*\*Απαιτείται μόνο για swaylock_ ninja -C build sudo ninja -C build install -Σε συστήματα με logind, χρειάζεται να ορίσετε μερικά δικαιώματα caps στο εκτελέσιμο αρχείο: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - Σε συστήματα χωρίς logind, χρειάζεται να θέσετε το suid bit στο εκτελέσιμο αρχείο: sudo chmod a+s /usr/local/bin/sway diff --git a/README.fr.md b/README.fr.md index 6ea4d14c..0a9697b8 100644 --- a/README.fr.md +++ b/README.fr.md @@ -71,10 +71,6 @@ Exécutez ces commandes : ninja -C build sudo ninja -C build install -Sur les systèmes avec logind, vous devez définir quelques caps sur le binaire : - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - Sur les systèmes sans logind, vous devez suid le binaire de sway : sudo chmod a+s /usr/local/bin/sway diff --git a/README.it.md b/README.it.md index 3b1b1ebc..653e6aea 100644 --- a/README.it.md +++ b/README.it.md @@ -72,10 +72,6 @@ Esegui questi comandi: ninja -C build sudo ninja -C build install -Per i sistemi con logind, devi impostare un paio di caps sull'eseguibile: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - Per i sistemi senza logind, devi cambiare i permessi (suid): sudo chmod a+s /usr/local/bin/sway diff --git a/README.ja.md b/README.ja.md index 7b437966..75d29c73 100644 --- a/README.ja.md +++ b/README.ja.md @@ -62,14 +62,12 @@ _\*\*swaylockでのみ必要です_ ninja -C build sudo ninja -C build install -logindを使用しているシステムでは、バイナリにいくつかのケーパビリティを設定する必要があります: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - logindを使用していないシステムでは、バイナリにsuidを設定する必要があります: sudo chmod a+s /usr/local/bin/sway +swayは起動後、すぐにroot許可を落とします。 + ## 設定 既にi3を使用している場合は、i3の設定ファイルを`~/.config/sway/config`にコピーすれば動きます。そうでない場合は、サンプルの設定ファイルを`~/.config/sway/config`にコピーしてください。サンプルの設定ファイルは、通常`/etc/sway/config`にあります。`man 5 sway`を実行することで、設定に関する情報を見ることができます。 diff --git a/README.md b/README.md index ce753265..0bad8343 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,12 @@ Run these commands: ninja -C build sudo ninja -C build install -On systems with logind, you need to set a few caps on the binary: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - On systems without logind, you need to suid the sway binary: sudo chmod a+s /usr/local/bin/sway +Sway will drop root permissions shortly after startup. + ## Configuration If you already use i3, then copy your i3 config to `~/.config/sway/config` and diff --git a/common/ipc-client.c b/common/ipc-client.c index 4d2d88cc..24a2f9c2 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -25,6 +25,7 @@ char *get_socketpath(void) { if (line && *line) { return line; } + free(line); } const char *i3sock = getenv("I3SOCK"); if (i3sock) { @@ -37,6 +38,7 @@ char *get_socketpath(void) { if (line && *line) { return line; } + free(line); } return NULL; } diff --git a/include/sway/decoration.h b/include/sway/decoration.h new file mode 100644 index 00000000..7916746e --- /dev/null +++ b/include/sway/decoration.h @@ -0,0 +1,17 @@ +#ifndef _SWAY_DECORATION_H +#define _SWAY_DECORATION_H + +#include + +struct sway_server_decoration { + struct wlr_server_decoration *wlr_server_decoration; + struct wl_list link; + + struct wl_listener destroy; + struct wl_listener mode; +}; + +struct sway_server_decoration *decoration_from_surface( + struct wlr_surface *surface); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index a3782f91..b93584b6 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -4,12 +4,13 @@ #include #include #include +#include #include #include #include +#include #include #include -#include // TODO WLR: make Xwayland optional #include "list.h" #include "config.h" @@ -42,11 +43,17 @@ struct sway_server { struct wlr_xdg_shell *xdg_shell; struct wl_listener xdg_shell_surface; + #ifdef HAVE_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; struct wl_listener xwayland_ready; #endif + + struct wlr_server_decoration_manager *server_decoration_manager; + struct wl_listener server_decoration; + struct wl_list decorations; // sway_server_decoration::link + bool debug_txn_timings; list_t *transactions; @@ -71,4 +78,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data); #ifdef HAVE_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); #endif +void handle_server_decoration(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 44ff9f7d..4d0e6003 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -113,7 +113,7 @@ struct sway_container { enum sway_container_type type; enum sway_container_layout layout; - enum sway_container_layout prev_layout; + enum sway_container_layout prev_split_layout; bool is_sticky; @@ -322,12 +322,23 @@ void container_get_box(struct sway_container *container, struct wlr_box *box); void container_floating_translate(struct sway_container *con, double x_amount, double y_amount); +/** + * Choose an output for the floating container's new position. + */ +struct sway_container *container_floating_find_output( + struct sway_container *con); + /** * Move a floating container to a new layout-local position. */ void container_floating_move_to(struct sway_container *con, double lx, double ly); +/** + * Move a floating container to the center of the workspace. + */ +void container_floating_move_to_center(struct sway_container *con); + /** * Mark a container as dirty if it isn't already. Dirty containers will be * included in the next transaction then unmarked as dirty. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 37fd02bc..c2225bcb 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -118,6 +118,8 @@ struct sway_view { struct sway_xdg_shell_v6_view { struct sway_view view; + enum wlr_server_decoration_manager_mode deco_mode; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -134,6 +136,8 @@ struct sway_xdg_shell_v6_view { struct sway_xdg_shell_view { struct sway_view view; + enum wlr_server_decoration_manager_mode deco_mode; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -315,6 +319,11 @@ void view_update_title(struct sway_view *view, bool force); */ void view_execute_criteria(struct sway_view *view); +/** + * Find any view that has the given mark and return it. + */ +struct sway_view *view_find_mark(char *mark); + /** * Find any view that has the given mark and remove the mark from the view. * Returns true if it matched a view. diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 3337f2c8..056f2329 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -1,6 +1,7 @@ #ifndef _SWAY_WORKSPACE_H #define _SWAY_WORKSPACE_H +#include #include "sway/tree/container.h" struct sway_view; @@ -15,9 +16,12 @@ struct sway_workspace { extern char *prev_workspace_name; +struct sway_container *workspace_get_initial_output(const char *name); + char *workspace_next_name(const char *output_name); -bool workspace_switch(struct sway_container *workspace); +bool workspace_switch(struct sway_container *workspace, + bool no_auto_back_and_forth); struct sway_container *workspace_by_number(const char* name); diff --git a/sway/commands/layout.c b/sway/commands/layout.c index c446f1f9..f4e4dda9 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -1,3 +1,4 @@ +#include #include #include #include "sway/commands.h" @@ -5,6 +6,26 @@ #include "sway/tree/container.h" #include "log.h" +static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { + if (strcasecmp(s, "splith") == 0) { + *ptr = L_HORIZ; + } else if (strcasecmp(s, "splitv") == 0) { + *ptr = L_VERT; + } else if (strcasecmp(s, "tabbed") == 0) { + *ptr = L_TABBED; + } else if (strcasecmp(s, "stacking") == 0) { + *ptr = L_STACKED; + } else { + return false; + } + return true; +} + +static const char* expected_syntax = + "Expected 'layout default|tabbed|stacking|splitv|splith' or " + "'layout toggle [split|all]' or " + "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; + struct cmd_results *cmd_layout(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { @@ -21,35 +42,69 @@ struct cmd_results *cmd_layout(int argc, char **argv) { parent = parent->parent; } - if (strcasecmp(argv[0], "default") == 0) { - parent->layout = parent->prev_layout; - if (parent->layout == L_NONE) { - parent->layout = container_get_default_layout(parent); - } - } else { - if (parent->layout != L_TABBED && parent->layout != L_STACKED) { - parent->prev_layout = parent->layout; - } - - if (strcasecmp(argv[0], "splith") == 0) { - parent->layout = L_HORIZ; - } else if (strcasecmp(argv[0], "splitv") == 0) { - parent->layout = L_VERT; - } else if (strcasecmp(argv[0], "tabbed") == 0) { - parent->layout = L_TABBED; - } else if (strcasecmp(argv[0], "stacking") == 0) { - parent->layout = L_STACKED; - } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { - if (parent->layout == L_HORIZ) { - parent->layout = L_VERT; + enum sway_container_layout prev = parent->layout; + bool assigned_directly = parse_layout_string(argv[0], &parent->layout); + if (!assigned_directly) { + if (strcasecmp(argv[0], "default") == 0) { + parent->layout = parent->prev_split_layout; + } else if (strcasecmp(argv[0], "toggle") == 0) { + if (argc == 1) { + parent->layout = + parent->layout == L_STACKED ? L_TABBED : + parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; + } else if (argc == 2) { + if (strcasecmp(argv[1], "all") == 0) { + parent->layout = + parent->layout == L_HORIZ ? L_VERT : + parent->layout == L_VERT ? L_STACKED : + parent->layout == L_STACKED ? L_TABBED : L_HORIZ; + } else if (strcasecmp(argv[1], "split") == 0) { + parent->layout = + parent->layout == L_HORIZ ? L_VERT : + parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; + } else { + return cmd_results_new(CMD_INVALID, "layout", expected_syntax); + } } else { - parent->layout = L_HORIZ; + enum sway_container_layout parsed_layout; + int curr = 1; + for (; curr < argc; curr++) { + bool valid = parse_layout_string(argv[curr], &parsed_layout); + if ((valid && parsed_layout == parent->layout) || + (strcmp(argv[curr], "split") == 0 && + (parent->layout == L_VERT || parent->layout == L_HORIZ))) { + break; + } + } + for (int i = curr + 1; i != curr; ++i) { + // cycle round to find next valid layout + if (i >= argc) { + i = 1; + } + if (parse_layout_string(argv[i], &parent->layout)) { + break; + } else if (strcmp(argv[i], "split") == 0) { + parent->layout = + parent->layout == L_HORIZ ? L_VERT : + parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; + break; + } // invalid layout strings are silently ignored + } } + } else { + return cmd_results_new(CMD_INVALID, "layout", expected_syntax); } } - - container_notify_subtree_changed(parent); - arrange_windows(parent); + if (parent->layout == L_NONE) { + parent->layout = container_get_default_layout(parent); + } + if (prev != parent->layout) { + if (prev != L_TABBED && prev != L_STACKED) { + parent->prev_split_layout = prev; + } + container_notify_subtree_changed(parent); + arrange_windows(parent); + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 841da4c4..de6b1b0a 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include #include @@ -8,6 +9,7 @@ #include "sway/commands.h" #include "sway/input/cursor.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" @@ -15,12 +17,13 @@ #include "sway/tree/workspace.h" #include "stringop.h" #include "list.h" +#include "log.h" -static const char* expected_syntax = +static const char *expected_syntax = "Expected 'move <[px] px>' or " - "'move to workspace ' or " - "'move to output ' or " - "'move position mouse'"; + "'move [--no-auto-back-and-forth] [to] workspace ' or " + "'move [--no-auto-back-and-forth] [to] output ' or " + "'move [to] mark '"; static struct sway_container *output_in_direction(const char *direction, struct wlr_output *reference, int ref_lx, int ref_ly) { @@ -52,128 +55,236 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move container/window", - EXPECTED_AT_LEAST, 4))) { + EXPECTED_AT_LEAST, 3))) { return error; - } else if (strcasecmp(argv[1], "to") == 0 - && strcasecmp(argv[2], "workspace") == 0) { - // move container to workspace x - if (current->type == C_WORKSPACE) { - if (current->children->length == 0) { - return cmd_results_new(CMD_FAILURE, "move", - "Can't move an empty workspace"); - } - 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."); - } - struct sway_container *ws; - char *ws_name = NULL; - if (argc == 5 && strcasecmp(argv[3], "number") == 0) { - // move "container to workspace number x" - ws_name = strdup(argv[4]); - ws = workspace_by_number(ws_name); - } else { - ws_name = join_args(argv + 3, argc - 3); - ws = workspace_by_name(ws_name); - } + } - if (config->auto_back_and_forth && prev_workspace_name) { - // auto back and forth move - struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); - if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { - // if target workspace is the current one - free(ws_name); - ws_name = strdup(prev_workspace_name); + if (current->type == C_WORKSPACE) { + if (current->children->length == 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Can't move an empty workspace"); + } + 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."); + } + + bool no_auto_back_and_forth = false; + while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { + no_auto_back_and_forth = true; + if (--argc < 3) { + return cmd_results_new(CMD_INVALID, "move", expected_syntax); + } + ++argv; + } + while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) { + no_auto_back_and_forth = true; + if (--argc < 3) { + return cmd_results_new(CMD_INVALID, "move", expected_syntax); + } + argv++; + } + + while (strcasecmp(argv[1], "to") == 0) { + if (--argc < 3) { + return cmd_results_new(CMD_INVALID, "move", expected_syntax); + } + argv++; + } + + struct sway_container *old_parent = current->parent; + struct sway_container *old_ws = container_parent(current, C_WORKSPACE); + struct sway_container *destination = NULL; + + // determine destination + if (strcasecmp(argv[1], "workspace") == 0) { + // move container to workspace x + struct sway_container *ws = NULL; + char *ws_name = NULL; + if (strcasecmp(argv[2], "next") == 0 || + strcasecmp(argv[2], "prev") == 0 || + strcasecmp(argv[2], "next_on_output") == 0 || + strcasecmp(argv[2], "prev_on_output") == 0 || + strcasecmp(argv[2], "current") == 0) { + ws = workspace_by_name(argv[2]); + } else if (strcasecmp(argv[2], "back_and_forth") == 0) { + if (!(ws = workspace_by_name(argv[2]))) { + if (prev_workspace_name) { + ws_name = strdup(prev_workspace_name); + } else { + return cmd_results_new(CMD_FAILURE, "move", + "No workspace was previously active."); + } + } + } else { + if (strcasecmp(argv[2], "number") == 0) { + // move "container to workspace number x" + if (argc < 4) { + return cmd_results_new(CMD_INVALID, "move", + expected_syntax); + } + ws_name = strdup(argv[3]); + ws = workspace_by_number(ws_name); + } else { + ws_name = join_args(argv + 2, argc - 2); ws = workspace_by_name(ws_name); } - } + if (!no_auto_back_and_forth && config->auto_back_and_forth && + prev_workspace_name) { + // auto back and forth move + if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) { + // if target workspace is the current one + free(ws_name); + ws_name = strdup(prev_workspace_name); + ws = workspace_by_name(ws_name); + } + } + } if (!ws) { + // We have to create the workspace, but if the container is + // sticky and the workspace is going to be created on the same + // output, we'll bail out first. + if (container_is_floating(current) && current->is_sticky) { + struct sway_container *old_output = + container_parent(current, C_OUTPUT); + struct sway_container *new_output = + workspace_get_initial_output(ws_name); + if (old_output == new_output) { + free(ws_name); + return cmd_results_new(CMD_FAILURE, "move", + "Can't move sticky container to another workspace " + "on the same output"); + } + } ws = workspace_create(NULL, ws_name); } free(ws_name); - struct sway_container *old_parent = current->parent; - struct sway_container *old_ws = container_parent(current, C_WORKSPACE); - struct sway_container *destination = seat_get_focus_inactive( - config->handler_context.seat, ws); - container_move_to(current, destination); - struct sway_container *focus = seat_get_focus_inactive( - config->handler_context.seat, old_parent); - seat_set_focus_warp(config->handler_context.seat, focus, true, false); - container_reap_empty(old_parent); - container_reap_empty(destination->parent); - - // TODO: Ideally we would arrange the surviving parent after reaping, - // but container_reap_empty does not return it, so we arrange the - // workspace instead. - arrange_windows(old_ws); - arrange_windows(destination->parent); - - return cmd_results_new(CMD_SUCCESS, NULL, NULL); - } else if (strcasecmp(argv[1], "to") == 0 - && strcasecmp(argv[2], "output") == 0) { - if (current->type == C_WORKSPACE) { - // TODO: Wrap children in a container and move that - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); - } else if (current->type != C_CONTAINER - && current->type != C_VIEW) { - return cmd_results_new(CMD_FAILURE, "move", - "Can only move containers and views."); - } + destination = seat_get_focus_inactive(config->handler_context.seat, ws); + } else if (strcasecmp(argv[1], "output") == 0) { struct sway_container *source = container_parent(current, C_OUTPUT); - struct sway_container *destination = output_in_direction(argv[3], + struct sway_container *dest_output = output_in_direction(argv[2], source->sway_output->wlr_output, current->x, current->y); - if (!destination) { + if (!dest_output) { return cmd_results_new(CMD_FAILURE, "move workspace", - "Can't find output with name/direction '%s'", argv[3]); + "Can't find output with name/direction '%s'", argv[2]); } - struct sway_container *focus = seat_get_focus_inactive( - config->handler_context.seat, destination); - if (!focus) { + destination = seat_get_focus_inactive( + config->handler_context.seat, dest_output); + if (!destination) { // We've never been to this output before - focus = destination->children->items[0]; + destination = dest_output->children->items[0]; } - 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_warp(config->handler_context.seat, old_parent, true, false); - container_reap_empty(old_parent); - container_reap_empty(focus->parent); - - // TODO: Ideally we would arrange the surviving parent after reaping, - // but container_reap_empty does not return it, so we arrange the - // workspace instead. - arrange_windows(old_ws); - arrange_windows(focus->parent); - - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } else if (strcasecmp(argv[1], "mark") == 0) { + struct sway_view *dest_view = view_find_mark(argv[2]); + if (dest_view == NULL) { + return cmd_results_new(CMD_FAILURE, "move", + "Mark '%s' not found", argv[2]); + } + destination = dest_view->swayc; + } else { + return cmd_results_new(CMD_INVALID, "move", expected_syntax); } - return cmd_results_new(CMD_INVALID, "move", expected_syntax); + + if (container_is_floating(current) && current->is_sticky) { + struct sway_container *old_output = container_parent(current, C_OUTPUT); + struct sway_container *new_output = destination->type == C_OUTPUT ? + destination : container_parent(destination, C_OUTPUT); + if (old_output == new_output) { + return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " + "container to another workspace on the same output"); + } + } + + // move container, arrange windows and return focus + container_move_to(current, destination); + struct sway_container *focus = + seat_get_focus_inactive(config->handler_context.seat, old_parent); + seat_set_focus_warp(config->handler_context.seat, focus, true, false); + container_reap_empty(old_parent); + container_reap_empty(destination->parent); + + // TODO: Ideally we would arrange the surviving parent after reaping, + // but container_reap_empty does not return it, so we arrange the + // workspace instead. + arrange_windows(old_ws); + arrange_windows(destination->parent); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +static void workspace_move_to_output(struct sway_container *workspace, + struct sway_container *output) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return; + } + if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { + return; + } + if (workspace->parent == output) { + return; + } + struct sway_container *old_output = container_remove_child(workspace); + struct sway_seat *seat = input_manager_get_default_seat(input_manager); + struct sway_container *new_output_focus = + seat_get_focus_inactive(seat, output); + + container_add_child(output, workspace); + wl_signal_emit(&workspace->events.reparent, old_output); + + // If moving the last workspace from the old output, create a new workspace + // on the old output + if (old_output->children->length == 0) { + char *ws_name = workspace_next_name(old_output->name); + struct sway_container *ws = workspace_create(old_output, ws_name); + free(ws_name); + seat_set_focus(seat, ws); + } + + // Try to remove an empty workspace from the destination output. + container_reap_empty_recursive(new_output_focus); + + container_sort_workspaces(output); + seat_set_focus(seat, output); + workspace_output_raise_priority(workspace, old_output, output); + ipc_event_workspace(NULL, workspace, "move"); + + container_notify_subtree_changed(old_output); + container_notify_subtree_changed(output); } static struct cmd_results *cmd_move_workspace(struct sway_container *current, int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { + if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { return error; - } else if (strcasecmp(argv[1], "to") != 0 - || strcasecmp(argv[2], "output") != 0) { + } + + while (strcasecmp(argv[1], "to") == 0) { + if (--argc < 3) { + return cmd_results_new(CMD_INVALID, "move", expected_syntax); + } + ++argv; + } + + if (strcasecmp(argv[1], "output") != 0) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } + struct sway_container *source = container_parent(current, C_OUTPUT); int center_x = current->width / 2 + current->x, center_y = current->height / 2 + current->y; - struct sway_container *destination = output_in_direction(argv[3], + struct sway_container *destination = output_in_direction(argv[2], source->sway_output->wlr_output, center_x, center_y); if (!destination) { return cmd_results_new(CMD_FAILURE, "move workspace", - "Can't find output with name/direction '%s'", argv[3]); + "Can't find output with name/direction '%s'", argv[2]); } if (current->type != C_WORKSPACE) { current = container_parent(current, C_WORKSPACE); } - container_move_to(current, destination); + workspace_move_to_output(current, destination); arrange_windows(source); arrange_windows(destination); @@ -242,9 +353,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static const char* expected_position_syntax = - "Expected 'move [absolute] position ' or " - "'move [absolute] position mouse'"; +static const char *expected_position_syntax = + "Expected 'move [absolute] position [px] [px]' or " + "'move [absolute] position center|mouse'"; static struct cmd_results *move_to_position(struct sway_container *container, int argc, char **argv) { @@ -279,10 +390,18 @@ static struct cmd_results *move_to_position(struct sway_container *container, double ly = seat->cursor->cursor->y - container->height / 2; container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } else if (strcmp(argv[0], "center") == 0) { + struct sway_container *ws = container_parent(container, C_WORKSPACE); + double lx = ws->x + (ws->width - container->width) / 2; + double ly = ws->y + (ws->height - container->height) / 2; + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } - if (argc != 2) { + + if (argc < 2) { return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); } + double lx, ly; char *inv; lx = (double)strtol(argv[0], &inv, 10); @@ -290,11 +409,22 @@ static struct cmd_results *move_to_position(struct sway_container *container, return cmd_results_new(CMD_FAILURE, "move", "Invalid position specified"); } + if (strcmp(argv[1], "px") == 0) { + --argc; + ++argv; + } + + if (argc > 3) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + ly = (double)strtol(argv[1], &inv, 10); - if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + if ((*inv != '\0' && strcasecmp(inv, "px") != 0) || + (argc == 3 && strcmp(argv[2], "px") != 0)) { return cmd_results_new(CMD_FAILURE, "move", "Invalid position specified"); } + container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -342,8 +472,11 @@ struct cmd_results *cmd_move(int argc, char **argv) { return move_in_direction(current, MOVE_UP, argc, argv); } else if (strcasecmp(argv[0], "down") == 0) { return move_in_direction(current, MOVE_DOWN, argc, argv); - } else if (strcasecmp(argv[0], "container") == 0 - || strcasecmp(argv[0], "window") == 0) { + } else if ((strcasecmp(argv[0], "container") == 0 + || strcasecmp(argv[0], "window") == 0) || + (strcasecmp(argv[0], "--no-auto-back-and-forth") && + (strcasecmp(argv[0], "container") == 0 + || strcasecmp(argv[0], "window") == 0))) { return cmd_move_container(current, argc, argv); } else if (strcasecmp(argv[0], "workspace") == 0) { return cmd_move_workspace(current, argc, argv); diff --git a/sway/commands/rename.c b/sway/commands/rename.c index a380ff9c..c6952bbb 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -61,6 +61,16 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } char *new_name = join_args(argv + argn, argc - argn); + if (strcasecmp(new_name, "next") == 0 || + strcasecmp(new_name, "prev") == 0 || + strcasecmp(new_name, "next_on_output") == 0 || + strcasecmp(new_name, "prev_on_output") == 0 || + strcasecmp(new_name, "back_and_forth") == 0 || + strcasecmp(new_name, "current") == 0) { + free(new_name); + return cmd_results_new(CMD_INVALID, "rename", + "Cannot use special workspace name '%s'", argv[argn]); + } struct sway_container *tmp_workspace = workspace_by_name(new_name); if (tmp_workspace) { free(new_name); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index e8b37182..f5558bb4 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -17,17 +17,6 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { int output_location = -1; - struct sway_container *current_container = config->handler_context.current_container; - struct sway_container *old_workspace = NULL, *old_output = NULL; - if (current_container) { - if (current_container->type == C_WORKSPACE) { - old_workspace = current_container; - } else { - old_workspace = container_parent(current_container, C_WORKSPACE); - } - old_output = container_parent(current_container, C_OUTPUT); - } - for (int i = 0; i < argc; ++i) { if (strcasecmp(argv[i], "output") == 0) { output_location = i; @@ -57,29 +46,36 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { if (config->reading || !config->active) { return cmd_results_new(CMD_DEFER, "workspace", NULL); } + + bool no_auto_back_and_forth = false; + while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { + no_auto_back_and_forth = true; + if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { + return error; + } + ++argv; + } + + struct sway_container *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { + if (argc < 2) { + cmd_results_new(CMD_INVALID, "workspace", + "Expected workspace number"); + } if (!(ws = workspace_by_number(argv[1]))) { char *name = join_args(argv + 1, argc - 1); ws = workspace_create(NULL, name); free(name); } - } else if (strcasecmp(argv[0], "next") == 0) { - ws = workspace_next(old_workspace); - } else if (strcasecmp(argv[0], "prev") == 0) { - ws = workspace_prev(old_workspace); - } else if (strcasecmp(argv[0], "next_on_output") == 0) { - ws = workspace_output_next(old_output); - } else if (strcasecmp(argv[0], "prev_on_output") == 0) { - ws = workspace_output_prev(old_output); + } else if (strcasecmp(argv[0], "next") == 0 || + strcasecmp(argv[0], "prev") == 0 || + strcasecmp(argv[0], "next_on_output") == 0 || + strcasecmp(argv[0], "prev_on_output") == 0 || + strcasecmp(argv[0], "current") == 0) { + ws = workspace_by_name(argv[0]); } else if (strcasecmp(argv[0], "back_and_forth") == 0) { - // if auto_back_and_forth is enabled, workspace_switch will swap - // the workspaces. If we created prev_workspace here, workspace_switch - // would put us back on original workspace. - if (config->auto_back_and_forth) { - ws = old_workspace; - } else if (prev_workspace_name - && !(ws = workspace_by_name(prev_workspace_name))) { + if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) { ws = workspace_create(NULL, prev_workspace_name); } } else { @@ -89,7 +85,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } free(name); } - workspace_switch(ws); + workspace_switch(ws, no_auto_back_and_forth); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/criteria.c b/sway/criteria.c index 39d300ea..9077aa9b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -361,8 +361,17 @@ static char *get_focused_prop(enum criteria_token token) { } } break; - case T_CON_ID: // These do not support __focused__ - case T_CON_MARK: + case T_CON_ID: + if (view->swayc == NULL) { + return NULL; + } + size_t id = view->swayc->id; + size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; + char *id_str = malloc(id_size); + snprintf(id_str, id_size, "%zu", id); + value = id_str; + break; + case T_CON_MARK: // These do not support __focused__ case T_FLOATING: #ifdef HAVE_XWAYLAND case T_ID: @@ -425,7 +434,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_CON_ID: criteria->con_id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { - error = strdup("The value for 'con_id' should be numeric"); + error = strdup("The value for 'con_id' should be '__focused__' or numeric"); } break; case T_CON_MARK: @@ -452,13 +461,18 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { criteria->tiling = true; break; case T_URGENT: - if (strcmp(effective_value, "latest") == 0) { + if (strcmp(effective_value, "latest") == 0 || + strcmp(effective_value, "newest") == 0 || + strcmp(effective_value, "last") == 0 || + strcmp(effective_value, "recent") == 0) { criteria->urgent = 'l'; - } else if (strcmp(effective_value, "oldest") == 0) { + } else if (strcmp(effective_value, "oldest") == 0 || + strcmp(effective_value, "first") == 0) { criteria->urgent = 'o'; } else { error = - strdup("The value for 'urgent' must be 'latest' or 'oldest'"); + strdup("The value for 'urgent' must be 'first', 'last', " + "'latest', 'newest', 'oldest' or 'recent'"); } break; case T_WORKSPACE: diff --git a/sway/decoration.c b/sway/decoration.c new file mode 100644 index 00000000..0e3e67ac --- /dev/null +++ b/sway/decoration.c @@ -0,0 +1,71 @@ +#include +#include "sway/decoration.h" +#include "sway/server.h" +#include "sway/tree/view.h" +#include "log.h" + +static void server_decoration_handle_destroy(struct wl_listener *listener, + void *data) { + struct sway_server_decoration *deco = + wl_container_of(listener, deco, destroy); + wl_list_remove(&deco->destroy.link); + wl_list_remove(&deco->mode.link); + wl_list_remove(&deco->link); + free(deco); +} + +static void server_decoration_handle_mode(struct wl_listener *listener, + void *data) { + struct sway_server_decoration *deco = + wl_container_of(listener, deco, mode); + struct sway_view *view = + view_from_wlr_surface(deco->wlr_server_decoration->surface); + if (view == NULL || view->surface != deco->wlr_server_decoration->surface) { + return; + } + + switch (view->type) { + case SWAY_VIEW_XDG_SHELL_V6:; + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + (struct sway_xdg_shell_v6_view *)view; + xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode; + break; + case SWAY_VIEW_XDG_SHELL:; + struct sway_xdg_shell_view *xdg_shell_view = + (struct sway_xdg_shell_view *)view; + xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode; + break; + default: + break; + } +} + +void handle_server_decoration(struct wl_listener *listener, void *data) { + struct wlr_server_decoration *wlr_deco = data; + + struct sway_server_decoration *deco = calloc(1, sizeof(*deco)); + if (deco == NULL) { + return; + } + + deco->wlr_server_decoration = wlr_deco; + + wl_signal_add(&wlr_deco->events.destroy, &deco->destroy); + deco->destroy.notify = server_decoration_handle_destroy; + + wl_signal_add(&wlr_deco->events.mode, &deco->mode); + deco->mode.notify = server_decoration_handle_mode; + + wl_list_insert(&server.decorations, &deco->link); +} + +struct sway_server_decoration *decoration_from_surface( + struct wlr_surface *surface) { + struct sway_server_decoration *deco; + wl_list_for_each(deco, &server.decorations, link) { + if (deco->wlr_server_decoration->surface == surface) { + return deco; + } + } + return NULL; +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index b364663d..3b73f99c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -6,6 +6,7 @@ #include #include #include "log.h" +#include "sway/decoration.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/server.h" @@ -170,6 +171,15 @@ static bool wants_floating(struct sway_view *view) { || toplevel->parent; } +static bool has_client_side_decorations(struct sway_view *view) { + struct sway_xdg_shell_view *xdg_shell_view = + xdg_shell_view_from_view(view); + if (xdg_shell_view == NULL) { + return true; + } + return xdg_shell_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER; +} + static void for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (xdg_shell_view_from_view(view) == NULL) { @@ -226,6 +236,7 @@ static const struct sway_view_impl view_impl = { .set_tiled = set_tiled, .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, + .has_client_side_decorations = has_client_side_decorations, .for_each_surface = for_each_surface, .for_each_popup = for_each_popup, .close = _close, @@ -357,6 +368,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_height = view->wlr_xdg_surface->surface->current.height; } + struct sway_server_decoration *deco = + decoration_from_surface(xdg_surface->surface); + if (deco != NULL) { + xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode; + } else { + xdg_shell_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; + } + view_map(view, view->wlr_xdg_surface->surface); if (xdg_surface->toplevel->client_pending.fullscreen) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index ffea03ad..a947fb35 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -4,14 +4,15 @@ #include #include #include +#include "log.h" +#include "sway/decoration.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" -#include "sway/input/seat.h" -#include "sway/input/input-manager.h" -#include "log.h" static const struct sway_view_child_impl popup_impl; @@ -166,6 +167,15 @@ static bool wants_floating(struct sway_view *view) { || toplevel->parent; } +static bool has_client_side_decorations(struct sway_view *view) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + xdg_shell_v6_view_from_view(view); + if (xdg_shell_v6_view == NULL) { + return true; + } + return xdg_shell_v6_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER; +} + static void for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (xdg_shell_v6_view_from_view(view) == NULL) { @@ -223,6 +233,7 @@ static const struct sway_view_impl view_impl = { .set_tiled = set_tiled, .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, + .has_client_side_decorations = has_client_side_decorations, .for_each_surface = for_each_surface, .for_each_popup = for_each_popup, .close = _close, @@ -353,6 +364,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; } + struct sway_server_decoration *deco = + decoration_from_surface(xdg_surface->surface); + if (deco != NULL) { + xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode; + } else { + xdg_shell_v6_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; + } + view_map(view, view->wlr_xdg_surface_v6->surface); if (xdg_surface->toplevel->client_pending.fullscreen) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 80b4f9dc..3f417e96 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -429,6 +429,8 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct sway_container *cont) { struct sway_seat *seat = cursor->seat; + seat_set_focus(seat, cont); + // Deny moving or resizing a fullscreen container if (container_is_fullscreen_or_child(cont)) { seat_pointer_notify_button(seat, time_msec, button, state); @@ -469,8 +471,6 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, return; } - // Send event to surface - seat_set_focus(seat, cont); seat_pointer_notify_button(seat, time_msec, button, state); } diff --git a/sway/input/seat.c b/sway/input/seat.c index dd4d5c3b..6dd7cf7d 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -717,12 +717,8 @@ void seat_set_focus_warp(struct sway_seat *seat, // If we've focused a floating container, bring it to the front. // We do this by putting it at the end of the floating list. - // This must happen for both the pending and current children lists. if (container && container_is_floating(container)) { list_move_to_end(container->parent->children, container); - if (container_has_ancestor(container, container->current.parent)) { - list_move_to_end(container->parent->current.children, container); - } } // clean up unfocused empty workspace on new output diff --git a/sway/meson.build b/sway/meson.build index c18fb6e2..2a457270 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -1,13 +1,14 @@ sway_sources = files( - 'main.c', - 'server.c', 'commands.c', 'config.c', 'criteria.c', 'debug-tree.c', + 'decoration.c', 'ipc-json.c', 'ipc-server.c', + 'main.c', 'security.c', + 'server.c', 'swaynag.c', 'desktop/desktop.c', diff --git a/sway/server.c b/sway/server.c index e8755360..e8dc63be 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,7 +19,6 @@ #include #include #include -// TODO WLR: make Xwayland optional #include "list.h" #include "sway/config.h" #include "sway/desktop/idle_inhibit_v1.h" @@ -85,7 +84,6 @@ bool server_init(struct sway_server *server) { &server->xdg_shell_surface); 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); @@ -109,11 +107,15 @@ bool server_init(struct sway_server *server) { } #endif - // TODO: Integration with sway borders - struct wlr_server_decoration_manager *deco_manager = + server->server_decoration_manager = wlr_server_decoration_manager_create(server->wl_display); wlr_server_decoration_manager_set_default_mode( - deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + server->server_decoration_manager, + WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + wl_signal_add(&server->server_decoration_manager->events.new_decoration, + &server->server_decoration); + server->server_decoration.notify = handle_server_decoration; + wl_list_init(&server->decorations); wlr_linux_dmabuf_v1_create(server->wl_display, renderer); wlr_export_dmabuf_manager_v1_create(server->wl_display); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 82df38e3..73a01152 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -84,6 +84,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *floating* enable|disable|toggle Make focused view floating, non-floating, or the opposite of what it is now. + *focus* + Moves focus to the container that matches the specified criteria. + *focus* up|right|down|left Moves focus to the next container in the specified direction. @@ -111,33 +114,53 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *fullscreen* Toggles fullscreen for the focused view. -*layout* splith|splitv|stacking|tabbed +*layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. -*layout* toggle split - Switches the focused container between the splitv and splith layouts. +*layout* toggle [split|all] + Cycles the layout mode of the focused container though a preset list of + layouts. If no argument is given, then it cycles through stacking, tabbed + and the last split layout. If "split" is given, then it cycles through + splith and splitv. If "all" is given, then it cycles through every layout. -*move* left|right|up|down [] +*layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]... + Cycles the layout mode of the focused container through a list of layouts. + +*move* left|right|up|down [ px] Moves the focused container in the direction specified. If the container, the optional _px_ argument specifies how many pixels to move the container. If unspecified, the default is 10 pixels. Pixels are ignored when moving tiled containers. -*move* container|window to workspace - Moves the focused container to the specified workspace. +*move* [absolute] position [px] [px] + Moves the focused container to the specified position. -*move* container|window to workspace prev|next - Moves the focused container to the previous or next workspace on this - output, or if no workspaces remain, the previous or next output. +*move* [absolute] position center|mouse + Moves the focused container to be centered on the workspace or mouse. -*move* container|window to workspace prev\_on\_output|next\_on\_output +*move* container|window [to] mark + Moves the focused container to the specified mark. + +*move* [--no-auto-back-and-forth] container|window [to] workspace [number] + Moves the focused container to the specified workspace. The string "number" + is optional and is used to match a workspace with the same number, even if + it has a different name. + +*move* container|window [to] workspace prev|next|current + Moves the focused container to the previous, next or current workspace on + this output, or if no workspaces remain, the previous or next output. + +*move* container|window [to] workspace prev\_on\_output|next\_on\_output Moves the focused container to the previous or next workspace on this output, wrapping around if already at the first or last workspace. -*move* container|window|workspace to output +*move* container|window [to] workspace back_and_forth + Moves the focused container to previously focused workspace. + +*move* container|window|workspace [to] output Moves the focused container or workspace to the specified output. -*move* container|window|workspace to output up|right|down|left +*move* container|window|workspace [to] output up|right|down|left Moves the focused container or workspace to next output in the specified direction. @@ -511,7 +534,7 @@ config after the others, or it will be matched instead of the others. state. Using _allow_ or _deny_ controls the window's ability to set itself as urgent. By default, windows are allowed to set their own urgency. -*workspace* [number] +*workspace* [--no-auto-back-and-forth] [number] Switches to the specified workspace. The string "number" is optional and is used to sort workspaces. @@ -522,6 +545,9 @@ config after the others, or it will be matched instead of the others. *workspace* prev\_on\_output|next\_on\_output Switches to the next workspace on the current output. +*workspace* back_and_forth + Switches to the previously focused workspace. + *workspace* output Specifies that workspace _name_ should be shown on the specified _output_. @@ -582,7 +608,9 @@ The following attributes may be matched with: the currently focused window. *con\_id* - Compare against the internal container ID, which you can find via IPC. + Compare against the internal container ID, which you can find via IPC. If + value is \_\_focused\_\_, then the id must be the same as that of the + currently focused window. *con\_mark* Compare against the window marks. Can be a regular expression. @@ -612,7 +640,8 @@ The following attributes may be matched with: currently focused window. *urgent* - Compares the urgent state of the window. Can be "latest" or "oldest". + Compares the urgent state of the window. Can be "first", "last", "latest", + "newest", "oldest" or "recent". *window\_role* Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular diff --git a/sway/tree/container.c b/sway/tree/container.c index 6da5ac3c..aecb2ac6 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -533,11 +533,10 @@ struct sway_container *container_parent(struct sway_container *container, return container; } -static struct sway_container *container_at_view(struct sway_container *swayc, - double lx, double ly, +static void surface_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")) { - return NULL; + return; } struct sway_view *sview = swayc->sway_view; double view_sx = lx - sview->x; @@ -567,9 +566,7 @@ static struct sway_container *container_at_view(struct sway_container *swayc, *sx = _sx; *sy = _sy; *surface = _surface; - return swayc; } - return NULL; } /** @@ -682,7 +679,8 @@ 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); + surface_at_view(con, lx, ly, surface, sx, sy); + return con; } if (!con->children->length) { return NULL; @@ -745,7 +743,7 @@ struct sway_container *container_at(struct sway_container *workspace, 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); + surface_at_view(focus, lx, ly, surface, sx, sy); if (*surface && surface_is_popup(*surface)) { return focus; } @@ -1163,19 +1161,16 @@ void container_floating_translate(struct sway_container *con, double x_amount, double y_amount) { con->x += x_amount; con->y += y_amount; - con->current.swayc_x += x_amount; - con->current.swayc_y += y_amount; if (con->type == C_VIEW) { con->sway_view->x += x_amount; con->sway_view->y += y_amount; - con->current.view_x += x_amount; - con->current.view_y += y_amount; } else { for (int i = 0; i < con->children->length; ++i) { struct sway_container *child = con->children->items[i]; container_floating_translate(child, x_amount, y_amount); } } + container_set_dirty(con); } /** @@ -1185,7 +1180,7 @@ void container_floating_translate(struct sway_container *con, * one, otherwise we'll choose whichever output is closest to the container's * center. */ -static struct sway_container *container_floating_find_output( +struct sway_container *container_floating_find_output( struct sway_container *con) { double center_x = con->x + con->width / 2; double center_y = con->y + con->height / 2; @@ -1219,9 +1214,7 @@ void container_floating_move_to(struct sway_container *con, "Expected a floating container")) { return; } - desktop_damage_whole_container(con); container_floating_translate(con, lx - con->x, ly - con->y); - desktop_damage_whole_container(con); struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); struct sway_container *new_output = container_floating_find_output(con); if (!sway_assert(new_output, "Unable to find any output")) { @@ -1239,6 +1232,17 @@ void container_floating_move_to(struct sway_container *con, } } +void container_floating_move_to_center(struct sway_container *con) { + if (!sway_assert(container_is_floating(con), + "Expected a floating container")) { + return; + } + struct sway_container *ws = container_parent(con, C_WORKSPACE); + double new_lx = ws->x + (ws->width - con->width) / 2; + double new_ly = ws->y + (ws->height - con->height) / 2; + container_floating_translate(con, new_lx - con->x, new_ly - con->y); +} + void container_set_dirty(struct sway_container *container) { if (container->dirty) { return; @@ -1318,6 +1322,11 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { container->y = container->saved_y; container->width = container->saved_width; container->height = container->saved_height; + struct sway_container *output = + container_floating_find_output(container); + if (!container_has_ancestor(container, output)) { + container_floating_move_to_center(container); + } } else { container->width = container->saved_width; container->height = container->saved_height; diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 07de9664..38e14d00 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -142,49 +142,55 @@ struct sway_container *container_remove_child(struct sway_container *child) { void container_move_to(struct sway_container *container, struct sway_container *destination) { + if (!sway_assert(container->type == C_CONTAINER || + container->type == C_VIEW, "Expected a container or view")) { + return; + } if (container == destination || container_has_ancestor(container, destination)) { return; } + struct sway_container *old_parent = NULL; + struct sway_container *new_parent = NULL; if (container_is_floating(container)) { - // TODO - return; - } - struct sway_container *old_parent = container_remove_child(container); - container->width = container->height = 0; - container->saved_width = container->saved_height = 0; - - struct sway_container *new_parent, *new_parent_focus; - struct sway_seat *seat = input_manager_get_default_seat(input_manager); - - // Get the focus of the destination before we change it. - new_parent_focus = seat_get_focus_inactive(seat, destination); - if (destination->type == C_VIEW) { - new_parent = container_add_sibling(destination, container); + // Resolve destination into a workspace + struct sway_container *new_ws = NULL; + if (destination->type == C_OUTPUT) { + new_ws = output_get_active_workspace(destination->sway_output); + } else if (destination->type == C_WORKSPACE) { + new_ws = destination; + } else { + new_ws = container_parent(destination, C_WORKSPACE); + } + if (!new_ws) { + // This can happen if the user has run "move container to mark foo", + // where mark foo is on a hidden scratchpad container. + return; + } + struct sway_container *old_output = + container_parent(container, C_OUTPUT); + old_parent = container_remove_child(container); + container_add_child(new_ws->sway_workspace->floating, container); + // If changing output, center it within the workspace + if (old_output != new_ws->parent && !container->is_fullscreen) { + container_floating_move_to_center(container); + } } else { - new_parent = destination; - container_add_child(destination, container); + old_parent = container_remove_child(container); + container->width = container->height = 0; + container->saved_width = container->saved_height = 0; + + if (destination->type == C_VIEW) { + new_parent = container_add_sibling(destination, container); + } else { + new_parent = destination; + container_add_child(destination, container); + } } + wl_signal_emit(&container->events.reparent, old_parent); - if (container->type == C_WORKSPACE) { - // If moving a workspace to a new output, maybe create a new workspace - // on the previous output - if (old_parent->children->length == 0) { - char *ws_name = workspace_next_name(old_parent->name); - struct sway_container *ws = workspace_create(old_parent, ws_name); - free(ws_name); - seat_set_focus(seat, ws); - } - - // Try to remove an empty workspace from the destination output. - container_reap_empty_recursive(new_parent_focus); - - container_sort_workspaces(new_parent); - seat_set_focus(seat, new_parent); - workspace_output_raise_priority(container, old_parent, new_parent); - ipc_event_workspace(NULL, container, "move"); - } else if (container->type == C_VIEW) { + if (container->type == C_VIEW) { ipc_event_window(container, "move"); } container_notify_subtree_changed(old_parent); @@ -859,7 +865,7 @@ struct sway_container *container_split(struct sway_container *child, } if (child->type == C_WORKSPACE && child->children->length == 0) { // Special case: this just behaves like splitt - child->prev_layout = child->layout; + child->prev_split_layout = child->layout; child->layout = layout; return child; } @@ -870,7 +876,7 @@ struct sway_container *container_split(struct sway_container *child, remove_gaps(child); - cont->prev_layout = L_NONE; + cont->prev_split_layout = L_NONE; cont->width = child->width; cont->height = child->height; cont->x = child->x; diff --git a/sway/tree/view.c b/sway/tree/view.c index 06cef900..950494d8 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -456,7 +457,13 @@ static struct sway_container *select_workspace(struct sway_view *view) { if (criteria->type == CT_ASSIGN_WORKSPACE) { ws = workspace_by_name(criteria->target); if (!ws) { - ws = workspace_create(NULL, criteria->target); + if (strcasecmp(criteria->target, "back_and_forth") == 0) { + if (prev_workspace_name) { + ws = workspace_create(NULL, prev_workspace_name); + } + } else { + ws = workspace_create(NULL, criteria->target); + } } break; } else { @@ -891,6 +898,15 @@ static bool find_by_mark_iterator(struct sway_container *con, return con->type == C_VIEW && view_has_mark(con->sway_view, mark); } +struct sway_view *view_find_mark(char *mark) { + struct sway_container *container = container_find(&root_container, + find_by_mark_iterator, mark); + if (!container) { + return NULL; + } + return container->sway_view; +} + bool view_find_and_unmark(char *mark) { struct sway_container *container = container_find(&root_container, find_by_mark_iterator, mark); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index cc225e79..b8bec044 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -18,7 +18,7 @@ #include "log.h" #include "util.h" -static struct sway_container *get_workspace_initial_output(const char *name) { +struct sway_container *workspace_get_initial_output(const char *name) { struct sway_container *parent; // Search for workspace<->output pair int e = config->workspace_outputs->length; @@ -48,7 +48,7 @@ static struct sway_container *get_workspace_initial_output(const char *name) { struct sway_container *workspace_create(struct sway_container *output, const char *name) { if (output == NULL) { - output = get_workspace_initial_output(name); + output = workspace_get_initial_output(name); } wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); @@ -59,7 +59,7 @@ struct sway_container *workspace_create(struct sway_container *output, workspace->width = output->width; workspace->height = output->height; workspace->name = !name ? NULL : strdup(name); - workspace->prev_layout = L_NONE; + workspace->prev_split_layout = L_NONE; workspace->layout = container_get_default_layout(output); struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); @@ -250,6 +250,7 @@ struct sway_container *workspace_by_name(const char *name) { current_workspace = container_parent(focus, C_WORKSPACE); current_output = container_parent(focus, C_OUTPUT); } + if (strcmp(name, "prev") == 0) { return workspace_prev(current_workspace); } else if (strcmp(name, "prev_on_output") == 0) { @@ -260,6 +261,9 @@ struct sway_container *workspace_by_name(const char *name) { return workspace_output_next(current_output); } else if (strcmp(name, "current") == 0) { return current_workspace; + } else if (strcasecmp(name, "back_and_forth") == 0) { + return prev_workspace_name ? container_find(&root_container, + _workspace_by_name, (void *)prev_workspace_name) : NULL; } else { return container_find(&root_container, _workspace_by_name, (void *)name); @@ -364,7 +368,8 @@ struct sway_container *workspace_prev(struct sway_container *current) { return workspace_prev_next_impl(current, false); } -bool workspace_switch(struct sway_container *workspace) { +bool workspace_switch(struct sway_container *workspace, + bool no_auto_back_and_forth) { if (!workspace) { return false; } @@ -379,7 +384,7 @@ bool workspace_switch(struct sway_container *workspace) { active_ws = container_parent(focus, C_WORKSPACE); } - if (config->auto_back_and_forth + if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) { struct sway_container *new_ws = workspace_by_name(prev_workspace_name); @@ -406,17 +411,20 @@ bool workspace_switch(struct sway_container *workspace) { struct sway_container *floating = next_output_prev_ws->sway_workspace->floating; bool has_sticky = false; - for (int i = 0; i < floating->children->length; ++i) { - struct sway_container *floater = floating->children->items[i]; - if (floater->is_sticky) { - has_sticky = true; - container_remove_child(floater); - container_add_child(workspace->sway_workspace->floating, floater); - if (floater == focus) { - seat_set_focus(seat, NULL); - seat_set_focus(seat, floater); + if (workspace != next_output_prev_ws) { + for (int i = 0; i < floating->children->length; ++i) { + struct sway_container *floater = floating->children->items[i]; + if (floater->is_sticky) { + has_sticky = true; + container_remove_child(floater); + container_add_child(workspace->sway_workspace->floating, + floater); + if (floater == focus) { + seat_set_focus(seat, NULL); + seat_set_focus(seat, floater); + } + --i; } - --i; } }