action: make "FocusOutput" behave like "MoveToOutput"

This commit is contained in:
Orfeas 2024-08-21 07:38:44 +03:00 committed by Andrew J. Hesford
parent f2b6661db4
commit dda47a5e14
10 changed files with 172 additions and 128 deletions

View file

@ -194,10 +194,20 @@ Actions are used in menus and keyboard/mouse bindings.
to 'fullscreen' or 'fullscreenForced', tearing will still only be
enabled if the active window is in fullscreen mode.
*<action name="FocusOutput" output="HDMI-A-1" />*
Give focus to topmost window on given output and warp the cursor
to the center of the window. If the given output does not contain
any windows, the cursor is centered on the given output.
*<action name="FocusOutput" output="HDMI-A-1" direction="value" wrap="no" />*
Give focus to topmost window on other output and warp the cursor
to the center of the window.
If *output* is specified, the focus is given to the specified output and
*direction* is ignored. If *output* is omitted, *direction* may be one
of "left", "right", "up" or "down" to indicate that the focus should be
given to the next output in that direction (if one exists).
*wrap* [yes|no] When using the direction attribute, wrap around
from right-to-left or top-to-bottom, and vice versa. Default is no.
If the target output does not contain any windows, the cursor will
be centered on the output.
*<action name="MoveToOutput" output="HDMI-A-1" direction="value" wrap="no" />*
Moves active window to other output, unless the window state is

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_DIRECTION_H
#define LABWC_DIRECTION_H
#include "view.h"
enum wlr_direction direction_from_view_edge(enum view_edge edge);
enum wlr_direction direction_get_opposite(enum wlr_direction direction);
#endif /* LABWC_DIRECTION_H */

View file

@ -460,6 +460,7 @@ void desktop_focus_view_or_surface(struct seat *seat, struct view *view,
void desktop_arrange_all_views(struct server *server);
void desktop_focus_output(struct output *output);
void warp_cursor(struct view *view);
struct view *desktop_topmost_focusable_view(struct server *server);
/**
@ -539,6 +540,21 @@ struct output *output_from_wlr_output(struct server *server,
struct output *output_from_name(struct server *server, const char *name);
struct output *output_nearest_to(struct server *server, int lx, int ly);
struct output *output_nearest_to_cursor(struct server *server);
/**
* output_get_adjacent() - get next output, in a given direction,
* from a given output
*
* @output: reference output
* @edge: direction in which to look for the nearest output
* @wrap: if true, wrap around at layout edge
*
* Note: if output is NULL, the output nearest the cursor will be used as the
* reference instead.
*/
struct output *output_get_adjacent(struct output *output,
enum view_edge edge, bool wrap);
bool output_is_usable(struct output *output);
void output_update_usable_area(struct output *output);
void output_update_all_usable_areas(struct server *server, bool layout_changed);

View file

@ -2,6 +2,7 @@
#ifndef LABWC_VIEW_H
#define LABWC_VIEW_H
#include "config/rcxml.h"
#include "config.h"
#include "ssd.h"
#include <stdbool.h>
@ -482,7 +483,7 @@ void view_center(struct view *view, const struct wlr_box *ref);
* @policy: placement policy to apply
*/
void view_place_by_policy(struct view *view, bool allow_cursor,
enum view_placement_policy);
enum view_placement_policy policy);
void view_constrain_size_to_that_of_usable_area(struct view *view);
void view_restore_to(struct view *view, struct wlr_box geometry);
@ -548,8 +549,6 @@ void view_on_output_destroy(struct view *view);
void view_connect_map(struct view *view, struct wlr_surface *surface);
void view_destroy(struct view *view);
struct output *view_get_adjacent_output(struct view *view, enum view_edge edge,
bool wrap);
enum view_axis view_axis_parse(const char *direction);
enum view_edge view_edge_parse(const char *direction);
enum view_placement_policy view_placement_parse(const char *policy);

View file

@ -19,7 +19,6 @@
#include "menu/menu.h"
#include "osd.h"
#include "output-virtual.h"
#include "placement.h"
#include "regions.h"
#include "ssd.h"
#include "view.h"
@ -413,11 +412,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
}
break;
case ACTION_TYPE_FOCUS_OUTPUT:
if (!strcmp(argument, "output")) {
action_arg_add_str(action, argument, content);
goto cleanup;
}
break;
case ACTION_TYPE_MOVE_TO_OUTPUT:
if (!strcmp(argument, "output")) {
action_arg_add_str(action, argument, content);
@ -552,9 +546,6 @@ action_is_valid(struct action *action)
case ACTION_TYPE_SNAP_TO_REGION:
arg_name = "region";
break;
case ACTION_TYPE_FOCUS_OUTPUT:
arg_name = "output";
break;
case ACTION_TYPE_IF:
case ACTION_TYPE_FOR_EACH:
; /* works around "a label can only be part of a statement" */
@ -725,6 +716,29 @@ start_window_cycling(struct server *server, enum lab_cycle_dir direction)
osd_update(server);
}
static struct output *
get_target_output(struct output *output, struct server *server,
struct action *action)
{
const char *output_name = action_get_str(action, "output", NULL);
struct output *target = NULL;
if (output_name) {
target = output_from_name(server, output_name);
} else {
enum view_edge edge =
action_get_int(action, "direction", VIEW_EDGE_INVALID);
bool wrap = action_get_bool(action, "wrap", false);
target = output_get_adjacent(output, edge, wrap);
}
if (!target) {
wlr_log(WLR_DEBUG, "Invalid output");
}
return target;
}
void
actions_run(struct view *activator, struct server *server,
struct wl_list *actions, uint32_t resize_edges)
@ -739,6 +753,9 @@ actions_run(struct view *activator, struct server *server,
struct view *view;
struct action *action;
struct output *output;
struct output *target;
wl_list_for_each(action, actions, link) {
wlr_log(WLR_DEBUG, "Handling action %u: %s", action->type,
action_names[action->type]);
@ -1009,25 +1026,10 @@ actions_run(struct view *activator, struct server *server,
if (!view) {
break;
}
const char *output_name = action_get_str(action, "output", NULL);
struct output *target = NULL;
if (output_name) {
target = output_from_name(view->server, output_name);
} else {
enum view_edge edge = action_get_int(action, "direction", 0);
bool wrap = action_get_bool(action, "wrap", false);
target = view_get_adjacent_output(view, edge, wrap);
target = get_target_output(view->output, server, action);
if (target) {
view_move_to_output(view, target);
}
if (!target) {
/*
* Most likely because we're already on the
* output furthest in the requested direction
* or the output or direction was invalid.
*/
wlr_log(WLR_DEBUG, "Invalid output");
break;
}
view_move_to_output(view, target);
break;
case ACTION_TYPE_FIT_TO_OUTPUT:
if (!view) {
@ -1039,7 +1041,7 @@ actions_run(struct view *activator, struct server *server,
if (!view) {
break;
}
struct output *output = view->output;
output = view->output;
if (!output) {
break;
}
@ -1058,9 +1060,10 @@ actions_run(struct view *activator, struct server *server,
}
break;
case ACTION_TYPE_FOCUS_OUTPUT:
{
const char *output_name = action_get_str(action, "output", NULL);
desktop_focus_output(output_from_name(server, output_name));
output = output_nearest_to_cursor(server);
target = get_target_output(output, server, action);
if (target) {
desktop_focus_output(target);
}
break;
case ACTION_TYPE_IF:

42
src/common/direction.c Normal file
View file

@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/types/wlr_output_layout.h>
#include "common/direction.h"
#include "view.h"
enum wlr_direction
direction_from_view_edge(enum view_edge edge)
{
switch (edge) {
case VIEW_EDGE_LEFT:
return WLR_DIRECTION_LEFT;
case VIEW_EDGE_RIGHT:
return WLR_DIRECTION_RIGHT;
case VIEW_EDGE_UP:
return WLR_DIRECTION_UP;
case VIEW_EDGE_DOWN:
return WLR_DIRECTION_DOWN;
case VIEW_EDGE_CENTER:
case VIEW_EDGE_INVALID:
default:
return WLR_DIRECTION_UP;
}
}
enum wlr_direction
direction_get_opposite(enum wlr_direction direction)
{
switch (direction) {
case WLR_DIRECTION_RIGHT:
return WLR_DIRECTION_LEFT;
case WLR_DIRECTION_LEFT:
return WLR_DIRECTION_RIGHT;
case WLR_DIRECTION_DOWN:
return WLR_DIRECTION_UP;
case WLR_DIRECTION_UP:
return WLR_DIRECTION_DOWN;
default:
assert(0); /* Unreachable */
return WLR_DIRECTION_UP;
}
}

View file

@ -1,5 +1,6 @@
labwc_sources += files(
'box.c',
'direction.c',
'box.c',
'buf.c',
'dir.c',
'fd-util.c',

View file

@ -197,10 +197,10 @@ desktop_focus_output(struct output *output)
if (wlr_output_layout_intersects(layout,
output->wlr_output, &view->current)) {
desktop_focus_view(view, /*raise*/ false);
wlr_cursor_warp(output->server->seat.cursor, NULL,
wlr_cursor_warp(view->server->seat.cursor, NULL,
view->current.x + view->current.width / 2,
view->current.y + view->current.height / 2);
cursor_update_focus(output->server);
cursor_update_focus(view->server);
return;
}
}

View file

@ -18,6 +18,7 @@
#include <wlr/types/wlr_scene.h>
#include <wlr/util/region.h>
#include <wlr/util/log.h>
#include "common/direction.h"
#include "common/macros.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
@ -880,6 +881,53 @@ output_nearest_to_cursor(struct server *server)
server->seat.cursor->y);
}
struct output *
output_get_adjacent(struct output *output, enum view_edge edge, bool wrap)
{
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR,
"output is not usable, cannot find adjacent output");
return NULL;
}
struct wlr_box box = output_usable_area_in_layout_coords(output);
int lx = box.x + box.width / 2;
int ly = box.y + box.height / 2;
/* Determine any adjacent output in the appropriate direction */
struct wlr_output *new_output = NULL;
struct wlr_output *current_output = output->wlr_output;
struct wlr_output_layout *layout = output->server->output_layout;
enum wlr_direction direction = direction_from_view_edge(edge);
new_output = wlr_output_layout_adjacent_output(layout, direction,
current_output, lx, ly);
/*
* Optionally wrap around from top-to-bottom or left-to-right, and vice
* versa.
*/
if (wrap && !new_output) {
new_output = wlr_output_layout_farthest_output(layout,
direction_get_opposite(direction), current_output, lx, ly);
}
/*
* When "adjacent" output is the same as the original, there is no
* adjacent
*/
if (!new_output || new_output == current_output) {
return NULL;
}
output = output_from_wlr_output(output->server, new_output);
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR, "invalid output in layout");
return NULL;
}
return output;
}
bool
output_is_usable(struct output *output)
{

View file

@ -1790,91 +1790,6 @@ view_on_output_destroy(struct view *view)
view->output = NULL;
}
static enum wlr_direction
opposite_direction(enum wlr_direction direction)
{
switch (direction) {
case WLR_DIRECTION_RIGHT:
return WLR_DIRECTION_LEFT;
case WLR_DIRECTION_LEFT:
return WLR_DIRECTION_RIGHT;
case WLR_DIRECTION_DOWN:
return WLR_DIRECTION_UP;
case WLR_DIRECTION_UP:
return WLR_DIRECTION_DOWN;
default:
return 0;
}
}
static enum wlr_direction
get_wlr_direction(enum view_edge edge)
{
switch (edge) {
case VIEW_EDGE_LEFT:
return WLR_DIRECTION_LEFT;
case VIEW_EDGE_RIGHT:
return WLR_DIRECTION_RIGHT;
case VIEW_EDGE_UP:
return WLR_DIRECTION_UP;
case VIEW_EDGE_DOWN:
return WLR_DIRECTION_DOWN;
case VIEW_EDGE_CENTER:
case VIEW_EDGE_INVALID:
default:
return 0;
}
}
struct output *
view_get_adjacent_output(struct view *view, enum view_edge edge, bool wrap)
{
assert(view);
struct output *output = view->output;
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR,
"view has no output, cannot find adjacent output");
return NULL;
}
struct wlr_box box = output_usable_area_in_layout_coords(output);
int lx = box.x + box.width / 2;
int ly = box.y + box.height / 2;
/* Determine any adjacent output in the appropriate direction */
struct wlr_output *new_output = NULL;
struct wlr_output *current_output = output->wlr_output;
struct wlr_output_layout *layout = view->server->output_layout;
enum wlr_direction direction = get_wlr_direction(edge);
new_output = wlr_output_layout_adjacent_output(layout, direction,
current_output, lx, ly);
/*
* Optionally wrap around from top-to-bottom or left-to-right, and vice
* versa.
*/
if (wrap && !new_output) {
new_output = wlr_output_layout_farthest_output(layout,
opposite_direction(direction), current_output, lx, ly);
}
/*
* When "adjacent" output is the same as the original, there is no
* adjacent
*/
if (!new_output || new_output == current_output) {
return NULL;
}
output = output_from_wlr_output(view->server, new_output);
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR, "invalid output in layout");
return NULL;
}
return output;
}
static int
shift_view_to_usable_1d(int size,
int cur_pos, int cur_lo, int cur_extent,
@ -1936,7 +1851,7 @@ view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_wind
/* Otherwise, move to edge of next adjacent display, if possible */
struct output *output =
view_get_adjacent_output(view, direction, /* wrap */ false);
output_get_adjacent(view->output, direction, /* wrap */ false);
if (!output) {
return;
}
@ -2115,7 +2030,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge,
if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) {
/* We are already tiled for this edge; try to switch outputs */
output = view_get_adjacent_output(view, edge, /* wrap */ false);
output = output_get_adjacent(view->output, edge, /* wrap */ false);
if (!output) {
/*