From 0985a44982ef01346832b92a673e4d782fda65c5 Mon Sep 17 00:00:00 2001 From: stormshadow <190884359+st0rm-shad0w@users.noreply.github.com> Date: Thu, 30 Apr 2026 20:04:41 +0530 Subject: [PATCH] ipc: add focus, focus next, focus prev commands --- docs/labmsg.1.scd | 31 +++++++++++++++++++++ src/ipc.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/docs/labmsg.1.scd b/docs/labmsg.1.scd index f371ad42..2454b627 100644 --- a/docs/labmsg.1.scd +++ b/docs/labmsg.1.scd @@ -152,6 +152,19 @@ The following commands are supported: *focus output* __|left|right|up|down Focus the given output by name or direction. +*focus* + Focus the target window. Raises the window to the front and gives it + keyboard focus. Typically used with criteria to focus a specific + window, e.g. *labmsg '[app_id="firefox"] focus'*. + +*focus next* + Focus the next window in z-order below the current one. Wraps to the + topmost window if the current window is at the bottom. + +*focus prev* (or *focus last*) + Focus the previous window in z-order above the current one. Wraps to + the bottommost window if the current window is at the top. + *move position* __ __ Move the target window to absolute coordinates. @@ -251,6 +264,24 @@ Chain multiple commands: labmsg 'workspace 3; exec firefox' ``` +Focus a specific window by application ID: + +``` +labmsg '[app_id="foot"] focus' +``` + +Cycle focus to the next window: + +``` +labmsg 'focus next' +``` + +Cycle focus to the previous window: + +``` +labmsg 'focus prev' +``` + # SEE ALSO labwc(1), labwc-actions(5) diff --git a/src/ipc.c b/src/ipc.c index 566d0c28..bc84e9c5 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1242,6 +1242,74 @@ execute_single_command(const char *cmd) return cmd_result(true, NULL); } + /* --- focus --- */ + if (!strcmp(cmd, "focus")) { + if (!target) { + return cmd_result(false, "No focused window"); + } + desktop_focus_view(target, /*raise*/ true); + return cmd_result(true, NULL); + } + + /* --- focus next --- */ + if (!strcmp(cmd, "focus next")) { + struct view *next = NULL; + struct view *first = NULL; + bool found_active = false; + struct view *view; + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + if (view->minimized) { + continue; + } + if (!first) { + first = view; + } + if (found_active) { + next = view; + break; + } + if (view == server.active_view) { + found_active = true; + } + } + if (!next) { + next = first; /* wrap */ + } + if (next) { + desktop_focus_view(next, true); + } else { + return cmd_result(false, "No focusable window"); + } + return cmd_result(true, NULL); + } + + /* --- focus prev --- */ + if (!strcmp(cmd, "focus prev") || !strcmp(cmd, "focus last")) { + struct view *prev = NULL; + struct view *last = NULL; + struct view *view; + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + if (view->minimized) { + continue; + } + if (view == server.active_view) { + break; + } + prev = view; + last = view; /* keep track of last seen for wrap */ + } + if (!prev) { + /* Find the last focusable view for wrap */ + prev = last; + } + if (prev) { + desktop_focus_view(prev, true); + } else { + return cmd_result(false, "No focusable window"); + } + return cmd_result(true, NULL); + } + /* --- move ... --- */ if (!strncmp(cmd, "move ", 5)) { const char *arg = cmd + 5;