This commit is contained in:
Orestis Floros 2026-06-01 01:40:30 +08:00 committed by GitHub
commit ca4f892e6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 114 additions and 93 deletions

View file

@ -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);

View file

@ -1,6 +1,5 @@
#include <float.h>
#include <strings.h>
#include <wlr/types/wlr_output_layout.h>
#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 <direction|name>'.");
"Expected 'focus output <direction|name|next|prev|current>'.");
}
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 <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) {
descend = false;
}

View file

@ -24,58 +24,9 @@
static const char expected_syntax[] =
"Expected 'move <left|right|up|down> <[px] px>' 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>'";
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,

View file

@ -7,29 +7,22 @@
#include <wlr/backend/headless.h>
#include <wlr/render/swapchain.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_gamma_control_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
#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();

View file

@ -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 <name>
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
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 <amount>
*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* <comment>
A no operation command that can be used to override default behaviour. The
optional comment argument is ignored, but logged for debugging purposes.