Fix focus issue when moving transient and related windows between workspaces.

Fixes #283
This commit is contained in:
Reginald Kennedy 2020-01-30 20:49:28 +08:00
parent 5bc8f0105b
commit a485f9a628

View file

@ -5,7 +5,7 @@
* Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org> * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
* Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi> * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
* Copyright (c) 2011 Jason L. Wright <jason@thought.net> * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
* Copyright (c) 2011-2019 Reginald Kennedy <rk@rejii.com> * Copyright (c) 2011-2020 Reginald Kennedy <rk@rejii.com>
* Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net> * Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
* Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com> * Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
* Copyright (c) 2012-2015 David Hill <dhill@mindcry.org> * Copyright (c) 2012-2015 David Hill <dhill@mindcry.org>
@ -1155,6 +1155,7 @@ char *expand_tilde(const char *);
void expose(xcb_expose_event_t *); void expose(xcb_expose_event_t *);
void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t); void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t);
struct swm_bar *find_bar(xcb_window_t); struct swm_bar *find_bar(xcb_window_t);
struct ws_win *find_main_window(struct ws_win *);
struct pid_e *find_pid(pid_t); struct pid_e *find_pid(pid_t);
struct swm_region *find_region(xcb_window_t); struct swm_region *find_region(xcb_window_t);
struct ws_win *find_unmanaged_window(xcb_window_t); struct ws_win *find_unmanaged_window(xcb_window_t);
@ -1179,7 +1180,7 @@ char *get_atom_name(xcb_atom_t);
#endif #endif
xcb_keycode_t get_binding_keycode(struct binding *); xcb_keycode_t get_binding_keycode(struct binding *);
struct ws_win *get_focus_magic(struct ws_win *); struct ws_win *get_focus_magic(struct ws_win *);
struct ws_win *get_focus_prev(struct ws_win *); struct ws_win *get_focus_other(struct ws_win *);
xcb_generic_event_t *get_next_event(bool); xcb_generic_event_t *get_next_event(bool);
#ifdef SWM_DEBUG #ifdef SWM_DEBUG
char *get_notify_detail_label(uint8_t); char *get_notify_detail_label(uint8_t);
@ -1786,7 +1787,8 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending)
if (changed & EWMH_F_HIDDEN) { if (changed & EWMH_F_HIDDEN) {
if (ICONIC(win)) { if (ICONIC(win)) {
if (focus_mode != SWM_FOCUS_FOLLOW) if (focus_mode != SWM_FOCUS_FOLLOW)
ws->focus_pending = get_focus_prev(win); ws->focus_pending =
get_focus_magic(get_focus_other(win));
unfocus_win(win); unfocus_win(win);
unmap_window(win); unmap_window(win);
@ -3983,7 +3985,6 @@ count_win(struct workspace *ws, bool count_transient)
continue; continue;
count++; count++;
} }
DNPRINTF(SWM_D_MISC, "count: %d\n", count);
return (count); return (count);
} }
@ -4759,13 +4760,14 @@ focus_win(struct ws_win *win)
if (ws->r) { if (ws->r) {
/* Set input focus if no input hint, or indicated by hint. */ /* Set input focus if no input hint, or indicated by hint. */
if (ACCEPTS_FOCUS(win)) { if (ACCEPTS_FOCUS(win)) {
DNPRINTF(SWM_D_FOCUS, "set_input_focus: %#x, revert-to:" DNPRINTF(SWM_D_FOCUS, "SetInputFocus: %#x, revert-to:"
" parent, time: %#x\n", win->id, last_event_time); " PointerRoot, time: %#x\n", win->id,
last_event_time);
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
win->id, last_event_time); win->id, last_event_time);
} else if (!win->take_focus) { } else if (!win->take_focus) {
DNPRINTF(SWM_D_FOCUS, "set_input_focus: %#x," DNPRINTF(SWM_D_FOCUS, "SetInputFocus: %#x, revert-to: "
" revert-to: parent, time: 0\n", ws->r->id); "PointerRoot, time: CurrentTime\n", ws->r->id);
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
ws->r->id, XCB_CURRENT_TIME); ws->r->id, XCB_CURRENT_TIME);
} }
@ -5397,123 +5399,117 @@ out:
DNPRINTF(SWM_D_MOVE, "done\n"); DNPRINTF(SWM_D_MOVE, "done\n");
} }
/* Determine focus other than specified window, but on the same workspace. */
struct ws_win * struct ws_win *
get_focus_prev(struct ws_win *win) get_focus_other(struct ws_win *win)
{ {
struct ws_win *winfocus = NULL; struct ws_win *w, *winfocus = NULL;
struct ws_win *cur_focus = NULL;
struct ws_win_list *wl = NULL; struct ws_win_list *wl = NULL;
struct workspace *ws = NULL; struct workspace *ws = NULL;
if (!(win && win->ws)) if (!(win && win->ws))
return (NULL); goto done;
ws = win->ws; ws = win->ws;
wl = &ws->winlist; wl = &ws->winlist;
cur_focus = ws->focus;
DNPRINTF(SWM_D_FOCUS, "win %#x, cur_focus: %#x, focus_prev: %#x\n", DNPRINTF(SWM_D_FOCUS, "win %#x, focus: %#x, focus_prev: %#x, "
WINID(win), WINID(cur_focus), WINID(ws->focus_prev)); "focus_close: %d, focus_close_wrap: %d, focus_default: %d\n",
WINID(win), WINID(ws->focus), WINID(ws->focus_prev), focus_close,
focus_close_wrap, focus_default);
/* pickle, just focus on whatever */ if (ws->focus == NULL) {
if (cur_focus == NULL) { /* Fallback to default. */
/* use prev_focus if valid */
if (ws->focus_prev && find_window(ws->focus_prev->id))
winfocus = ws->focus_prev;
goto done;
}
/* if transient focus on parent */
if (TRANS(cur_focus)) {
winfocus = find_window(cur_focus->transient);
goto done;
}
/* if in max_stack try harder */
if ((win->quirks & SWM_Q_FOCUSPREV) ||
(ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
if (cur_focus != ws->focus_prev)
winfocus = ws->focus_prev;
else
winfocus = TAILQ_PREV(win, ws_win_list, entry);
if (winfocus)
goto done;
}
DNPRINTF(SWM_D_FOCUS, "focus_close: %d\n", focus_close);
if (winfocus == NULL || winfocus == win) {
switch (focus_close) {
case SWM_STACK_BOTTOM:
TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) && winfocus != cur_focus)
break;
break;
case SWM_STACK_TOP:
TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
if (!ICONIC(winfocus) && winfocus != cur_focus)
break;
break;
case SWM_STACK_ABOVE:
winfocus = TAILQ_NEXT(cur_focus, entry);
while (winfocus && ICONIC(winfocus))
winfocus = TAILQ_NEXT(winfocus, entry);
if (winfocus == NULL) {
if (focus_close_wrap) {
TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) &&
winfocus != cur_focus)
break;
} else {
TAILQ_FOREACH_REVERSE(winfocus, wl,
ws_win_list, entry)
if (!ICONIC(winfocus) &&
winfocus != cur_focus)
break;
}
}
break;
case SWM_STACK_BELOW:
winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
while (winfocus && ICONIC(winfocus))
winfocus = TAILQ_PREV(winfocus, ws_win_list,
entry);
if (winfocus == NULL) {
if (focus_close_wrap) {
TAILQ_FOREACH_REVERSE(winfocus, wl,
ws_win_list, entry)
if (!ICONIC(winfocus) &&
winfocus != cur_focus)
break;
} else {
TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) &&
winfocus != cur_focus)
break;
}
}
break;
}
}
done:
if (winfocus == NULL ||
(winfocus && (ICONIC(winfocus) || winfocus == cur_focus))) {
if (focus_default == SWM_STACK_TOP) { if (focus_default == SWM_STACK_TOP) {
TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
if (!ICONIC(winfocus) && winfocus != cur_focus) if (!ICONIC(winfocus) && winfocus != win)
break; break;
} else { } else {
TAILQ_FOREACH(winfocus, wl, entry) TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) && winfocus != cur_focus) if (!ICONIC(winfocus) && winfocus != win)
break; break;
} }
goto done;
}
if (ws->focus != win && !ICONIC(ws->focus)) {
winfocus = ws->focus;
goto done;
}
/* FOCUSPREV quirk: try previously focused window. */
if (((win->quirks & SWM_Q_FOCUSPREV) ||
(ws->cur_layout->flags & SWM_L_FOCUSPREV)) &&
ws->focus_prev && ws->focus_prev != win &&
!ICONIC(ws->focus_prev)) {
winfocus = ws->focus_prev;
goto done;
}
if (ws->focus == win && TRANS(win)) {
w = find_window(win->transient);
if (w && w->ws == ws && !ICONIC(w)) {
winfocus = w;
goto done;
}
} }
kill_refs(win); switch (focus_close) {
case SWM_STACK_BOTTOM:
TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) && winfocus != win)
break;
break;
case SWM_STACK_TOP:
TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
if (!ICONIC(winfocus) && winfocus != win)
break;
break;
case SWM_STACK_ABOVE:
winfocus = TAILQ_NEXT(win, entry);
while (winfocus && ICONIC(winfocus))
winfocus = TAILQ_NEXT(winfocus, entry);
return (get_focus_magic(winfocus)); if (winfocus == NULL) {
if (focus_close_wrap) {
TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) &&
winfocus != win)
break;
} else {
TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list,
entry)
if (!ICONIC(winfocus) &&
winfocus != win)
break;
}
}
break;
case SWM_STACK_BELOW:
winfocus = TAILQ_PREV(win, ws_win_list, entry);
while (winfocus && ICONIC(winfocus))
winfocus = TAILQ_PREV(winfocus, ws_win_list, entry);
if (winfocus == NULL) {
if (focus_close_wrap) {
TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list,
entry)
if (!ICONIC(winfocus) &&
winfocus != win)
break;
} else {
TAILQ_FOREACH(winfocus, wl, entry)
if (!ICONIC(winfocus) &&
winfocus != win)
break;
}
}
break;
}
done:
DNPRINTF(SWM_D_FOCUS, "winfocus: %#x\n", WINID(winfocus));
return (winfocus);
} }
struct ws_win * struct ws_win *
@ -6467,7 +6463,10 @@ send_to_ws(struct binding *bp, struct swm_region *r, union arg *args)
/* Set new focus on current ws. */ /* Set new focus on current ws. */
if (focus_mode != SWM_FOCUS_FOLLOW) { if (focus_mode != SWM_FOCUS_FOLLOW) {
if (r->ws->focus != NULL) { if (r->ws->focus == NULL)
r->ws->focus = r->ws->focus_pending;
if (r->ws->focus) {
focus_win(r->ws->focus); focus_win(r->ws->focus);
} else { } else {
DNPRINTF(SWM_D_FOCUS, "set_input_focus: %#x, " DNPRINTF(SWM_D_FOCUS, "set_input_focus: %#x, "
@ -6506,11 +6505,38 @@ send_to_rg_relative(struct binding *bp, struct swm_region *r, union arg *args)
send_to_ws(bp, r, &args_abs); send_to_ws(bp, r, &args_abs);
} }
/* Determine a window to consider 'main' for specified window. */
struct ws_win *
find_main_window(struct ws_win *win)
{
struct ws_win *w;
int i, wincount;
if (!TRANS(win) || win == NULL)
return (win);
/* Limit search to prevent infinite loop. */
wincount = 0;
for (i = 0; i < workspace_limit; i++)
wincount += count_win(&win->s->ws[i], true);
/* Resolve TRANSIENT_FOR as far as possible. */
w = win;
for (i = 0; TRANS(w) && i < wincount; i++) {
w = find_window(w->transient);
if (w == win)
/* Transient loop shouldn't occur. */
break;
}
return (w);
}
void void
win_to_ws(struct ws_win *win, int wsid, bool unfocus) win_to_ws(struct ws_win *win, int wsid, bool unfocus)
{ {
struct ws_win *parent; struct ws_win *mainw, *w, *tmpw;
struct workspace *ws, *nws, *pws; struct workspace *ws, *nws;
if (wsid < 0 || wsid >= workspace_limit) if (wsid < 0 || wsid >= workspace_limit)
return; return;
@ -6523,76 +6549,49 @@ win_to_ws(struct ws_win *win, int wsid, bool unfocus)
DNPRINTF(SWM_D_MOVE, "win %#x, ws %d -> %d\n", win->id, ws->idx, wsid); DNPRINTF(SWM_D_MOVE, "win %#x, ws %d -> %d\n", win->id, ws->idx, wsid);
/* Cleanup focus on source ws. */ mainw = find_main_window(win);
if (focus_mode != SWM_FOCUS_FOLLOW &&
(ws->focus == win || ws->focus_pending == win))
ws->focus_pending = get_focus_prev(win);
/* Move the parent if this is a transient window. */ DNPRINTF(SWM_D_MOVE, "focus %#x, mainw: %#x\n", WINID(ws->focus), WINID(mainw));
if (TRANS(win)) {
parent = find_window(win->transient);
if (parent) {
pws = parent->ws;
/* Set new focus in parent's ws if needed. */
if (pws->focus == parent) {
if (focus_mode != SWM_FOCUS_FOLLOW)
pws->focus_pending =
get_focus_prev(parent);
unfocus_win(parent); /* Transfer main window and any related transients. */
TAILQ_FOREACH_SAFE(w, &ws->winlist, entry, tmpw) {
if (find_main_window(w) == mainw) {
if (ws->focus == w) {
ws->focus_pending = get_focus_other(w);
if (focus_mode != SWM_FOCUS_FOLLOW) { if (unfocus)
pws->focus = pws->focus_pending; unfocus_win(w);
pws->focus_pending = NULL;
}
} }
/* Don't unmap parent if new ws is visible */ /* Don't unmap if new ws is visible */
if (nws->r == NULL) if (nws->r == NULL)
unmap_window(parent); unmap_window(w);
/* Transfer */ /* Transfer */
TAILQ_REMOVE(&ws->winlist, parent, entry); TAILQ_REMOVE(&ws->winlist, w, entry);
TAILQ_REMOVE(&ws->stack, parent, stack_entry); TAILQ_REMOVE(&ws->stack, w, stack_entry);
TAILQ_INSERT_TAIL(&nws->winlist, parent, entry); TAILQ_INSERT_TAIL(&nws->winlist, w, entry);
TAILQ_INSERT_TAIL(&nws->stack, parent, stack_entry); TAILQ_INSERT_TAIL(&nws->stack, w, stack_entry);
parent->ws = nws; w->ws = nws;
DNPRINTF(SWM_D_PROP, "set property: " /* Cleanup references. */
"_NET_WM_DESKTOP: %d\n", wsid); if (ws->focus == w)
ws->focus = NULL;
if (ws->focus_prev == w)
ws->focus_prev = NULL;
if (ws->focus_pending == w)
ws->focus_pending = NULL;
if (ws->focus_raise == w)
ws->focus_raise = NULL;
DNPRINTF(SWM_D_PROP, "win %#x, set property: "
"_NET_WM_DESKTOP: %d\n", w->id, wsid);
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
parent->id, ewmh[_NET_WM_DESKTOP].atom, w->id, ewmh[_NET_WM_DESKTOP].atom,
XCB_ATOM_CARDINAL, 32, 1, &wsid); XCB_ATOM_CARDINAL, 32, 1, &wsid);
} }
} }
if (unfocus)
unfocus_win(win);
if (ws->focus_prev == win)
ws->focus_prev = NULL;
if (focus_mode != SWM_FOCUS_FOLLOW && ws->focus_pending != NULL) {
ws->focus = ws->focus_pending;
ws->focus_pending = NULL;
}
/* Don't unmap if new ws is visible */
if (nws->r == NULL)
unmap_window(win);
/* Transfer */
TAILQ_REMOVE(&ws->winlist, win, entry);
TAILQ_REMOVE(&ws->stack, win, stack_entry);
TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
TAILQ_INSERT_TAIL(&nws->stack, win, stack_entry);
win->ws = nws;
/* Update the window's workspace property: _NET_WM_DESKTOP */
DNPRINTF(SWM_D_PROP, "set property: _NET_WM_DESKTOP: %d\n", wsid);
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &wsid);
ewmh_update_client_list(); ewmh_update_client_list();
DNPRINTF(SWM_D_MOVE, "done\n"); DNPRINTF(SWM_D_MOVE, "done\n");
@ -7934,21 +7933,26 @@ resize(struct binding *bp, struct swm_region *r, union arg *args)
void void
regionize(struct ws_win *win, int x, int y) regionize(struct ws_win *win, int x, int y)
{ {
struct swm_region *r = NULL; struct swm_region *r, *r_orig;
if (win == NULL)
return;
r_orig = win->ws->r;
r = region_under(win->s, x, y); r = region_under(win->s, x, y);
if (r == NULL) if (r == NULL)
r = region_under(win->s, X(win) + WIDTH(win) / 2, r = region_under(win->s, X(win) + WIDTH(win) / 2,
Y(win) + HEIGHT(win) / 2); Y(win) + HEIGHT(win) / 2);
if (r && r != win->ws->r) { if (r && r != r_orig) {
clear_maximized(r->ws); clear_maximized(r->ws);
win_to_ws(win, r->ws->idx, false); win_to_ws(win, r->ws->idx, false);
/* Stack old and new region. */ /* Need to restack both regions. */
stack(r_orig);
stack(r); stack(r);
stack(win->ws->r);
/* Set focus on new ws. */ /* Set focus on new ws. */
unfocus_win(r->ws->focus); unfocus_win(r->ws->focus);
@ -11627,14 +11631,8 @@ destroynotify(xcb_destroy_notify_event_t *e)
goto out; goto out;
} }
if (focus_mode != SWM_FOCUS_FOLLOW) { if (focus_mode != SWM_FOCUS_FOLLOW && win == ws->focus)
/* If we were focused, make sure we focus on something else. */ ws->focus_pending = get_focus_other(win);
if (win == ws->focus) {
ws->focus_pending = get_focus_prev(win);
if (ws->focus_pending == win)
ws->focus_pending = NULL;
}
}
unmanage_window(win); unmanage_window(win);
TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
@ -12156,11 +12154,9 @@ unmapnotify(xcb_unmap_notify_event_t *e)
/* If win was focused, make sure to focus on something else. */ /* If win was focused, make sure to focus on something else. */
if (win == ws->focus) { if (win == ws->focus) {
if (focus_mode != SWM_FOCUS_FOLLOW) { if (focus_mode != SWM_FOCUS_FOLLOW)
ws->focus_pending = get_focus_prev(win); ws->focus_pending =
DNPRINTF(SWM_D_EVENT, "focus_pending: %#x\n", get_focus_magic(get_focus_other(win));
WINID(ws->focus_pending));
}
unfocus_win(win); unfocus_win(win);
} }