chore: add alt-tab navigation with update_mru flag and MRU-based taskbar rendering

This commit is contained in:
2026-01-16 17:24:59 +00:00
parent a6eeeb978e
commit c32bda853c
28 changed files with 144 additions and 41 deletions
+1 -1
View File
@@ -111,7 +111,7 @@ static void handle_command(struct mg_connection *c, cJSON *json) {
if (cl->workspace != (unsigned int)dwn->current_workspace) {
workspace_switch(cl->workspace);
}
client_focus(cl);
client_focus(cl, true);
handle_get_status(c);
}
}
+13 -7
View File
@@ -232,7 +232,7 @@ Client *client_manage(Window window)
client_show(client);
client_sync_log("client_manage: focusing");
client_focus(client);
client_focus(client, true);
client_sync_log("client_manage: DONE");
return client;
@@ -249,6 +249,10 @@ void client_unmanage(Client *client)
LOG_DEBUG("Unmanaging window: %lu", client->window);
if (dwn->alt_tab_client == client) {
workspace_end_alt_tab();
}
client_sync_log("client_unmanage: remove from MRU");
workspace_mru_remove(client->workspace, client);
@@ -274,7 +278,7 @@ void client_unmanage(Client *client)
if (ws != NULL && ws->focused == NULL) {
Client *next = workspace_mru_get_previous(dwn->current_workspace, NULL);
if (next != NULL) {
client_focus(next);
client_focus(next, true);
} else {
XSetInputFocus(dwn->display, dwn->root, RevertToPointerRoot, CurrentTime);
atoms_set_active_window(None);
@@ -316,7 +320,7 @@ Client *client_find_by_frame(Window frame)
}
void client_focus(Client *client)
void client_focus(Client *client, bool update_mru)
{
client_sync_log("client_focus: START");
@@ -354,7 +358,9 @@ void client_focus(Client *client)
if (ws != NULL) {
ws->focused = client;
workspace_mru_push(client->workspace, client);
if (update_mru) {
workspace_mru_push(client->workspace, client);
}
}
client_sync_log("client_focus: raising");
@@ -442,7 +448,7 @@ void client_minimize(Client *client)
if (ws != NULL && ws->focused == client) {
Client *next = workspace_mru_get_previous(client->workspace, client);
if (next != NULL) {
client_focus(next);
client_focus(next, true);
} else {
ws->focused = NULL;
XSetInputFocus(dwn->display, dwn->root, RevertToPointerRoot, CurrentTime);
@@ -459,7 +465,7 @@ void client_restore(Client *client)
client->flags &= ~CLIENT_MINIMIZED;
client_show(client);
client_focus(client);
client_focus(client, true);
}
@@ -848,7 +854,7 @@ void client_set_maximize(Client *client, bool maximized)
client_configure(client);
decorations_render(client, true);
client_raise(client);
client_focus(client);
client_focus(client, true);
} else {
client->flags &= ~CLIENT_MAXIMIZED;
+6 -2
View File
@@ -457,6 +457,10 @@ void keys_handle_release(XKeyEvent *ev)
super_pressed = false;
super_used_in_combo = false;
}
if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
workspace_end_alt_tab();
}
}
@@ -629,12 +633,12 @@ void key_toggle_minimize(void)
void key_focus_next(void)
{
workspace_focus_next();
workspace_alt_tab_next();
}
void key_focus_prev(void)
{
workspace_focus_prev();
workspace_alt_tab_prev();
}
void key_workspace_next(void)
+5 -5
View File
@@ -377,7 +377,7 @@ static void handle_enter_notify(XCrossingEvent *ev)
if (c != NULL && c->workspace == (unsigned int)dwn->current_workspace) {
int delay = dwn->config->focus_follow_delay_ms;
if (delay <= 0) {
client_focus(c);
client_focus(c, true);
} else {
dwn->pending_focus_client = c;
dwn->pending_focus_time = get_time_ms() + delay;
@@ -453,11 +453,11 @@ static void handle_button_press(XButtonEvent *ev)
if (is_client_window) {
XAllowEvents(dwn->display, ReplayPointer, ev->time);
client_focus(c);
client_focus(c, true);
return;
}
client_focus(c);
client_focus(c, true);
if (c->frame != None && ev->window == c->frame) {
ButtonType btn = decorations_hit_test_button(c, ev->x, ev->y);
@@ -573,7 +573,7 @@ static void handle_client_message(XClientMessageEvent *ev)
if (c->workspace != (unsigned int)dwn->current_workspace) {
workspace_switch(c->workspace);
}
client_focus(c);
client_focus(c, true);
}
} else if (ev->message_type == ewmh.NET_CLOSE_WINDOW) {
if (c != NULL) {
@@ -897,7 +897,7 @@ void dwn_run(void)
Workspace *ws = workspace_get_current();
if (ws != NULL && ws->focused != dwn->pending_focus_client) {
if (dwn->pending_focus_client->workspace == (unsigned int)dwn->current_workspace) {
client_focus(dwn->pending_focus_client);
client_focus(dwn->pending_focus_client, true);
}
}
dwn->pending_focus_client = NULL;
+22 -20
View File
@@ -372,10 +372,14 @@ void panel_render_taskbar(Panel *panel, int x, int *width)
if (available_width < 0) available_width = 0;
int item_count = 0;
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
if (c->workspace == (unsigned int)dwn->current_workspace) {
item_count++;
}
Workspace *ws = workspace_get_current();
if (ws == NULL) {
*width = 0;
return;
}
for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
item_count++;
}
if (item_count == 0) {
@@ -388,14 +392,12 @@ void panel_render_taskbar(Panel *panel, int x, int *width)
item_width = TASKBAR_ITEM_WIDTH;
}
Workspace *ws = workspace_get_current();
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
if (c->workspace != (unsigned int)dwn->current_workspace) {
continue;
}
bool focused = (ws != NULL && ws->focused == c);
bool focused = (ws->focused == c);
bool minimized = client_is_minimized(c);
unsigned long bg;
@@ -554,7 +556,7 @@ void panel_handle_click(Panel *panel, int x, int y, int button)
if (ws != NULL && ws->focused == c) {
client_minimize(c);
} else {
client_focus(c);
client_focus(c, true);
}
}
} else if (button == 3) {
@@ -593,7 +595,11 @@ Client *panel_hit_test_taskbar(Panel *panel, int x, int y)
}
Workspace *ws = workspace_get_current();
const char *layout_symbol = layout_get_symbol(ws != NULL ? ws->layout : LAYOUT_TILING);
if (ws == NULL) {
return NULL;
}
const char *layout_symbol = layout_get_symbol(ws->layout);
int layout_width = panel_text_width(layout_symbol, strlen(layout_symbol));
int taskbar_start = PANEL_PADDING + MAX_WORKSPACES * WORKSPACE_WIDTH + WIDGET_SPACING + layout_width + WIDGET_SPACING;
if (x < taskbar_start) {
@@ -607,10 +613,8 @@ Client *panel_hit_test_taskbar(Panel *panel, int x, int y)
if (available_width < 0) available_width = 0;
int item_count = 0;
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
if (c->workspace == (unsigned int)dwn->current_workspace) {
item_count++;
}
for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
item_count++;
}
if (item_count == 0) {
@@ -625,13 +629,11 @@ Client *panel_hit_test_taskbar(Panel *panel, int x, int y)
int index = (x - taskbar_start) / item_width;
int i = 0;
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
if (c->workspace == (unsigned int)dwn->current_workspace) {
if (i == index) {
return c;
}
i++;
for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
if (i == index) {
return c;
}
i++;
}
return NULL;
+89 -5
View File
@@ -95,11 +95,11 @@ void workspace_switch(int index)
Workspace *ws = workspace_get(index);
if (ws != NULL) {
if (ws->focused != NULL) {
client_focus(ws->focused);
client_focus(ws->focused, true);
} else {
Client *first = workspace_get_first_client(index);
if (first != NULL) {
client_focus(first);
client_focus(first, true);
} else {
XSetInputFocus(dwn->display, dwn->root, RevertToPointerRoot, CurrentTime);
atoms_set_active_window(None);
@@ -409,7 +409,7 @@ void workspace_focus_next(void)
}
if (next != NULL && next != ws->focused) {
client_focus(next);
client_focus(next, true);
}
}
@@ -435,7 +435,7 @@ void workspace_focus_prev(void)
}
if (prev != NULL && prev != ws->focused) {
client_focus(prev);
client_focus(prev, true);
}
}
@@ -443,7 +443,7 @@ void workspace_focus_master(void)
{
Client *master = workspace_get_first_client(dwn->current_workspace);
if (master != NULL) {
client_focus(master);
client_focus(master, true);
}
}
@@ -518,3 +518,87 @@ Client *workspace_mru_get_previous(int workspace, Client *current)
return NULL;
}
void workspace_alt_tab_next(void)
{
if (dwn == NULL) return;
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->mru_head == NULL) return;
if (!dwn->is_alt_tabbing) {
dwn->is_alt_tabbing = true;
dwn->alt_tab_client = ws->focused;
XGrabKeyboard(dwn->display, dwn->root, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
}
Client *current = dwn->alt_tab_client;
if (current == NULL) {
current = ws->mru_head;
}
if (current != NULL && current->mru_next != NULL) {
dwn->alt_tab_client = current->mru_next;
} else {
dwn->alt_tab_client = ws->mru_head;
}
if (dwn->alt_tab_client != NULL) {
client_focus(dwn->alt_tab_client, false);
}
}
void workspace_alt_tab_prev(void)
{
if (dwn == NULL) return;
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->mru_head == NULL) return;
if (!dwn->is_alt_tabbing) {
dwn->is_alt_tabbing = true;
dwn->alt_tab_client = ws->focused;
XGrabKeyboard(dwn->display, dwn->root, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
}
Client *current = dwn->alt_tab_client;
if (current != NULL && current->mru_prev != NULL) {
dwn->alt_tab_client = current->mru_prev;
} else {
dwn->alt_tab_client = ws->mru_tail;
}
if (dwn->alt_tab_client != NULL) {
client_focus(dwn->alt_tab_client, false);
}
}
void workspace_end_alt_tab(void)
{
if (dwn == NULL || !dwn->is_alt_tabbing) return;
dwn->is_alt_tabbing = false;
if (dwn->alt_tab_client != NULL) {
workspace_mru_push(dwn->current_workspace, dwn->alt_tab_client);
/* Redraw panel to show new MRU order */
panel_render_all();
}
dwn->alt_tab_client = NULL;
XUngrabKeyboard(dwn->display, CurrentTime);
}