mirror of https://github.com/yshui/picom.git
event: delayed handling of reparent notifies
We used to fetch the WM_STATE property in event handler, but we should only do that inside the critical section. With this commit we keep a list of all subwins (direct children of toplevels), and keep track of their WM_STATE properties, only fetching it in critical section when needed. Stop changing event masks in win_mark/unmark_client, since that's entirely managed by the subwins now. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
54fb0c7ac0
commit
5cd0edf743
|
@ -291,6 +291,8 @@ typedef struct session {
|
|||
// === Window related ===
|
||||
/// A hash table of all windows.
|
||||
struct win *windows;
|
||||
/// Direct children of all toplevels.
|
||||
struct subwin *subwins;
|
||||
/// Windows in their stacking order
|
||||
struct list_node window_stack;
|
||||
/// Pointer to <code>win</code> of current active window. Used by
|
||||
|
|
243
src/event.c
243
src/event.c
|
@ -9,6 +9,7 @@
|
|||
#include <xcb/damage.h>
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "c2.h"
|
||||
|
@ -19,6 +20,7 @@
|
|||
#include "log.h"
|
||||
#include "picom.h"
|
||||
#include "region.h"
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "win.h"
|
||||
#include "win_defs.h"
|
||||
|
@ -195,7 +197,19 @@ static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
|
|||
static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
|
||||
if (ev->parent == ps->c.screen_info->root) {
|
||||
add_win_top(ps, ev->window);
|
||||
return;
|
||||
}
|
||||
|
||||
auto w = find_win(ps, ev->parent);
|
||||
if (w == NULL) {
|
||||
// The parent window is not a toplevel window, we don't care about it.
|
||||
// This can happen if a toplevel is reparented somewhere, and more events
|
||||
// were generated from it before we can unsubscribe.
|
||||
return;
|
||||
}
|
||||
// A direct child of a toplevel, subscribe to property changes so we can
|
||||
// know if WM_STATE is set on this window.
|
||||
add_subwin_and_subscribe(&ps->subwins, &ps->c, ev->window, ev->parent);
|
||||
}
|
||||
|
||||
/// Handle configure event of a regular window
|
||||
|
@ -267,6 +281,11 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event
|
|||
}
|
||||
|
||||
static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
|
||||
auto subwin = find_subwin(ps->subwins, ev->window);
|
||||
if (subwin) {
|
||||
remove_subwin(&ps->subwins, subwin);
|
||||
}
|
||||
|
||||
auto w = find_win(ps, ev->window);
|
||||
auto mw = find_toplevel(ps, ev->window);
|
||||
if (mw && mw->client_win == mw->base.id) {
|
||||
|
@ -281,7 +300,7 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *
|
|||
|
||||
if (w != NULL) {
|
||||
destroy_win_start(ps, w);
|
||||
if (!w->managed || !((struct managed_win *)w)->to_paint) {
|
||||
if (!w->managed || !win_as_managed(w)->to_paint) {
|
||||
// If the window wasn't managed, or was already not rendered,
|
||||
// we don't need to fade it out.
|
||||
destroy_win_finish(ps, w);
|
||||
|
@ -289,7 +308,7 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *
|
|||
return;
|
||||
}
|
||||
if (mw != NULL) {
|
||||
win_unmark_client(ps, mw);
|
||||
win_unmark_client(mw);
|
||||
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
return;
|
||||
|
@ -341,80 +360,98 @@ static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev)
|
|||
static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
|
||||
log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
|
||||
ev->window, ev->parent, ev->override_redirect);
|
||||
auto w_top = find_toplevel(ps, ev->window);
|
||||
if (w_top) {
|
||||
win_unmark_client(ps, w_top);
|
||||
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
|
||||
auto old_toplevel = find_toplevel(ps, ev->window);
|
||||
if (old_toplevel) {
|
||||
win_unmark_client(old_toplevel);
|
||||
win_set_flags(old_toplevel, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
|
||||
// If the window was a toplevel window, we need to destroy it. But X will generate
|
||||
// reparent notify even if the parent didn't actually change (i.e. reparent again
|
||||
// to current parent). So if it's a root -> root reparent, we don't want to
|
||||
// destroy then recreate the window.
|
||||
auto old_w = find_win(ps, ev->window);
|
||||
auto old_subwin = find_subwin(ps->subwins, ev->window);
|
||||
auto new_toplevel = find_win(ps, ev->parent);
|
||||
xcb_window_t old_parent = XCB_NONE;
|
||||
if (old_subwin) {
|
||||
old_parent = old_subwin->toplevel;
|
||||
} else if (old_w) {
|
||||
old_parent = ps->c.screen_info->root;
|
||||
}
|
||||
// A window can't be a toplevel and a subwin at the same time
|
||||
assert(old_w == NULL || old_subwin == NULL);
|
||||
|
||||
log_debug("old toplevel: %p, old subwin: %p, new toplevel: %p, old parent: "
|
||||
"%#010x, new parent: %#010x, root window: %#010x",
|
||||
old_w, old_subwin, new_toplevel, old_parent, ev->parent,
|
||||
ps->c.screen_info->root);
|
||||
|
||||
if (old_w == NULL && old_subwin == NULL && new_toplevel == NULL &&
|
||||
ev->parent != ps->c.screen_info->root) {
|
||||
// The window is neither a toplevel nor a subwin, and the new parent is
|
||||
// neither a root nor a toplevel, we don't care about this window.
|
||||
// This can happen if a window is reparented to somewhere irrelevant, but
|
||||
// more events from it are generated before we can unsubscribe.
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev->parent == old_parent) {
|
||||
// Parent unchanged, but if the parent is root, we need to move the window
|
||||
// to the top of the window stack
|
||||
if (old_w) {
|
||||
// root -> root reparent, we just need to move it to the top
|
||||
restack_top(ps, old_w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (old_w) {
|
||||
// A toplevel is reparented, so it is no longer a toplevel. We need to
|
||||
// destroy the existing toplevel.
|
||||
if (old_w->managed) {
|
||||
auto mw = (struct managed_win *)old_w;
|
||||
// Usually, damage for unmapped windows are added in
|
||||
// `paint_preprocess`, when a window was painted before and isn't
|
||||
// anymore. But since we are reparenting the window here, we would
|
||||
// lose track of the `to_paint` information. So we just add damage
|
||||
// here.
|
||||
if (mw->to_paint) {
|
||||
add_damage_from_win(ps, mw);
|
||||
}
|
||||
// Emulating what X server does: a destroyed
|
||||
// window is always unmapped first.
|
||||
if (mw->state == WSTATE_MAPPED) {
|
||||
unmap_win_start(ps, mw);
|
||||
}
|
||||
}
|
||||
destroy_win_start(ps, old_w);
|
||||
destroy_win_finish(ps, old_w);
|
||||
}
|
||||
|
||||
// We need to guarantee a subwin exists iff it has a valid toplevel.
|
||||
auto new_subwin = old_subwin;
|
||||
if (new_subwin != NULL && new_toplevel == NULL) {
|
||||
remove_subwin_and_unsubscribe(&ps->subwins, &ps->c, new_subwin);
|
||||
new_subwin = NULL;
|
||||
} else if (new_subwin == NULL && new_toplevel != NULL) {
|
||||
new_subwin =
|
||||
add_subwin_and_subscribe(&ps->subwins, &ps->c, ev->window, ev->parent);
|
||||
}
|
||||
if (new_subwin) {
|
||||
new_subwin->toplevel = new_toplevel->id;
|
||||
if (new_toplevel->managed) {
|
||||
win_set_flags(win_as_managed(new_toplevel), WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->parent == ps->c.screen_info->root) {
|
||||
// X will generate reparent notify even if the parent didn't actually
|
||||
// change (i.e. reparent again to current parent). So we check if that's
|
||||
// the case
|
||||
auto w = find_win(ps, ev->window);
|
||||
if (w) {
|
||||
// This window has already been reparented to root before,
|
||||
// so we don't need to create a new window for it, we just need to
|
||||
// move it to the top
|
||||
restack_top(ps, w);
|
||||
} else {
|
||||
add_win_top(ps, ev->window);
|
||||
}
|
||||
} else {
|
||||
// otherwise, find and destroy the window first
|
||||
{
|
||||
auto w = find_win(ps, ev->window);
|
||||
if (w) {
|
||||
if (w->managed) {
|
||||
auto mw = (struct managed_win *)w;
|
||||
// Usually, damage for unmapped windows
|
||||
// are added in `paint_preprocess`, when
|
||||
// a window was painted before and isn't
|
||||
// anymore. But since we are reparenting
|
||||
// the window here, we would lose track
|
||||
// of the `to_paint` information. So we
|
||||
// just add damage here.
|
||||
if (mw->to_paint) {
|
||||
add_damage_from_win(ps, mw);
|
||||
}
|
||||
// Emulating what X server does: a destroyed
|
||||
// window is always unmapped first.
|
||||
if (mw->state == WSTATE_MAPPED) {
|
||||
unmap_win_start(ps, mw);
|
||||
}
|
||||
}
|
||||
// Window reparenting is unlike normal window destruction,
|
||||
// This window is going to be rendered under another
|
||||
// parent, so we don't fade here.
|
||||
destroy_win_start(ps, w);
|
||||
destroy_win_finish(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset event mask in case something wrong happens
|
||||
uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN);
|
||||
|
||||
if (!wid_has_prop(ps->c.c, ev->window, ps->atoms->aWM_STATE)) {
|
||||
log_debug("Window %#010x doesn't have WM_STATE property, it is "
|
||||
"probably not a client window. But we will listen for "
|
||||
"property change in case it gains one.",
|
||||
ev->window);
|
||||
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||
} else {
|
||||
auto w = find_managed_win(ps, ev->parent);
|
||||
if (w) {
|
||||
log_debug("Mark window %#010x (%s) as having a stale "
|
||||
"client",
|
||||
w->base.id, w->name);
|
||||
win_set_flags(w, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
} else {
|
||||
log_debug("parent %#010x not found", ev->parent);
|
||||
}
|
||||
}
|
||||
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
|
||||
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
|
||||
// New parent is root, add a toplevel;
|
||||
assert(old_w == NULL);
|
||||
assert(new_toplevel == NULL);
|
||||
add_win_top(ps, ev->window);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,6 +504,50 @@ static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline void ev_subwin_wm_state_changed(session_t *ps, xcb_property_notify_event_t *ev) {
|
||||
auto subwin = find_subwin(ps->subwins, ev->window);
|
||||
if (!subwin) {
|
||||
// We only care if a direct child of a toplevel gained/lost WM_STATE
|
||||
return;
|
||||
}
|
||||
|
||||
enum tristate old_has_wm_state = subwin->has_wm_state;
|
||||
subwin->has_wm_state = ev->state == XCB_PROPERTY_DELETE ? TRI_FALSE : TRI_TRUE;
|
||||
if (old_has_wm_state == subwin->has_wm_state) {
|
||||
if (subwin->has_wm_state == TRI_FALSE) {
|
||||
log_warn("Child window %#010x of window %#010x lost WM_STATE a "
|
||||
"second time?",
|
||||
ev->window, subwin->toplevel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto toplevel = find_win(ps, subwin->toplevel);
|
||||
BUG_ON(toplevel == NULL);
|
||||
if (!toplevel->managed) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto managed = (struct managed_win *)toplevel;
|
||||
if (managed->client_win == subwin->id) {
|
||||
// 1. This window is the client window of its toplevel, and now it lost
|
||||
// its WM_STATE (implies it must have it before)
|
||||
assert(subwin->has_wm_state == TRI_FALSE);
|
||||
win_set_flags(managed, WIN_FLAGS_CLIENT_STALE);
|
||||
} else if (subwin->has_wm_state == TRI_TRUE) {
|
||||
// 2. This window is not the client window of its toplevel, and
|
||||
// now it gained WM_STATE
|
||||
if (managed->client_win != XCB_NONE && managed->client_win != toplevel->id) {
|
||||
log_warn("Toplevel %#010x already has a client window %#010x, "
|
||||
"but another of its child windows %#010x gained "
|
||||
"WM_STATE.",
|
||||
toplevel->id, managed->client_win, subwin->id);
|
||||
} else {
|
||||
win_set_flags(managed, WIN_FLAGS_CLIENT_STALE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) {
|
||||
if (unlikely(log_get_level_tls() <= LOG_LEVEL_TRACE)) {
|
||||
// Print out changed atom
|
||||
|
@ -499,24 +580,8 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
}
|
||||
|
||||
ps->pending_updates = true;
|
||||
// If WM_STATE changes
|
||||
if (ev->atom == ps->atoms->aWM_STATE) {
|
||||
// Check whether it could be a client window
|
||||
if (!find_toplevel(ps, ev->window)) {
|
||||
// Reset event mask anyway
|
||||
const uint32_t evmask =
|
||||
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN);
|
||||
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
|
||||
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
|
||||
|
||||
auto w_top = find_managed_window_or_parent(ps, ev->window);
|
||||
// ev->window might have not been managed yet, in that case w_top
|
||||
// would be NULL.
|
||||
if (w_top) {
|
||||
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
|
||||
}
|
||||
}
|
||||
return;
|
||||
ev_subwin_wm_state_changed(ps, ev);
|
||||
}
|
||||
|
||||
// If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
|
||||
|
|
|
@ -30,7 +30,7 @@ setup_window(struct x_connection *c, struct atom *atoms, struct options *options
|
|||
struct managed_win *w = ccalloc(1, struct managed_win);
|
||||
w->state = WSTATE_MAPPED;
|
||||
w->base.id = target;
|
||||
w->client_win = win_get_client_window(c, atoms, w);
|
||||
w->client_win = win_get_client_window(c, NULL, atoms, w);
|
||||
win_update_wintype(c, atoms, w);
|
||||
win_update_frame_extents(c, atoms, w, w->client_win, options->frame_opacity);
|
||||
// TODO(yshui) get leader
|
||||
|
|
11
src/picom.c
11
src/picom.c
|
@ -1725,7 +1725,7 @@ static void handle_pending_updates(EV_P_ struct session *ps) {
|
|||
// Catching up with X server
|
||||
handle_queued_x_events(EV_A_ & ps->event_check, 0);
|
||||
|
||||
// Call fill_win on new windows
|
||||
// Process new windows, and maybe allocate struct managed_win for them
|
||||
handle_new_windows(ps);
|
||||
|
||||
// Handle screen changes
|
||||
|
@ -1734,7 +1734,7 @@ static void handle_pending_updates(EV_P_ struct session *ps) {
|
|||
// stale.
|
||||
handle_root_flags(ps);
|
||||
|
||||
// Process window flags (window mapping)
|
||||
// Process window flags
|
||||
refresh_windows(ps);
|
||||
|
||||
{
|
||||
|
@ -2619,6 +2619,13 @@ static void session_destroy(session_t *ps) {
|
|||
}
|
||||
list_init_head(&ps->window_stack);
|
||||
|
||||
{
|
||||
struct subwin *subwin, *next_subwin;
|
||||
HASH_ITER(hh, ps->subwins, subwin, next_subwin) {
|
||||
remove_subwin(&ps->subwins, subwin);
|
||||
}
|
||||
}
|
||||
|
||||
// Free blacklists
|
||||
options_destroy(&ps->o);
|
||||
c2_state_free(ps->c2_state);
|
||||
|
|
|
@ -14,6 +14,8 @@ typedef enum {
|
|||
UNSET
|
||||
} switch_t;
|
||||
|
||||
enum tristate { TRI_FALSE = -1, TRI_UNKNOWN = 0, TRI_TRUE = 1 };
|
||||
|
||||
/// A structure representing margins around a rectangle.
|
||||
typedef struct {
|
||||
int top;
|
||||
|
|
102
src/win.c
102
src/win.c
|
@ -512,10 +512,13 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
|||
// Check client first, because later property updates need accurate client
|
||||
// window information
|
||||
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
|
||||
auto client_win = win_get_client_window(&ps->c, ps->atoms, w);
|
||||
log_debug("Rechecking client window for %#010x (%s)", w->base.id, w->name);
|
||||
auto client_win = win_get_client_window(&ps->c, ps->subwins, ps->atoms, w);
|
||||
if (w->client_win && w->client_win != client_win) {
|
||||
win_unmark_client(ps, w);
|
||||
win_unmark_client(w);
|
||||
}
|
||||
log_debug("New client window for %#010x (%s): %#010x", w->base.id,
|
||||
w->name, client_win);
|
||||
win_mark_client(ps, w, client_win);
|
||||
win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
|
||||
}
|
||||
|
@ -1440,15 +1443,6 @@ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t c
|
|||
return;
|
||||
}
|
||||
|
||||
auto e = xcb_request_check(
|
||||
ps->c.c, xcb_change_window_attributes_checked(
|
||||
ps->c.c, client, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)}));
|
||||
if (e) {
|
||||
log_error("Failed to change event mask of window %#010x", client);
|
||||
free(e);
|
||||
}
|
||||
|
||||
win_update_wintype(&ps->c, ps->atoms, w);
|
||||
|
||||
// Get frame widths. The window is in damaged area already.
|
||||
|
@ -1472,6 +1466,7 @@ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t c
|
|||
// Update everything related to conditions
|
||||
win_on_factor_change(ps, w);
|
||||
|
||||
xcb_generic_error_t *e = NULL;
|
||||
auto r = xcb_get_window_attributes_reply(
|
||||
ps->c.c, xcb_get_window_attributes(ps->c.c, w->client_win), &e);
|
||||
if (!r) {
|
||||
|
@ -1489,24 +1484,18 @@ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t c
|
|||
* @param ps current session
|
||||
* @param w struct _win of the parent window
|
||||
*/
|
||||
void win_unmark_client(session_t *ps, struct managed_win *w) {
|
||||
void win_unmark_client(struct managed_win *w) {
|
||||
xcb_window_t client = w->client_win;
|
||||
log_debug("Detaching client window %#010x from frame %#010x (%s)", client,
|
||||
w->base.id, w->name);
|
||||
|
||||
w->client_win = XCB_NONE;
|
||||
|
||||
// Recheck event mask
|
||||
xcb_change_window_attributes(
|
||||
ps->c.c, client, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for the client window of a particular window.
|
||||
*/
|
||||
static xcb_window_t
|
||||
find_client_win(struct x_connection *c, struct atom *atoms, xcb_window_t w) {
|
||||
static xcb_window_t find_client_win(struct x_connection *c, struct subwin *subwins,
|
||||
struct atom *atoms, xcb_window_t w) {
|
||||
xcb_query_tree_reply_t *reply =
|
||||
xcb_query_tree_reply(c->c, xcb_query_tree(c->c, w), NULL);
|
||||
if (!reply) {
|
||||
|
@ -1518,7 +1507,17 @@ find_client_win(struct x_connection *c, struct atom *atoms, xcb_window_t w) {
|
|||
xcb_window_t ret = XCB_NONE;
|
||||
|
||||
for (int i = 0; i < nchildren; ++i) {
|
||||
if (wid_has_prop(c->c, children[i], atoms->aWM_STATE)) {
|
||||
auto subwin = find_subwin(subwins, children[i]);
|
||||
bool has_wm_state;
|
||||
if (!subwin || subwin->has_wm_state == TRI_UNKNOWN) {
|
||||
has_wm_state = wid_has_prop(c->c, children[i], atoms->aWM_STATE);
|
||||
if (subwin) {
|
||||
subwin->has_wm_state = has_wm_state ? TRI_TRUE : TRI_FALSE;
|
||||
}
|
||||
} else {
|
||||
has_wm_state = subwin->has_wm_state == TRI_TRUE;
|
||||
}
|
||||
if (has_wm_state) {
|
||||
ret = children[i];
|
||||
break;
|
||||
}
|
||||
|
@ -1534,11 +1533,11 @@ find_client_win(struct x_connection *c, struct atom *atoms, xcb_window_t w) {
|
|||
* @param ps current session
|
||||
* @param w struct _win of the parent window
|
||||
*/
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct atom *atoms,
|
||||
const struct managed_win *w) {
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct subwin *subwins,
|
||||
struct atom *atoms, const struct managed_win *w) {
|
||||
// Always recursively look for a window with WM_STATE, as Fluxbox
|
||||
// sets override-redirect flags on all frame windows.
|
||||
xcb_window_t cw = find_client_win(c, atoms, w->base.id);
|
||||
xcb_window_t cw = find_client_win(c, subwins, atoms, w->base.id);
|
||||
if (cw) {
|
||||
log_debug("(%#010x): client %#010x", w->base.id, cw);
|
||||
} else {
|
||||
|
@ -1581,6 +1580,44 @@ void free_win_res(session_t *ps, struct managed_win *w) {
|
|||
c2_window_state_destroy(ps->c2_state, &w->c2_state);
|
||||
}
|
||||
|
||||
struct subwin *add_subwin_and_subscribe(struct subwin **subwins, struct x_connection *c,
|
||||
xcb_window_t id, xcb_window_t parent) {
|
||||
struct subwin *subwin = NULL;
|
||||
HASH_FIND_INT(*subwins, &id, subwin);
|
||||
BUG_ON(subwin != NULL);
|
||||
|
||||
subwin = ccalloc(1, struct subwin);
|
||||
subwin->id = id;
|
||||
subwin->toplevel = parent;
|
||||
HASH_ADD_INT(*subwins, id, subwin);
|
||||
|
||||
log_debug("Allocated subwin %p for window %#010x, total: %d", subwin, id,
|
||||
HASH_COUNT(*subwins));
|
||||
XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, id, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE});
|
||||
return subwin;
|
||||
}
|
||||
|
||||
struct subwin *find_subwin(struct subwin *subwins, xcb_window_t id) {
|
||||
struct subwin *subwin = NULL;
|
||||
HASH_FIND_INT(subwins, &id, subwin);
|
||||
return subwin;
|
||||
}
|
||||
|
||||
void remove_subwin(struct subwin **subwins, struct subwin *subwin) {
|
||||
log_debug("Freeing subwin %p for window %#010x", subwin, subwin->id);
|
||||
HASH_DEL(*subwins, subwin);
|
||||
free(subwin);
|
||||
}
|
||||
|
||||
void remove_subwin_and_unsubscribe(struct subwin **subwins, struct x_connection *c,
|
||||
struct subwin *subwin) {
|
||||
log_debug("Freeing subwin %p for window %#010x", subwin, subwin->id);
|
||||
XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, subwin->id, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){0});
|
||||
remove_subwin(subwins, subwin);
|
||||
}
|
||||
|
||||
/// Insert a new window after list_node `prev`
|
||||
/// New window will be in unmapped state
|
||||
static struct win *add_win(session_t *ps, xcb_window_t id, struct list_node *prev) {
|
||||
|
@ -1719,8 +1756,10 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi
|
|||
|
||||
w->is_new = false;
|
||||
|
||||
// Reject overlay window and already added windows
|
||||
// Reject overlay window
|
||||
if (w->id == ps->overlay) {
|
||||
// Would anyone reparent windows to the overlay window? Doing this
|
||||
// just in case.
|
||||
return w;
|
||||
}
|
||||
|
||||
|
@ -2319,9 +2358,20 @@ void restack_top(session_t *ps, struct win *w) {
|
|||
/// Start destroying a window. Windows cannot always be destroyed immediately
|
||||
/// because of fading and such.
|
||||
void destroy_win_start(session_t *ps, struct win *w) {
|
||||
auto mw = (struct managed_win *)w;
|
||||
assert(w);
|
||||
|
||||
{
|
||||
// A toplevel window is destroyed, all of its children lose their
|
||||
// subwin status.
|
||||
struct subwin *subwin, *next_subwin;
|
||||
HASH_ITER(hh, ps->subwins, subwin, next_subwin) {
|
||||
if (subwin->toplevel == w->id) {
|
||||
remove_subwin_and_unsubscribe(&ps->subwins, &ps->c, subwin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
log_debug("Destroying %#010x \"%s\", managed = %d", w->id,
|
||||
(w->managed ? mw->name : NULL), w->managed);
|
||||
|
||||
|
|
28
src/win.h
28
src/win.h
|
@ -21,6 +21,7 @@
|
|||
#include "utils.h"
|
||||
#include "win_defs.h"
|
||||
#include "x.h"
|
||||
#include "xcb/xproto.h"
|
||||
|
||||
struct backend_base;
|
||||
typedef struct session session_t;
|
||||
|
@ -88,6 +89,14 @@ struct win {
|
|||
bool managed : 1;
|
||||
};
|
||||
|
||||
/// Direct children of a toplevel.
|
||||
struct subwin {
|
||||
UT_hash_handle hh;
|
||||
xcb_window_t id;
|
||||
xcb_window_t toplevel;
|
||||
enum tristate has_wm_state;
|
||||
};
|
||||
|
||||
struct win_geometry {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
|
@ -332,7 +341,7 @@ void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t v
|
|||
void win_set_focused(session_t *ps, struct managed_win *w);
|
||||
bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w);
|
||||
void win_on_factor_change(session_t *ps, struct managed_win *w);
|
||||
void win_unmark_client(session_t *ps, struct managed_win *w);
|
||||
void win_unmark_client(struct managed_win *w);
|
||||
|
||||
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
|
||||
|
||||
|
@ -377,6 +386,14 @@ region_t win_get_region_frame_local_by_val(const struct managed_win *w);
|
|||
struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below);
|
||||
/// Insert a new win entry at the top of the stack
|
||||
struct win *add_win_top(session_t *ps, xcb_window_t id);
|
||||
/// Add a new subwin, and subscribe to relevant events.
|
||||
struct subwin *add_subwin_and_subscribe(struct subwin **subwins, struct x_connection *c,
|
||||
xcb_window_t id, xcb_window_t parent);
|
||||
struct subwin *find_subwin(struct subwin *subwins, xcb_window_t id);
|
||||
void remove_subwin(struct subwin **subwins, struct subwin *subwin);
|
||||
/// Remove a subwin, and unsubscribe from events.
|
||||
void remove_subwin_and_unsubscribe(struct subwin **subwins, struct x_connection *c,
|
||||
struct subwin *subwin);
|
||||
/// Query the Xorg for information about window `win`
|
||||
/// `win` pointer might become invalid after this function returns
|
||||
struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct win *win);
|
||||
|
@ -428,6 +445,11 @@ static inline bool attr_pure win_is_wmwin(const struct managed_win *w) {
|
|||
return w->base.id == w->client_win && !w->a.override_redirect;
|
||||
}
|
||||
|
||||
static inline struct managed_win *win_as_managed(struct win *w) {
|
||||
BUG_ON(!w->managed);
|
||||
return (struct managed_win *)w;
|
||||
}
|
||||
|
||||
/// check if reg_ignore_valid is true for all windows above us
|
||||
bool attr_pure win_is_region_ignore_valid(session_t *ps, const struct managed_win *w);
|
||||
|
||||
|
@ -448,8 +470,8 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags);
|
|||
/// Mark properties as stale for a window
|
||||
void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *prop, int nprops);
|
||||
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct atom *atoms,
|
||||
const struct managed_win *w);
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct subwin *subwins,
|
||||
struct atom *atoms, const struct managed_win *w);
|
||||
bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct managed_win *w);
|
||||
/**
|
||||
* Retrieve frame extents from a window.
|
||||
|
|
Loading…
Reference in New Issue