From dda47a5e14b33e49ed8349cd9a6546f78fded2a9 Mon Sep 17 00:00:00 2001
From: Orfeas <38209077+0xfea5@users.noreply.github.com>
Date: Wed, 21 Aug 2024 07:38:44 +0300
Subject: [PATCH] action: make "FocusOutput" behave like "MoveToOutput"
---
docs/labwc-actions.5.scd | 18 ++++++--
include/common/direction.h | 10 +++++
include/labwc.h | 16 +++++++
include/view.h | 5 +--
src/action.c | 65 +++++++++++++++-------------
src/common/direction.c | 42 ++++++++++++++++++
src/common/meson.build | 3 +-
src/desktop.c | 4 +-
src/output.c | 48 ++++++++++++++++++++
src/view.c | 89 +-------------------------------------
10 files changed, 172 insertions(+), 128 deletions(-)
create mode 100644 include/common/direction.h
create mode 100644 src/common/direction.c
diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd
index 1c9e2ec1..6b9dfe8d 100644
--- a/docs/labwc-actions.5.scd
+++ b/docs/labwc-actions.5.scd
@@ -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.
-**
- 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.
+**
+ 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.
**
Moves active window to other output, unless the window state is
diff --git a/include/common/direction.h b/include/common/direction.h
new file mode 100644
index 00000000..bc8cbd0c
--- /dev/null
+++ b/include/common/direction.h
@@ -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 */
diff --git a/include/labwc.h b/include/labwc.h
index 30f9fbaf..810fed13 100644
--- a/include/labwc.h
+++ b/include/labwc.h
@@ -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);
diff --git a/include/view.h b/include/view.h
index 0fde958e..6f47f2a7 100644
--- a/include/view.h
+++ b/include/view.h
@@ -2,6 +2,7 @@
#ifndef LABWC_VIEW_H
#define LABWC_VIEW_H
+#include "config/rcxml.h"
#include "config.h"
#include "ssd.h"
#include
@@ -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);
diff --git a/src/action.c b/src/action.c
index c38cba70..79008566 100644
--- a/src/action.c
+++ b/src/action.c
@@ -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:
diff --git a/src/common/direction.c b/src/common/direction.c
new file mode 100644
index 00000000..fa88eee4
--- /dev/null
+++ b/src/common/direction.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include
+#include
+#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;
+ }
+}
diff --git a/src/common/meson.build b/src/common/meson.build
index 1569f775..d9be1d1a 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -1,5 +1,6 @@
labwc_sources += files(
- 'box.c',
+ 'direction.c',
+ 'box.c',
'buf.c',
'dir.c',
'fd-util.c',
diff --git a/src/desktop.c b/src/desktop.c
index 2c0db207..e30154b3 100644
--- a/src/desktop.c
+++ b/src/desktop.c
@@ -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;
}
}
diff --git a/src/output.c b/src/output.c
index a94c7c2d..15c8ed46 100644
--- a/src/output.c
+++ b/src/output.c
@@ -18,6 +18,7 @@
#include
#include
#include
+#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)
{
diff --git a/src/view.c b/src/view.c
index 4d8586b8..b1b4265a 100644
--- a/src/view.c
+++ b/src/view.c
@@ -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) {
/*
--
2.52.0