mirror of
https://github.com/swaywm/sway.git
synced 2026-06-13 14:33:19 -04:00
commands: accept 'next'/'prev' output names
`move container to output`, `move workspace to output` and `focus output` now accept 'next' and 'prev', which cycle through the enabled outputs with wrap-around. This matches i3's behaviour of expanding 'next' into the full output list so the following output relative to the current one is picked. The name/direction/current resolution used by the move commands was also duplicated inline in `focus_output`; both call sites now share a new `output_by_direction_or_name` helper in `sway/desktop/output.c`. Closes: #8799 i3 implementation in https://github.com/i3/i3/pull/4338 Note: - i3 accepts multiple output arguments and `next` works as a wildcard. I considered that out of scope for this PR. - i3 does not have `prev`. Accepting it in sway is natural and practically free complexity-wise so I added it.
This commit is contained in:
parent
1cbb8a440f
commit
9b81b6273f
5 changed files with 114 additions and 93 deletions
|
|
@ -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
|
// 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 *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_sort_workspaces(struct sway_output *output);
|
||||||
|
|
||||||
void output_enable(struct sway_output *output);
|
void output_enable(struct sway_output *output);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sway/commands.h"
|
#include "sway/commands.h"
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
|
|
@ -12,7 +11,6 @@
|
||||||
#include "sway/tree/view.h"
|
#include "sway/tree/view.h"
|
||||||
#include "sway/tree/workspace.h"
|
#include "sway/tree/workspace.h"
|
||||||
#include "stringop.h"
|
#include "stringop.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
static bool get_direction_from_next_prev(struct sway_container *container,
|
static bool get_direction_from_next_prev(struct sway_container *container,
|
||||||
struct sway_seat *seat, const char *name, enum wlr_direction *out) {
|
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) {
|
int argc, char **argv) {
|
||||||
if (!argc) {
|
if (!argc) {
|
||||||
return cmd_results_new(CMD_INVALID,
|
return cmd_results_new(CMD_INVALID,
|
||||||
"Expected 'focus output <direction|name>'.");
|
"Expected 'focus output <direction|name|next|prev|current>'.");
|
||||||
}
|
}
|
||||||
char *identifier = join_args(argv, argc);
|
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) {
|
if (!output) {
|
||||||
enum wlr_direction direction;
|
struct cmd_results *err = cmd_results_new(CMD_INVALID,
|
||||||
if (!parse_direction(identifier, &direction)) {
|
"No output matches '%s'.", identifier);
|
||||||
free(identifier);
|
free(identifier);
|
||||||
return cmd_results_new(CMD_INVALID,
|
return err;
|
||||||
"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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(identifier);
|
free(identifier);
|
||||||
if (output) {
|
|
||||||
seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));
|
seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));
|
||||||
seat_consider_warp_to_focus(seat);
|
seat_consider_warp_to_focus(seat);
|
||||||
}
|
|
||||||
|
|
||||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
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)) {
|
if (!get_direction_from_next_prev(container, seat, argv[0], &direction)) {
|
||||||
return cmd_results_new(CMD_INVALID,
|
return cmd_results_new(CMD_INVALID,
|
||||||
"Expected 'focus <direction|next|prev|parent|child|mode_toggle|floating|tiling>' "
|
"Expected 'focus <direction|next|prev|parent|child|mode_toggle|floating|tiling>' "
|
||||||
"or 'focus output <direction|name>'");
|
"or 'focus output <direction|name|next|prev|current>'");
|
||||||
} else if (argc == 2 && strcasecmp(argv[1], "sibling") == 0) {
|
} else if (argc == 2 && strcasecmp(argv[1], "sibling") == 0) {
|
||||||
descend = false;
|
descend = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,58 +24,9 @@
|
||||||
static const char expected_syntax[] =
|
static const char expected_syntax[] =
|
||||||
"Expected 'move <left|right|up|down> <[px] px>' or "
|
"Expected 'move <left|right|up|down> <[px] px>' or "
|
||||||
"'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or "
|
"'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or "
|
||||||
"'move <container|window|workspace> [to] output <name|direction>' or "
|
"'move <container|window|workspace> [to] output <name|direction|next|prev|current>' or "
|
||||||
"'move <container|window> [to] mark <mark>'";
|
"'move <container|window> [to] mark <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,
|
static bool is_parallel(enum sway_container_layout layout,
|
||||||
enum wlr_direction dir) {
|
enum wlr_direction dir) {
|
||||||
switch (layout) {
|
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);
|
struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws);
|
||||||
destination = dst ? &dst->node : &ws->node;
|
destination = dst ? &dst->node : &ws->node;
|
||||||
} else if (strcasecmp(argv[0], "output") == 0) {
|
} 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);
|
old_output, container->pending.x, container->pending.y);
|
||||||
if (!new_output) {
|
if (!new_output) {
|
||||||
return cmd_results_new(CMD_FAILURE,
|
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;
|
struct sway_output *old_output = workspace->output;
|
||||||
int center_x = workspace->width / 2 + workspace->x,
|
int center_x = workspace->width / 2 + workspace->x,
|
||||||
center_y = workspace->height / 2 + workspace->y;
|
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);
|
old_output, center_x, center_y);
|
||||||
if (!new_output) {
|
if (!new_output) {
|
||||||
return cmd_results_new(CMD_FAILURE,
|
return cmd_results_new(CMD_FAILURE,
|
||||||
|
|
|
||||||
|
|
@ -7,29 +7,22 @@
|
||||||
#include <wlr/backend/headless.h>
|
#include <wlr/backend/headless.h>
|
||||||
#include <wlr/render/swapchain.h>
|
#include <wlr/render/swapchain.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_buffer.h>
|
|
||||||
#include <wlr/types/wlr_alpha_modifier_v1.h>
|
#include <wlr/types/wlr_alpha_modifier_v1.h>
|
||||||
#include <wlr/types/wlr_gamma_control_v1.h>
|
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output_management_v1.h>
|
#include <wlr/types/wlr_output_management_v1.h>
|
||||||
#include <wlr/types/wlr_output_power_management_v1.h>
|
#include <wlr/types/wlr_output_power_management_v1.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_presentation_time.h>
|
#include <wlr/types/wlr_presentation_time.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/util/region.h>
|
|
||||||
#include <wlr/util/transform.h>
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
#include "sway/desktop/transaction.h"
|
#include "sway/desktop/transaction.h"
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
#include "sway/input/seat.h"
|
#include "sway/input/seat.h"
|
||||||
#include "sway/ipc-server.h"
|
#include "sway/ipc-server.h"
|
||||||
#include "sway/layers.h"
|
|
||||||
#include "sway/output.h"
|
#include "sway/output.h"
|
||||||
#include "sway/scene_descriptor.h"
|
#include "sway/scene_descriptor.h"
|
||||||
#include "sway/server.h"
|
#include "sway/server.h"
|
||||||
#include "sway/tree/arrange.h"
|
|
||||||
#include "sway/tree/container.h"
|
#include "sway/tree/container.h"
|
||||||
#include "sway/tree/root.h"
|
#include "sway/tree/root.h"
|
||||||
#include "sway/tree/view.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;
|
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_workspace *output_get_active_workspace(struct sway_output *output) {
|
||||||
struct sway_seat *seat = input_manager_current_seat();
|
struct sway_seat *seat = input_manager_current_seat();
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,13 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
|
||||||
*focus* output up|right|down|left
|
*focus* output up|right|down|left
|
||||||
Moves focus to the next output in the specified direction.
|
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 <name>
|
*focus* output <name>
|
||||||
Moves focus to the named output.
|
Moves focus to the named output.
|
||||||
|
|
||||||
|
|
@ -275,6 +282,10 @@ set|plus|minus|toggle <amount>
|
||||||
Moves the focused container to next output in the specified
|
Moves the focused container to next output in the specified
|
||||||
direction.
|
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
|
*move* [container|window] [to] scratchpad
|
||||||
Moves the focused container to the scratchpad.
|
Moves the focused container to the scratchpad.
|
||||||
|
|
||||||
|
|
@ -290,6 +301,14 @@ set|plus|minus|toggle <amount>
|
||||||
*move* workspace to [output] up|right|down|left
|
*move* workspace to [output] up|right|down|left
|
||||||
Moves the focused workspace to next output in the specified direction.
|
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* <comment>
|
*nop* <comment>
|
||||||
A no operation command that can be used to override default behaviour. The
|
A no operation command that can be used to override default behaviour. The
|
||||||
optional comment argument is ignored, but logged for debugging purposes.
|
optional comment argument is ignored, but logged for debugging purposes.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue