ipc: add focus, focus next, focus prev commands

This commit is contained in:
stormshadow 2026-04-30 20:04:41 +05:30
parent 8063daef52
commit 0985a44982
2 changed files with 99 additions and 0 deletions

View file

@ -152,6 +152,19 @@ The following commands are supported:
*focus output* _<name>_|left|right|up|down *focus output* _<name>_|left|right|up|down
Focus the given output by name or direction. 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* _<x>_ _<y>_ *move position* _<x>_ _<y>_
Move the target window to absolute coordinates. Move the target window to absolute coordinates.
@ -251,6 +264,24 @@ Chain multiple commands:
labmsg 'workspace 3; exec firefox' 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 # SEE ALSO
labwc(1), labwc-actions(5) labwc(1), labwc-actions(5)

View file

@ -1242,6 +1242,74 @@ execute_single_command(const char *cmd)
return cmd_result(true, NULL); 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 ... --- */ /* --- move ... --- */
if (!strncmp(cmd, "move ", 5)) { if (!strncmp(cmd, "move ", 5)) {
const char *arg = cmd + 5; const char *arg = cmd + 5;