diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 72e81d6e..b5e7d2bc 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -80,6 +80,11 @@ Actions are used in menus and keyboard/mouse bindings. to Virtual Machines, VNC clients or nested compositors. A second call will restore all original keybinds. +** + 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. + ** Switch to workspace. Supported values are "last", "left", "right" or the full name of a workspace or its index (starting at 1) as configured in diff --git a/include/labwc.h b/include/labwc.h index 378e1e20..d71e00f0 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -372,6 +372,7 @@ void desktop_move_to_front(struct view *view); void desktop_move_to_back(struct view *view); void desktop_focus_and_activate_view(struct seat *seat, struct view *view); void desktop_arrange_all_views(struct server *server); +void desktop_focus_output(struct output *output); enum lab_cycle_dir { LAB_CYCLE_DIR_NONE, @@ -418,6 +419,7 @@ void output_init(struct server *server); void output_manager_init(struct server *server); struct output *output_from_wlr_output(struct server *server, struct wlr_output *wlr_output); +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); bool output_is_usable(struct output *output); diff --git a/src/action.c b/src/action.c index 61f6612f..96b02145 100644 --- a/src/action.c +++ b/src/action.c @@ -61,6 +61,7 @@ enum action_type { ACTION_TYPE_SEND_TO_DESKTOP, ACTION_TYPE_SNAP_TO_REGION, ACTION_TYPE_TOGGLE_KEYBINDS, + ACTION_TYPE_FOCUS_OUTPUT, }; const char *action_names[] = { @@ -90,6 +91,7 @@ const char *action_names[] = { "SendToDesktop", "SnapToRegion", "ToggleKeybinds", + "FocusOutput", NULL }; @@ -119,6 +121,9 @@ action_arg_from_xml_node(struct action *action, char *nodename, char *content) } else if (!strcmp(nodename, "region.action")) { /* SnapToRegion */ action_arg_add_str(action, NULL, content); + } else if (!strcmp(nodename, "output.action")) { + /* FocusOutput */ + action_arg_add_str(action, NULL, content); } } @@ -470,6 +475,14 @@ actions_run(struct view *activator, struct server *server, wlr_log(WLR_DEBUG, "%s keybinds", server->seat.inhibit_keybinds ? "Disabled" : "Enabled"); break; + case ACTION_TYPE_FOCUS_OUTPUT: + if (!arg) { + wlr_log(WLR_ERROR, "Missing argument for FocusOutput"); + break; + } + const char *output_name = action_str_from_arg(arg); + desktop_focus_output(output_from_name(server, output_name)); + break; case ACTION_TYPE_INVALID: wlr_log(WLR_ERROR, "Not executing unknown action"); break; diff --git a/src/desktop.c b/src/desktop.c index 5c7151fc..3b871f51 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -268,6 +268,46 @@ desktop_focus_topmost_mapped_view(struct server *server) desktop_move_to_front(view); } +void +desktop_focus_output(struct output *output) +{ + if (!output_is_usable(output) || output->server->input_mode + != LAB_INPUT_STATE_PASSTHROUGH) { + return; + } + struct view *view; + struct wlr_scene_node *node; + struct wlr_output_layout *layout = output->server->output_layout; + struct wl_list *list_head = + &output->server->workspace_current->tree->children; + wl_list_for_each_reverse(node, list_head, link) { + if (!node->data) { + continue; + } + view = node_view_from_node(node); + if (!isfocusable(view)) { + continue; + } + if (wlr_output_layout_intersects(layout, + output->wlr_output, &view->current)) { + desktop_focus_and_activate_view(&output->server->seat, view); + wlr_cursor_warp(output->server->seat.cursor, NULL, + view->current.x + view->current.width / 2, + view->current.y + view->current.height / 2); + cursor_update_focus(output->server); + return; + } + } + /* No view found on desired output */ + struct wlr_box layout_box; + wlr_output_layout_get_box(output->server->output_layout, + output->wlr_output, &layout_box); + wlr_cursor_warp(output->server->seat.cursor, NULL, + layout_box.x + output->usable_area.x + output->usable_area.width / 2, + layout_box.y + output->usable_area.y + output->usable_area.height / 2); + cursor_update_focus(output->server); +} + static struct wlr_surface * get_surface_from_layer_node(struct wlr_scene_node *node) { diff --git a/src/output.c b/src/output.c index 3684ec0a..1183add4 100644 --- a/src/output.c +++ b/src/output.c @@ -8,6 +8,7 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -458,6 +459,21 @@ output_from_wlr_output(struct server *server, struct wlr_output *wlr_output) return NULL; } +struct output * +output_from_name(struct server *server, const char *name) +{ + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + if (!output_is_usable(output) || !output->wlr_output->name) { + continue; + } + if (!strcasecmp(name, output->wlr_output->name)) { + return output; + } + } + return NULL; +} + struct output * output_nearest_to(struct server *server, int lx, int ly) {