diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd
index bc9929b9..f406b887 100644
--- a/docs/labwc-actions.5.scd
+++ b/docs/labwc-actions.5.scd
@@ -340,6 +340,19 @@ Actions are used in menus and keyboard/mouse bindings.
decorations (including those for which the server-side titlebar has been
hidden) are not eligible for shading.
+**
+ Warp the cursor to a position relative to the active output or window.
+
+ *to* [output|window] Specifies the target area of the warp.
+ Default is "output"
+ *x* [center|value] Specifies the horizontal warp position within the target area.
+ "center": Moves the cursor to the horizontal center of the target area.
+ Positive or negative integers warp the cursor to a position offset by the specified
+ number of pixels from the left or right edge of the target area respectively.
+ Default is "center"
+ *y* [center|value] Equivalent for the vertical warp position within the target area.
+ Default is "center"
+
**++
**++
**
diff --git a/include/labwc.h b/include/labwc.h
index 6e5d7fdd..f8c46637 100644
--- a/include/labwc.h
+++ b/include/labwc.h
@@ -458,7 +458,6 @@ 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);
/**
diff --git a/src/action.c b/src/action.c
index ce4f077f..2a789b2c 100644
--- a/src/action.c
+++ b/src/action.c
@@ -121,7 +121,8 @@ enum action_type {
ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_TOGGLE_MAGNIFY,
ACTION_TYPE_ZOOM_IN,
- ACTION_TYPE_ZOOM_OUT
+ ACTION_TYPE_ZOOM_OUT,
+ ACTION_TYPE_WARP_CURSOR,
};
const char *action_names[] = {
@@ -186,6 +187,7 @@ const char *action_names[] = {
"ToggleMagnify",
"ZoomIn",
"ZoomOut",
+ "WarpCursor",
NULL
};
@@ -475,6 +477,12 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
goto cleanup;
}
break;
+ case ACTION_TYPE_WARP_CURSOR:
+ if (!strcmp(argument, "to") || !strcmp(argument, "x") || !strcmp(argument, "y")) {
+ action_arg_add_str(action, argument, content);
+ goto cleanup;
+ }
+ break;
}
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'",
@@ -824,6 +832,43 @@ get_target_output(struct output *output, struct server *server,
return target;
}
+static void
+warp_cursor(struct view *view, struct output *output, const char *to, const char *x, const char *y)
+{
+ struct wlr_box target_area = {0};
+ int goto_x;
+ int goto_y;
+
+ if (!strcasecmp(to, "output") && output) {
+ target_area = output_usable_area_in_layout_coords(output);
+ } else if (!strcasecmp(to, "window") && view) {
+ target_area = view->current;
+ } else {
+ wlr_log(WLR_ERROR, "Invalid argument for action WarpCursor: 'to' (%s)", to);
+ }
+
+ if (!strcasecmp(x, "center")) {
+ goto_x = target_area.x + target_area.width / 2;
+ } else {
+ int offset_x = atoi(x);
+ goto_x = offset_x >= 0 ?
+ target_area.x + offset_x :
+ target_area.x + target_area.width + offset_x;
+ }
+
+ if (!strcasecmp(y, "center")) {
+ goto_y = target_area.y + target_area.height / 2;
+ } else {
+ int offset_y = atoi(y);
+ goto_y = offset_y >= 0 ?
+ target_area.y + offset_y :
+ target_area.y + target_area.height + offset_y;
+ }
+
+ wlr_cursor_warp(output->server->seat.cursor, NULL, goto_x, goto_y);
+ cursor_update_focus(output->server);
+}
+
void
actions_run(struct view *activator, struct server *server,
struct wl_list *actions, struct cursor_context *cursor_ctx)
@@ -1287,6 +1332,16 @@ actions_run(struct view *activator, struct server *server,
case ACTION_TYPE_ZOOM_OUT:
magnify_set_scale(server, MAGNIFY_DECREASE);
break;
+ case ACTION_TYPE_WARP_CURSOR:
+ {
+ const char *to = action_get_str(action, "to", "output");
+ const char *x = action_get_str(action, "x", "center");
+ const char *y = action_get_str(action, "y", "center");
+ struct output *output = output_nearest_to_cursor(server);
+
+ warp_cursor(view, output, to, x, y);
+ }
+ break;
case ACTION_TYPE_INVALID:
wlr_log(WLR_ERROR, "Not executing unknown action");
break;