diff --git a/include/sway/output.h b/include/sway/output.h index ae2e50d36..eb3eb6b5f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -110,6 +110,9 @@ struct sway_output *output_by_name_or_id(const char *name_or_id); // this includes all the outputs, including disabled ones struct sway_output *all_output_by_name_or_id(const char *name_or_id); +struct sway_output *output_by_direction_or_name(const char *spec, + const struct sway_output *reference, double ref_lx, double ref_ly); + void output_sort_workspaces(struct sway_output *output); void output_enable(struct sway_output *output); diff --git a/sway/commands/focus.c b/sway/commands/focus.c index facd82de7..acffc56a5 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,6 +1,5 @@ #include #include -#include #include "log.h" #include "sway/commands.h" #include "sway/input/input-manager.h" @@ -12,7 +11,6 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "stringop.h" -#include "util.h" static bool get_direction_from_next_prev(struct sway_container *container, struct sway_seat *seat, const char *name, enum wlr_direction *out) { @@ -295,43 +293,31 @@ static struct cmd_results *focus_output(struct sway_seat *seat, int argc, char **argv) { if (!argc) { return cmd_results_new(CMD_INVALID, - "Expected 'focus output '."); + "Expected 'focus output '."); } char *identifier = join_args(argv, argc); - struct sway_output *output = output_by_name_or_id(identifier); + + struct sway_workspace *ws = seat_get_focused_workspace(seat); + struct sway_output *reference = ws ? ws->output : NULL; + double ref_lx = 0, ref_ly = 0; + if (reference) { + ref_lx = reference->lx + reference->width / 2; + ref_ly = reference->ly + reference->height / 2; + } + + struct sway_output *output = output_by_direction_or_name(identifier, + reference, ref_lx, ref_ly); if (!output) { - enum wlr_direction direction; - if (!parse_direction(identifier, &direction)) { - free(identifier); - return cmd_results_new(CMD_INVALID, - "There is no output with that name."); - } - struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (!ws) { - free(identifier); - return cmd_results_new(CMD_FAILURE, - "No focused workspace to base directions off of."); - } - output = output_get_in_direction(ws->output, direction); - - if (!output) { - int center_lx = ws->output->lx + ws->output->width / 2; - int center_ly = ws->output->ly + ws->output->height / 2; - struct wlr_output *target = wlr_output_layout_farthest_output( - root->output_layout, opposite_direction(direction), - ws->output->wlr_output, center_lx, center_ly); - if (target) { - output = output_from_wlr_output(target); - } - } + struct cmd_results *err = cmd_results_new(CMD_INVALID, + "No output matches '%s'.", identifier); + free(identifier); + return err; } - free(identifier); - if (output) { - seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node)); - seat_consider_warp_to_focus(seat); - } + + seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node)); + seat_consider_warp_to_focus(seat); return cmd_results_new(CMD_SUCCESS, NULL); } @@ -426,7 +412,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { if (!get_direction_from_next_prev(container, seat, argv[0], &direction)) { return cmd_results_new(CMD_INVALID, "Expected 'focus ' " - "or 'focus output '"); + "or 'focus output '"); } else if (argc == 2 && strcasecmp(argv[1], "sibling") == 0) { descend = false; } diff --git a/sway/commands/move.c b/sway/commands/move.c index 43fce0d6d..f22ec06a4 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -24,58 +24,9 @@ static const char expected_syntax[] = "Expected 'move <[px] px>' or " "'move [--no-auto-back-and-forth] [to] workspace ' or " - "'move [to] output ' or " + "'move [to] output ' or " "'move [to] mark '"; -static struct sway_output *output_in_direction(const char *direction_string, - struct sway_output *reference, int ref_lx, int ref_ly) { - if (strcasecmp(direction_string, "current") == 0) { - struct sway_workspace *active_ws = - seat_get_focused_workspace(config->handler_context.seat); - if (!active_ws) { - return NULL; - } - return active_ws->output; - } - - struct { - char *name; - enum wlr_direction direction; - } names[] = { - { "up", WLR_DIRECTION_UP }, - { "down", WLR_DIRECTION_DOWN }, - { "left", WLR_DIRECTION_LEFT }, - { "right", WLR_DIRECTION_RIGHT }, - }; - - enum wlr_direction direction = 0; - - for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { - if (strcasecmp(names[i].name, direction_string) == 0) { - direction = names[i].direction; - break; - } - } - - if (reference && direction) { - struct wlr_output *target = wlr_output_layout_adjacent_output( - root->output_layout, direction, reference->wlr_output, - ref_lx, ref_ly); - - if (!target) { - target = wlr_output_layout_farthest_output( - root->output_layout, opposite_direction(direction), - reference->wlr_output, ref_lx, ref_ly); - } - - if (target) { - return target->data; - } - } - - return output_by_name_or_id(direction_string); -} - static bool is_parallel(enum sway_container_layout layout, enum wlr_direction dir) { switch (layout) { @@ -516,7 +467,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); destination = dst ? &dst->node : &ws->node; } else if (strcasecmp(argv[0], "output") == 0) { - struct sway_output *new_output = output_in_direction(argv[1], + struct sway_output *new_output = output_by_direction_or_name(argv[1], old_output, container->pending.x, container->pending.y); if (!new_output) { return cmd_results_new(CMD_FAILURE, @@ -650,7 +601,7 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { struct sway_output *old_output = workspace->output; int center_x = workspace->width / 2 + workspace->x, center_y = workspace->height / 2 + workspace->y; - struct sway_output *new_output = output_in_direction(argv[0], + struct sway_output *new_output = output_by_direction_or_name(argv[0], old_output, center_x, center_y); if (!new_output) { return cmd_results_new(CMD_FAILURE, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 12dc9cc7a..b9dbf1295 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -7,29 +7,22 @@ #include #include #include -#include #include -#include #include #include #include #include #include #include -#include -#include -#include "config.h" #include "log.h" #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" -#include "sway/layers.h" #include "sway/output.h" #include "sway/scene_descriptor.h" #include "sway/server.h" -#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" @@ -72,6 +65,75 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) { return NULL; } +static struct sway_output *output_cycle(const struct sway_output *reference, + bool forward) { + if (root->outputs->length == 0) { + return NULL; + } + int ref_idx = -1; + if (reference) { + for (int i = 0; i < root->outputs->length; ++i) { + if (root->outputs->items[i] == reference) { + ref_idx = i; + break; + } + } + } + int next_idx; + if (ref_idx < 0) { + next_idx = forward ? 0 : root->outputs->length - 1; + } else { + next_idx = forward + ? (ref_idx + 1) % root->outputs->length + : (ref_idx - 1 + root->outputs->length) % root->outputs->length; + } + return root->outputs->items[next_idx]; +} + +struct sway_output *output_by_direction_or_name(const char *spec, + const struct sway_output *reference, double ref_lx, double ref_ly) { + if (strcasecmp(spec, "current") == 0) { + const struct sway_workspace *active_ws = + seat_get_focused_workspace(config->handler_context.seat); + return active_ws ? active_ws->output : NULL; + } + + if (strcasecmp(spec, "next") == 0) { + return output_cycle(reference, true); + } + if (strcasecmp(spec, "prev") == 0) { + return output_cycle(reference, false); + } + + enum wlr_direction direction = 0; + if (strcasecmp(spec, "up") == 0) { + direction = WLR_DIRECTION_UP; + } else if (strcasecmp(spec, "down") == 0) { + direction = WLR_DIRECTION_DOWN; + } else if (strcasecmp(spec, "left") == 0) { + direction = WLR_DIRECTION_LEFT; + } else if (strcasecmp(spec, "right") == 0) { + direction = WLR_DIRECTION_RIGHT; + } + + if (reference && direction) { + struct wlr_output *target = wlr_output_layout_adjacent_output( + root->output_layout, direction, reference->wlr_output, + ref_lx, ref_ly); + + if (!target) { + target = wlr_output_layout_farthest_output( + root->output_layout, opposite_direction(direction), + reference->wlr_output, ref_lx, ref_ly); + } + + if (target) { + return target->data; + } + } + + return output_by_name_or_id(spec); +} struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 952d243d2..e79bb7210 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -139,6 +139,13 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *focus* output up|right|down|left Moves focus to the next output in the specified direction. +*focus* output next|prev + Moves focus to the next or previous output, wrapping around. + +*focus* output current + Moves focus to the output containing the focused workspace. Mainly useful + with criteria to refocus the seat's current output. + *focus* output Moves focus to the named output. @@ -275,6 +282,10 @@ set|plus|minus|toggle Moves the focused container to next output in the specified direction. +*move* [container|window] [to] output next|prev + Moves the focused container to the next or previous output, + wrapping around. + *move* [container|window] [to] scratchpad Moves the focused container to the scratchpad. @@ -290,6 +301,14 @@ set|plus|minus|toggle *move* workspace to [output] up|right|down|left Moves the focused workspace to next output in the specified direction. +*move* workspace [to] output next|prev + Moves the focused workspace to the next or previous output, + wrapping around. + +*move* workspace to [output] next|prev + Moves the focused workspace to the next or previous output, + wrapping around. + *nop* A no operation command that can be used to override default behaviour. The optional comment argument is ignored, but logged for debugging purposes.