mirror of https://github.com/yshui/picom.git
wm: separate out window management into a file
Conversion is mostly mechanical, except a couple `ps->pending_updates = true` is added. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
fa0a89c35a
commit
a5ea66a134
17
src/common.h
17
src/common.h
|
@ -288,22 +288,7 @@ typedef struct session {
|
|||
/// Index of the next free slot in <code>expose_rects</code>.
|
||||
int n_expose;
|
||||
|
||||
// === 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
|
||||
/// EWMH <code>_NET_ACTIVE_WINDOW</code> focus detection. In theory,
|
||||
/// it's more reliable to store the window ID directly here, just in
|
||||
/// case the WM does something extraordinary, but caching the pointer
|
||||
/// means another layer of complexity.
|
||||
struct managed_win *active_win;
|
||||
/// Window ID of leader window of currently active window. Used for
|
||||
/// subsidiary window detection.
|
||||
xcb_window_t active_leader;
|
||||
struct wm *wm;
|
||||
|
||||
// === Shadow/dimming related ===
|
||||
/// 1x1 black Picture.
|
||||
|
|
112
src/dbus.c
112
src/dbus.c
|
@ -32,6 +32,7 @@
|
|||
#include "utils.h"
|
||||
#include "win.h"
|
||||
#include "win_defs.h"
|
||||
#include "wm.h"
|
||||
|
||||
#include "dbus.h"
|
||||
|
||||
|
@ -470,41 +471,41 @@ static bool cdbus_append_string_variant(DBusMessage *msg, const char *data) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static int cdbus_append_wids_callback(struct win *w, void *data) {
|
||||
DBusMessageIter *iter = data;
|
||||
cdbus_window_t wid = w->id;
|
||||
if (!dbus_message_iter_append_basic(iter, CDBUS_TYPE_WINDOW, &wid)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Append all window IDs in the window list of a session to a D-Bus message
|
||||
static bool cdbus_append_wids(DBusMessage *msg, session_t *ps) {
|
||||
// Get the number of wids we are to include
|
||||
unsigned count = 0;
|
||||
HASH_ITER2(ps->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
++count;
|
||||
}
|
||||
|
||||
unsigned count = wm_num_windows(ps->wm);
|
||||
if (!count) {
|
||||
// Nothing to append
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allocate memory for an array of window IDs
|
||||
auto arr = ccalloc(count, cdbus_window_t);
|
||||
|
||||
// Build the array
|
||||
cdbus_window_t *pcur = arr;
|
||||
HASH_ITER2(ps->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
*pcur = w->id;
|
||||
++pcur;
|
||||
}
|
||||
assert(pcur == arr + count);
|
||||
|
||||
// Append arguments
|
||||
if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, &arr,
|
||||
count, DBUS_TYPE_INVALID)) {
|
||||
log_error("Failed to append argument.");
|
||||
free(arr);
|
||||
DBusMessageIter it, subit;
|
||||
dbus_message_iter_init_append(msg, &it);
|
||||
if (!dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY,
|
||||
DBUS_TYPE_UINT32_AS_STRING, &subit)) {
|
||||
log_error("Failed to open container.");
|
||||
return false;
|
||||
}
|
||||
|
||||
free(arr);
|
||||
auto result = wm_foreach(ps->wm, cdbus_append_wids_callback, &subit);
|
||||
if (!dbus_message_iter_close_container(&it, &subit)) {
|
||||
log_error("Failed to close container.");
|
||||
return false;
|
||||
}
|
||||
if (result != 0) {
|
||||
log_error("Failed to append argument.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -587,7 +588,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = find_managed_win(ps, wid);
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
if (!w) {
|
||||
log_debug("Window %#010x not found.", wid);
|
||||
|
@ -614,7 +615,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
|
|||
|
||||
if (!strcmp("Next", target)) {
|
||||
cdbus_window_t next_id = 0;
|
||||
if (!list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)) {
|
||||
if (!list_node_is_last(wm_stack_end(ps->wm), &w->base.stack_neighbour)) {
|
||||
next_id = list_entry(w->base.stack_neighbour.next, struct win,
|
||||
stack_neighbour)
|
||||
->id;
|
||||
|
@ -674,7 +675,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = find_managed_win(ps, wid);
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
if (!w) {
|
||||
log_debug("Window %#010x not found.", wid);
|
||||
|
@ -693,7 +694,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
|
||||
if (!strcmp("next", target)) {
|
||||
xcb_window_t next_id =
|
||||
list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)
|
||||
list_node_is_last(wm_stack_end(ps->wm), &w->base.stack_neighbour)
|
||||
? 0
|
||||
: list_entry(w->base.stack_neighbour.next, struct win, stack_neighbour)
|
||||
->id;
|
||||
|
@ -759,7 +760,7 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = find_managed_win(ps, wid);
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
if (!w) {
|
||||
log_debug("Window %#010x not found.", wid);
|
||||
|
@ -816,14 +817,15 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
|
|||
dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
auto w = find_toplevel(ps, client);
|
||||
auto w = wm_find_by_client(ps->wm, client);
|
||||
if (w) {
|
||||
wid = w->base.id;
|
||||
}
|
||||
} else if (!strcmp("focused", target)) {
|
||||
// Find focused window
|
||||
if (ps->active_win && ps->active_win->state != WSTATE_UNMAPPED) {
|
||||
wid = ps->active_win->base.id;
|
||||
auto active_win = wm_active_win(ps->wm);
|
||||
if (active_win && active_win->state != WSTATE_UNMAPPED) {
|
||||
wid = active_win->base.id;
|
||||
}
|
||||
} else {
|
||||
log_debug(CDBUS_ERROR_BADTGT_S, target);
|
||||
|
@ -1066,6 +1068,21 @@ static DBusHandlerResult cdbus_process_introspect(DBusMessage *reply) {
|
|||
}
|
||||
///@}
|
||||
|
||||
static int cdbus_process_windows_root_introspect_callback(struct win *w, void *data) {
|
||||
char **introspect = data;
|
||||
if (!w->managed) {
|
||||
return 0;
|
||||
}
|
||||
char *tmp = NULL;
|
||||
if (asprintf(&tmp, "<node name='%#010x'/>\n", w->id) < 0) {
|
||||
log_fatal("Failed to allocate memory.");
|
||||
return 1;
|
||||
}
|
||||
mstrextend(introspect, tmp);
|
||||
free(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an D-Bus Introspect request, for /windows.
|
||||
*/
|
||||
|
@ -1086,31 +1103,16 @@ cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *reply) {
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
char *ret = NULL;
|
||||
bool success = true;
|
||||
mstrextend(&ret, str_introspect);
|
||||
|
||||
HASH_ITER2(ps->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
if (!w->managed) {
|
||||
continue;
|
||||
}
|
||||
char *tmp = NULL;
|
||||
if (asprintf(&tmp, "<node name='%#010x'/>\n", w->id) < 0) {
|
||||
log_fatal("Failed to allocate memory.");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
mstrextend(&ret, tmp);
|
||||
free(tmp);
|
||||
scoped_charp introspect = NULL;
|
||||
mstrextend(&introspect, str_introspect);
|
||||
if (wm_foreach(ps->wm, cdbus_process_windows_root_introspect_callback, &introspect)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
mstrextend(&ret, "</node>");
|
||||
|
||||
if (success) {
|
||||
success = cdbus_append_string(reply, ret);
|
||||
mstrextend(&introspect, "</node>");
|
||||
if (!cdbus_append_string(reply, introspect)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
free(ret);
|
||||
return success ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static bool cdbus_process_window_introspect(DBusMessage *reply) {
|
||||
|
|
73
src/event.c
73
src/event.c
|
@ -24,6 +24,7 @@
|
|||
#include "utils.h"
|
||||
#include "win.h"
|
||||
#include "win_defs.h"
|
||||
#include "wm.h"
|
||||
#include "x.h"
|
||||
|
||||
/// Event handling with X is complicated. Handling events with other events possibly
|
||||
|
@ -73,9 +74,9 @@ static inline const char *ev_window_name(session_t *ps, xcb_window_t wid) {
|
|||
} else if (ps->overlay == wid) {
|
||||
name = "(Overlay)";
|
||||
} else {
|
||||
auto w = find_managed_win(ps, wid);
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
if (!w) {
|
||||
w = find_toplevel(ps, wid);
|
||||
w = wm_find_by_client(ps->wm, wid);
|
||||
}
|
||||
|
||||
if (w && w->name) {
|
||||
|
@ -196,11 +197,12 @@ 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);
|
||||
wm_stack_add_top(ps->wm, ev->window);
|
||||
ps->pending_updates = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto w = find_managed_win(ps, ev->parent);
|
||||
auto w = wm_find_managed(ps->wm, 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
|
||||
|
@ -209,7 +211,7 @@ static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev
|
|||
}
|
||||
// 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);
|
||||
wm_subwin_add_and_subscribe(ps->wm, &ps->c, ev->window, ev->parent);
|
||||
if (w->client_win == XCB_NONE || w->client_win == w->base.id) {
|
||||
win_set_flags(w, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
|
@ -218,20 +220,20 @@ static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev
|
|||
|
||||
/// Handle configure event of a regular window
|
||||
static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||
auto w = find_win(ps, ce->window);
|
||||
auto w = wm_find(ps->wm, ce->window);
|
||||
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!w->managed) {
|
||||
restack_above(ps, w, ce->above_sibling);
|
||||
wm_stack_move_above(ps->wm, w, ce->above_sibling);
|
||||
return;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
|
||||
restack_above(ps, w, ce->above_sibling);
|
||||
wm_stack_move_above(ps->wm, w, ce->above_sibling);
|
||||
|
||||
// We check against pending_g here, because there might have been multiple
|
||||
// configure notifies in this cycle, or the window could receive multiple updates
|
||||
|
@ -285,13 +287,13 @@ 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);
|
||||
auto subwin = wm_subwin_find(ps->wm, ev->window);
|
||||
if (subwin) {
|
||||
remove_subwin(&ps->subwins, subwin);
|
||||
wm_subwin_remove(ps->wm, subwin);
|
||||
}
|
||||
|
||||
auto w = find_win(ps, ev->window);
|
||||
auto mw = find_toplevel(ps, ev->window);
|
||||
auto w = wm_find(ps->wm, ev->window);
|
||||
auto mw = wm_find_by_client(ps->wm, ev->window);
|
||||
if (mw && mw->client_win == mw->base.id) {
|
||||
// We only want _real_ frame window
|
||||
assert(&mw->base == w);
|
||||
|
@ -335,7 +337,7 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto w = find_managed_win(ps, ev->window);
|
||||
auto w = wm_find_managed(ps->wm, ev->window);
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
|
@ -355,7 +357,7 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
|
|||
}
|
||||
|
||||
static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) {
|
||||
auto w = find_managed_win(ps, ev->window);
|
||||
auto w = wm_find_managed(ps->wm, ev->window);
|
||||
if (w) {
|
||||
unmap_win_start(ps, w);
|
||||
}
|
||||
|
@ -365,10 +367,10 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||
log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
|
||||
ev->window, ev->parent, ev->override_redirect);
|
||||
|
||||
auto old_toplevel = find_toplevel(ps, ev->window);
|
||||
auto old_w = find_win(ps, ev->window);
|
||||
auto old_subwin = find_subwin(ps->subwins, ev->window);
|
||||
auto new_toplevel = find_managed_win(ps, ev->parent);
|
||||
auto old_toplevel = wm_find_by_client(ps->wm, ev->window);
|
||||
auto old_w = wm_find(ps->wm, ev->window);
|
||||
auto old_subwin = wm_subwin_find(ps->wm, ev->window);
|
||||
auto new_toplevel = wm_find_managed(ps->wm, ev->parent);
|
||||
xcb_window_t old_parent = XCB_NONE;
|
||||
if (old_subwin) {
|
||||
old_parent = old_subwin->toplevel;
|
||||
|
@ -402,7 +404,9 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||
// 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);
|
||||
log_debug("Restack %#010x (%s) to top", old_w->id,
|
||||
win_get_name_if_managed(old_w));
|
||||
wm_stack_move_to_top(ps->wm, old_w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -441,11 +445,11 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||
// 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);
|
||||
wm_subwin_remove_and_unsubscribe(ps->wm, &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);
|
||||
wm_subwin_add_and_subscribe(ps->wm, &ps->c, ev->window, ev->parent);
|
||||
}
|
||||
if (new_subwin) {
|
||||
new_subwin->toplevel = new_toplevel->base.id;
|
||||
|
@ -460,21 +464,24 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||
// New parent is root, add a toplevel;
|
||||
assert(old_w == NULL);
|
||||
assert(new_toplevel == NULL);
|
||||
add_win_top(ps, ev->window);
|
||||
wm_stack_add_top(ps->wm, ev->window);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
|
||||
auto w = find_win(ps, ev->window);
|
||||
auto w = wm_find(ps->wm, ev->window);
|
||||
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("Moving window %#010x (%s) to the %s", w->id,
|
||||
win_get_name_if_managed(w), ev->place == PlaceOnTop ? "top" : "bottom");
|
||||
if (ev->place == PlaceOnTop) {
|
||||
restack_top(ps, w);
|
||||
wm_stack_move_to_top(ps->wm, w);
|
||||
} else {
|
||||
restack_bottom(ps, w);
|
||||
wm_stack_move_to_bottom(ps->wm, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,7 +521,7 @@ 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);
|
||||
auto subwin = wm_subwin_find(ps->wm, ev->window);
|
||||
if (!subwin) {
|
||||
// We only care if a direct child of a toplevel gained/lost WM_STATE
|
||||
return;
|
||||
|
@ -531,7 +538,7 @@ static inline void ev_subwin_wm_state_changed(session_t *ps, xcb_property_notify
|
|||
return;
|
||||
}
|
||||
|
||||
auto toplevel = find_win(ps, subwin->toplevel);
|
||||
auto toplevel = wm_find(ps->wm, subwin->toplevel);
|
||||
BUG_ON(toplevel == NULL);
|
||||
if (!toplevel->managed) {
|
||||
return;
|
||||
|
@ -589,7 +596,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
}
|
||||
|
||||
ps->pending_updates = true;
|
||||
auto w = find_toplevel(ps, ev->window);
|
||||
auto w = wm_find_by_client(ps->wm, ev->window);
|
||||
if (ev->atom == ps->atoms->aWM_STATE) {
|
||||
ev_subwin_wm_state_changed(ps, ev);
|
||||
}
|
||||
|
@ -607,7 +614,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
// We already handle if this is set on the client window, check
|
||||
// if this is set on the frame window as well.
|
||||
// TODO(yshui) do we really need this?
|
||||
auto toplevel = find_managed_win(ps, ev->window);
|
||||
auto toplevel = wm_find_managed(ps->wm, ev->window);
|
||||
if (toplevel) {
|
||||
win_set_property_stale(toplevel, ev->atom);
|
||||
}
|
||||
|
@ -616,9 +623,9 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
// Check for other atoms we are tracking
|
||||
if (c2_state_is_property_tracked(ps->c2_state, ev->atom)) {
|
||||
bool change_is_on_client = false;
|
||||
w = find_managed_win(ps, ev->window);
|
||||
w = wm_find_managed(ps->wm, ev->window);
|
||||
if (!w) {
|
||||
w = find_toplevel(ps, ev->window);
|
||||
w = wm_find_by_client(ps->wm, ev->window);
|
||||
change_is_on_client = true;
|
||||
}
|
||||
if (w) {
|
||||
|
@ -700,7 +707,7 @@ static inline void ev_damage_notify(session_t *ps, xcb_damage_notify_event_t *de
|
|||
return;
|
||||
} */
|
||||
|
||||
auto w = find_managed_win(ps, de->drawable);
|
||||
auto w = wm_find_managed(ps->wm, de->drawable);
|
||||
|
||||
if (!w) {
|
||||
return;
|
||||
|
@ -710,7 +717,7 @@ static inline void ev_damage_notify(session_t *ps, xcb_damage_notify_event_t *de
|
|||
}
|
||||
|
||||
static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev) {
|
||||
auto w = find_managed_win(ps, ev->affected_window);
|
||||
auto w = wm_find_managed(ps->wm, ev->affected_window);
|
||||
if (!w || w->a.map_state == XCB_MAP_STATE_UNMAPPED) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ base_deps = [
|
|||
srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
|
||||
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
|
||||
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c',
|
||||
'vblank.c', 'transition.c') ]
|
||||
'vblank.c', 'transition.c', 'wm.c') ]
|
||||
picom_inc = include_directories('.')
|
||||
|
||||
cflags = []
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "uthash_extra.h"
|
||||
#include "utils.h"
|
||||
#include "win.h"
|
||||
#include "wm.h"
|
||||
|
||||
#include "opengl.h"
|
||||
|
||||
|
@ -241,7 +242,7 @@ void glx_destroy(session_t *ps) {
|
|||
}
|
||||
|
||||
// Free all GLX resources of windows
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
free_win_res_glx(ps, w);
|
||||
}
|
||||
|
||||
|
|
98
src/picom.c
98
src/picom.c
|
@ -50,6 +50,7 @@
|
|||
#include "picom.h"
|
||||
#include "transition.h"
|
||||
#include "win_defs.h"
|
||||
#include "wm.h"
|
||||
#ifdef CONFIG_OPENGL
|
||||
#include "opengl.h"
|
||||
#endif
|
||||
|
@ -509,7 +510,7 @@ void update_ewmh_active_win(session_t *ps) {
|
|||
// Search for the window
|
||||
xcb_window_t wid = wid_get_prop_window(&ps->c, ps->c.screen_info->root,
|
||||
ps->atoms->a_NET_ACTIVE_WINDOW);
|
||||
auto w = find_toplevel(ps, wid);
|
||||
auto w = wm_find_by_client(ps->wm, wid);
|
||||
|
||||
// Mark the window focused. No need to unfocus the previous one.
|
||||
if (w) {
|
||||
|
@ -571,7 +572,7 @@ static void recheck_focus(session_t *ps) {
|
|||
wid = parent;
|
||||
}
|
||||
|
||||
auto w = find_managed_win(ps, wid);
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
// And we set the focus state here
|
||||
if (w) {
|
||||
|
@ -602,7 +603,7 @@ static void rebuild_shadow_exclude_reg(session_t *ps) {
|
|||
|
||||
/// Free up all the images and deinit the backend
|
||||
static void destroy_backend(session_t *ps) {
|
||||
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
// Wrapping up fading in progress
|
||||
win_skip_fading(w);
|
||||
|
||||
|
@ -691,6 +692,20 @@ static bool initialize_blur(session_t *ps) {
|
|||
return ps->backend_blur_context != NULL;
|
||||
}
|
||||
|
||||
static int mark_pixmap_stale(struct win *w, void *data) {
|
||||
struct session *ps = data;
|
||||
if (!w->managed) {
|
||||
return 0;
|
||||
}
|
||||
auto mw = win_as_managed(w);
|
||||
assert(mw->state != WSTATE_DESTROYED);
|
||||
// We need to reacquire image
|
||||
log_debug("Marking window %#010x (%s) for update after redirection", w->id, mw->name);
|
||||
win_set_flags(mw, WIN_FLAGS_PIXMAP_STALE);
|
||||
ps->pending_updates = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Init the backend and bind all the window pixmap to backend images
|
||||
static bool initialize_backend(session_t *ps) {
|
||||
if (!ps->o.legacy_backends) {
|
||||
|
@ -739,22 +754,9 @@ static bool initialize_backend(session_t *ps) {
|
|||
}
|
||||
}
|
||||
|
||||
// window_stack shouldn't include window that's
|
||||
// not in the hash table at this point. Since
|
||||
// there cannot be any fading windows.
|
||||
HASH_ITER2(ps->windows, _w) {
|
||||
if (!_w->managed) {
|
||||
continue;
|
||||
}
|
||||
auto w = (struct managed_win *)_w;
|
||||
assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
|
||||
// We need to reacquire image
|
||||
log_debug("Marking window %#010x (%s) for update after "
|
||||
"redirection",
|
||||
w->base.id, w->name);
|
||||
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
// wm_stack shouldn't include window that's not iterated by wm_foreach at
|
||||
// this moment. Since there cannot be any fading windows.
|
||||
wm_foreach(ps->wm, mark_pixmap_stale, ps);
|
||||
}
|
||||
|
||||
// The old backends binds pixmap lazily, nothing to do here
|
||||
|
@ -808,7 +810,7 @@ static void configure_root(session_t *ps) {
|
|||
rebuild_shadow_exclude_reg(ps);
|
||||
|
||||
// Invalidate reg_ignore from the top
|
||||
auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
|
||||
auto top_w = wm_stack_next_managed(ps->wm, wm_stack_end(ps->wm));
|
||||
if (top_w) {
|
||||
rc_region_unref(&top_w->reg_ignore);
|
||||
top_w->reg_ignore_valid = false;
|
||||
|
@ -817,7 +819,7 @@ static void configure_root(session_t *ps) {
|
|||
// Whether a window is fullscreen depends on the new screen
|
||||
// size. So we need to refresh the fullscreen state of all
|
||||
// windows.
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
win_update_is_fullscreen(ps, w);
|
||||
}
|
||||
|
||||
|
@ -902,7 +904,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
|
|||
// First, let's process fading, and animated shaders
|
||||
// TODO(yshui) check if a window is fully obscured, and if we don't need to
|
||||
// process fading or animation for it.
|
||||
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
const winmode_t mode_old = w->mode;
|
||||
const bool was_painted = w->to_paint;
|
||||
|
||||
|
@ -959,7 +961,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
|
|||
// Track whether it's the highest window to paint
|
||||
bool is_highest = true;
|
||||
bool reg_ignore_valid = true;
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
__label__ skip_window;
|
||||
bool to_paint = true;
|
||||
// w->to_paint remembers whether this window is painted last time
|
||||
|
@ -1640,7 +1642,7 @@ static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents
|
|||
}
|
||||
|
||||
static void handle_new_windows(session_t *ps) {
|
||||
list_foreach_safe(struct win, w, &ps->window_stack, stack_neighbour) {
|
||||
list_foreach_safe(struct win, w, wm_stack_end(ps->wm), stack_neighbour) {
|
||||
if (w->is_new) {
|
||||
auto new_w = maybe_allocate_managed_win(ps, w);
|
||||
if (new_w == w) {
|
||||
|
@ -1648,11 +1650,7 @@ static void handle_new_windows(session_t *ps) {
|
|||
}
|
||||
|
||||
assert(new_w->managed);
|
||||
list_replace(&w->stack_neighbour, &new_w->stack_neighbour);
|
||||
struct win *replaced = NULL;
|
||||
HASH_REPLACE_INT(ps->windows, id, new_w, replaced);
|
||||
assert(replaced == w);
|
||||
free(w);
|
||||
wm_stack_replace(ps->wm, w, new_w);
|
||||
|
||||
auto mw = (struct managed_win *)new_w;
|
||||
if (mw->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
|
@ -1672,13 +1670,13 @@ static void handle_new_windows(session_t *ps) {
|
|||
}
|
||||
|
||||
static void refresh_windows(session_t *ps) {
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
win_process_update_flags(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_images(session_t *ps) {
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
win_process_image_flags(ps, w);
|
||||
}
|
||||
}
|
||||
|
@ -1775,7 +1773,7 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
|||
//
|
||||
// Using foreach_safe here since skipping fading can cause window to be
|
||||
// freed if it's destroyed.
|
||||
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
win_skip_fading(w);
|
||||
if (w->state == WSTATE_DESTROYED) {
|
||||
destroy_win_finish(ps, &w->base);
|
||||
|
@ -1785,7 +1783,7 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
|||
|
||||
if (ps->o.benchmark) {
|
||||
if (ps->o.benchmark_wid) {
|
||||
auto w = find_managed_win(ps, ps->o.benchmark_wid);
|
||||
auto w = wm_find_managed(ps->wm, ps->o.benchmark_wid);
|
||||
if (!w) {
|
||||
log_fatal("Couldn't find specified benchmark window.");
|
||||
exit(1);
|
||||
|
@ -2022,10 +2020,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
.size_expose = 0,
|
||||
.n_expose = 0,
|
||||
|
||||
.windows = NULL,
|
||||
.active_win = NULL,
|
||||
.active_leader = XCB_NONE,
|
||||
|
||||
.black_picture = XCB_NONE,
|
||||
.cshadow_picture = XCB_NONE,
|
||||
.white_picture = XCB_NONE,
|
||||
|
@ -2072,7 +2066,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
// Allocate a session and copy default values into it
|
||||
session_t *ps = cmalloc(session_t);
|
||||
*ps = s_def;
|
||||
list_init_head(&ps->window_stack);
|
||||
ps->loop = EV_DEFAULT;
|
||||
pixman_region32_init(&ps->screen_reg);
|
||||
|
||||
|
@ -2525,6 +2518,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
|
||||
ps->server_grabbed = false;
|
||||
|
||||
ps->wm = wm_new();
|
||||
if (query_tree_reply) {
|
||||
xcb_window_t *children;
|
||||
int nchildren;
|
||||
|
@ -2533,13 +2527,13 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
nchildren = xcb_query_tree_children_length(query_tree_reply);
|
||||
|
||||
for (int i = 0; i < nchildren; i++) {
|
||||
add_win_above(ps, children[i], i ? children[i - 1] : XCB_NONE);
|
||||
wm_stack_add_above(ps->wm, children[i], i ? children[i - 1] : XCB_NONE);
|
||||
}
|
||||
free(query_tree_reply);
|
||||
}
|
||||
|
||||
log_debug("Initial stack:");
|
||||
list_foreach(struct win, w, &ps->window_stack, stack_neighbour) {
|
||||
list_foreach(struct win, w, wm_stack_end(ps->wm), stack_neighbour) {
|
||||
log_debug("%#010x", w->id);
|
||||
}
|
||||
|
||||
|
@ -2586,27 +2580,8 @@ static void session_destroy(session_t *ps) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Free window linked list
|
||||
|
||||
list_foreach_safe(struct win, w, &ps->window_stack, stack_neighbour) {
|
||||
if (!w->destroyed) {
|
||||
win_ev_stop(ps, w);
|
||||
HASH_DEL(ps->windows, w);
|
||||
}
|
||||
|
||||
if (w->managed) {
|
||||
auto mw = (struct managed_win *)w;
|
||||
free_win_res(ps, mw);
|
||||
}
|
||||
free(w);
|
||||
}
|
||||
list_init_head(&ps->window_stack);
|
||||
|
||||
{
|
||||
struct subwin *subwin, *next_subwin;
|
||||
HASH_ITER(hh, ps->subwins, subwin, next_subwin) {
|
||||
remove_subwin(&ps->subwins, subwin);
|
||||
}
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
free_win_res(ps, w);
|
||||
}
|
||||
|
||||
// Free blacklists
|
||||
|
@ -2714,6 +2689,7 @@ static void session_destroy(session_t *ps) {
|
|||
ev_signal_stop(ps->loop, &ps->usr1_signal);
|
||||
ev_signal_stop(ps->loop, &ps->int_signal);
|
||||
|
||||
wm_free(ps->wm, &ps->c);
|
||||
free_x_connection(&ps->c);
|
||||
}
|
||||
|
||||
|
|
400
src/win.c
400
src/win.c
|
@ -33,6 +33,7 @@
|
|||
#include "uthash_extra.h"
|
||||
#include "utils.h"
|
||||
#include "win_defs.h"
|
||||
#include "wm.h"
|
||||
#include "x.h"
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
|
@ -96,15 +97,6 @@ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t c
|
|||
return ret; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear leader cache of all windows.
|
||||
*/
|
||||
static inline void clear_cache_win_leaders(session_t *ps) {
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
w->cache_leader = XCB_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions);
|
||||
|
||||
/**
|
||||
|
@ -138,13 +130,31 @@ static void win_update_focused(session_t *ps, struct managed_win *w) {
|
|||
|
||||
// If window grouping detection is enabled, mark the window active if
|
||||
// its group is
|
||||
if (ps->o.track_leader && ps->active_leader &&
|
||||
win_get_leader(ps, w) == ps->active_leader) {
|
||||
auto active_leader = wm_active_leader(ps->wm);
|
||||
if (ps->o.track_leader && active_leader &&
|
||||
win_get_leader(ps, w) == active_leader) {
|
||||
w->focused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct group_callback_data {
|
||||
struct session *ps;
|
||||
xcb_window_t leader;
|
||||
};
|
||||
|
||||
static inline int group_on_factor_change_callback(struct win *w, void *data_) {
|
||||
struct group_callback_data *data = data_;
|
||||
if (!w->managed) {
|
||||
return 0;
|
||||
}
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (data->leader == win_get_leader(data->ps, mw)) {
|
||||
win_on_factor_change(data->ps, mw);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run win_on_factor_change() on all windows with the same leader window.
|
||||
*
|
||||
|
@ -155,24 +165,23 @@ static inline void group_on_factor_change(session_t *ps, xcb_window_t leader) {
|
|||
return;
|
||||
}
|
||||
|
||||
HASH_ITER2(ps->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
if (!w->managed) {
|
||||
continue;
|
||||
}
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (win_get_leader(ps, mw) == leader) {
|
||||
win_on_factor_change(ps, mw);
|
||||
}
|
||||
}
|
||||
struct group_callback_data data = {
|
||||
.ps = ps,
|
||||
.leader = leader,
|
||||
};
|
||||
wm_foreach(ps->wm, group_on_factor_change_callback, &data);
|
||||
}
|
||||
|
||||
static inline const char *win_get_name_if_managed(const struct win *w) {
|
||||
static inline int group_is_focused_callback(struct win *w, void *data_) {
|
||||
struct group_callback_data *data = data_;
|
||||
if (!w->managed) {
|
||||
return "(unmanaged)";
|
||||
return 0;
|
||||
}
|
||||
auto mw = (struct managed_win *)w;
|
||||
return mw->name;
|
||||
if (data->leader == win_get_leader(data->ps, mw) && win_is_focused_raw(mw)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,18 +195,11 @@ static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
|
|||
return false;
|
||||
}
|
||||
|
||||
HASH_ITER2(ps->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
if (!w->managed) {
|
||||
continue;
|
||||
}
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (win_get_leader(ps, mw) == leader && win_is_focused_raw(mw)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
struct group_callback_data data = {
|
||||
.ps = ps,
|
||||
.leader = leader,
|
||||
};
|
||||
return wm_foreach(ps->wm, group_is_focused_callback, &data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -210,13 +212,15 @@ static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_wind
|
|||
|
||||
// Forcefully do this to deal with the case when a child window
|
||||
// gets mapped before parent, or when the window is a waypoint
|
||||
clear_cache_win_leaders(ps);
|
||||
win_stack_foreach_managed(i, wm_stack_end(ps->wm)) {
|
||||
i->cache_leader = XCB_NONE;
|
||||
}
|
||||
|
||||
// Update the old and new window group and active_leader if the
|
||||
// window could affect their state.
|
||||
xcb_window_t cache_leader = win_get_leader(ps, w);
|
||||
if (win_is_focused_raw(w) && cache_leader_old != cache_leader) {
|
||||
ps->active_leader = cache_leader;
|
||||
wm_set_active_leader(ps->wm, cache_leader);
|
||||
|
||||
group_on_factor_change(ps, cache_leader_old);
|
||||
group_on_factor_change(ps, cache_leader);
|
||||
|
@ -510,7 +514,7 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
|||
// window information
|
||||
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
|
||||
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);
|
||||
auto client_win = win_get_client_window(&ps->c, ps->wm, ps->atoms, w);
|
||||
if (w->client_win && w->client_win != client_win) {
|
||||
win_unmark_client(w);
|
||||
}
|
||||
|
@ -1491,8 +1495,8 @@ void win_unmark_client(struct managed_win *w) {
|
|||
/**
|
||||
* Look for the client window of a particular window.
|
||||
*/
|
||||
static xcb_window_t find_client_win(struct x_connection *c, struct subwin *subwins,
|
||||
struct atom *atoms, xcb_window_t w) {
|
||||
static xcb_window_t
|
||||
find_client_win(struct x_connection *c, struct wm *wm, 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) {
|
||||
|
@ -1504,9 +1508,9 @@ static xcb_window_t find_client_win(struct x_connection *c, struct subwin *subwi
|
|||
xcb_window_t ret = XCB_NONE;
|
||||
|
||||
for (int i = 0; i < nchildren; ++i) {
|
||||
auto subwin = find_subwin(subwins, children[i]);
|
||||
auto subwin = wm ? wm_subwin_find(wm, children[i]) : NULL;
|
||||
bool has_wm_state;
|
||||
assert(subwin != NULL || subwins == NULL);
|
||||
assert(subwin != NULL || wm == NULL);
|
||||
if (!subwin || subwin->has_wm_state == TRI_UNKNOWN) {
|
||||
has_wm_state = wid_has_prop(c->c, children[i], atoms->aWM_STATE);
|
||||
if (subwin) {
|
||||
|
@ -1531,11 +1535,11 @@ static xcb_window_t find_client_win(struct x_connection *c, struct subwin *subwi
|
|||
* @param ps current session
|
||||
* @param w struct _win of the parent window
|
||||
*/
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct subwin *subwins,
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct wm *wm,
|
||||
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, subwins, atoms, w->base.id);
|
||||
xcb_window_t cw = find_client_win(c, wm, atoms, w->base.id);
|
||||
if (cw) {
|
||||
log_debug("(%#010x): client %#010x", w->base.id, cw);
|
||||
} else {
|
||||
|
@ -1578,88 +1582,6 @@ 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) {
|
||||
log_debug("Adding window %#010x", id);
|
||||
struct win *old_w = NULL;
|
||||
HASH_FIND_INT(ps->windows, &id, old_w);
|
||||
assert(old_w == NULL);
|
||||
|
||||
auto new_w = cmalloc(struct win);
|
||||
list_insert_after(prev, &new_w->stack_neighbour);
|
||||
new_w->id = id;
|
||||
new_w->managed = false;
|
||||
new_w->is_new = true;
|
||||
new_w->destroyed = false;
|
||||
|
||||
HASH_ADD_INT(ps->windows, id, new_w);
|
||||
ps->pending_updates = true;
|
||||
return new_w;
|
||||
}
|
||||
|
||||
/// Insert a new win entry at the top of the stack
|
||||
struct win *add_win_top(session_t *ps, xcb_window_t id) {
|
||||
return add_win(ps, id, &ps->window_stack);
|
||||
}
|
||||
|
||||
/// Insert a new window above window with id `below`, if there is no window, add
|
||||
/// to top New window will be in unmapped state
|
||||
struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below) {
|
||||
struct win *w = NULL;
|
||||
HASH_FIND_INT(ps->windows, &below, w);
|
||||
if (!w) {
|
||||
if (!list_is_empty(&ps->window_stack)) {
|
||||
// `below` window is not found even if the window stack is
|
||||
// not empty
|
||||
return NULL;
|
||||
}
|
||||
return add_win_top(ps, id);
|
||||
}
|
||||
// we found something from the hash table, so if the stack is
|
||||
// empty, we are in an inconsistent state.
|
||||
assert(!list_is_empty(&ps->window_stack));
|
||||
return add_win(ps, id, w->stack_neighbour.prev);
|
||||
}
|
||||
|
||||
/// Query the Xorg for information about window `win`, and return that
|
||||
/// information in a new managed_win object. However, if the window does
|
||||
/// not need to be managed, the original `win` object is returned.
|
||||
|
@ -1761,7 +1683,7 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi
|
|||
return w;
|
||||
}
|
||||
|
||||
auto duplicated_win = find_managed_win(ps, w->id);
|
||||
auto duplicated_win = wm_find_managed(ps->wm, w->id);
|
||||
if (duplicated_win) {
|
||||
log_debug("Window %#010x (recorded name: %s) added multiple "
|
||||
"times",
|
||||
|
@ -1854,8 +1776,7 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi
|
|||
if (tree_reply) {
|
||||
auto children = xcb_query_tree_children(tree_reply);
|
||||
for (int i = 0; i < xcb_query_tree_children_length(tree_reply); i++) {
|
||||
add_subwin_and_subscribe(&ps->subwins, &ps->c, children[i],
|
||||
new->base.id);
|
||||
wm_subwin_add_and_subscribe(ps->wm, &ps->c, children[i], new->base.id);
|
||||
}
|
||||
free(tree_reply);
|
||||
}
|
||||
|
@ -1922,7 +1843,7 @@ static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int
|
|||
// If the leader of this window isn't itself, look for its
|
||||
// ancestors
|
||||
if (w->cache_leader && w->cache_leader != w->client_win) {
|
||||
auto wp = find_toplevel(ps, w->cache_leader);
|
||||
auto wp = wm_find_by_client(ps->wm, w->cache_leader);
|
||||
if (wp) {
|
||||
// Dead loop?
|
||||
if (recursions > WIN_GET_LEADER_MAX_RECURSION) {
|
||||
|
@ -1986,18 +1907,19 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
|
|||
xcb_window_t leader = win_get_leader(ps, w);
|
||||
|
||||
// If the window gets focused, replace the old active_leader
|
||||
if (win_is_focused_raw(w) && leader != ps->active_leader) {
|
||||
xcb_window_t active_leader_old = ps->active_leader;
|
||||
auto active_leader = wm_active_leader(ps->wm);
|
||||
if (win_is_focused_raw(w) && leader != active_leader) {
|
||||
xcb_window_t active_leader_old = active_leader;
|
||||
|
||||
ps->active_leader = leader;
|
||||
wm_set_active_leader(ps->wm, leader);
|
||||
|
||||
group_on_factor_change(ps, active_leader_old);
|
||||
group_on_factor_change(ps, leader);
|
||||
}
|
||||
// If the group get unfocused, remove it from active_leader
|
||||
else if (!win_is_focused_raw(w) && leader &&
|
||||
leader == ps->active_leader && !group_is_focused(ps, leader)) {
|
||||
ps->active_leader = XCB_NONE;
|
||||
else if (!win_is_focused_raw(w) && leader && leader == active_leader &&
|
||||
!group_is_focused(ps, leader)) {
|
||||
wm_set_active_leader(ps->wm, XCB_NONE);
|
||||
group_on_factor_change(ps, leader);
|
||||
}
|
||||
}
|
||||
|
@ -2024,13 +1946,13 @@ void win_set_focused(session_t *ps, struct managed_win *w) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto old_active_win = wm_active_win(ps->wm);
|
||||
if (w->is_ewmh_focused) {
|
||||
assert(ps->active_win == w);
|
||||
assert(old_active_win == w);
|
||||
return;
|
||||
}
|
||||
|
||||
auto old_active_win = ps->active_win;
|
||||
ps->active_win = w;
|
||||
wm_set_active_win(ps->wm, w);
|
||||
w->is_ewmh_focused = true;
|
||||
|
||||
if (old_active_win) {
|
||||
|
@ -2196,7 +2118,7 @@ void win_update_frame_extents(struct x_connection *c, struct atom *atoms,
|
|||
}
|
||||
|
||||
bool win_is_region_ignore_valid(session_t *ps, const struct managed_win *w) {
|
||||
win_stack_foreach_managed(i, &ps->window_stack) {
|
||||
win_stack_foreach_managed(i, wm_stack_end(ps->wm)) {
|
||||
if (i == w) {
|
||||
break;
|
||||
}
|
||||
|
@ -2207,33 +2129,12 @@ bool win_is_region_ignore_valid(session_t *ps, const struct managed_win *w) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening for events on a particular window.
|
||||
*/
|
||||
void win_ev_stop(session_t *ps, const struct win *w) {
|
||||
xcb_change_window_attributes(ps->c.c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0});
|
||||
|
||||
if (!w->managed) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (mw->client_win) {
|
||||
xcb_change_window_attributes(ps->c.c, mw->client_win, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){0});
|
||||
}
|
||||
|
||||
if (ps->shape_exists) {
|
||||
xcb_shape_select_input(ps->c.c, w->id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Finish the destruction of a window (e.g. after fading has finished).
|
||||
/// Frees `w`
|
||||
void destroy_win_finish(session_t *ps, struct win *w) {
|
||||
log_verbose("Trying to finish destroying (%#010x)", w->id);
|
||||
|
||||
auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
|
||||
auto next_w = wm_stack_next_managed(ps->wm, &w->stack_neighbour);
|
||||
list_remove(&w->stack_neighbour);
|
||||
|
||||
if (w->managed) {
|
||||
|
@ -2256,7 +2157,7 @@ void destroy_win_finish(session_t *ps, struct win *w) {
|
|||
next_w->reg_ignore_valid = false;
|
||||
}
|
||||
|
||||
if (mw == ps->active_win) {
|
||||
if (mw == wm_active_win(ps->wm)) {
|
||||
// Usually, the window cannot be the focused at
|
||||
// destruction. FocusOut should be generated before the
|
||||
// window is destroyed. We do this check just to be
|
||||
|
@ -2264,7 +2165,7 @@ void destroy_win_finish(session_t *ps, struct win *w) {
|
|||
log_debug("window %#010x (%s) is destroyed while being "
|
||||
"focused",
|
||||
w->id, mw->name);
|
||||
ps->active_win = NULL;
|
||||
wm_set_active_win(ps->wm, NULL);
|
||||
}
|
||||
|
||||
free_win_res(ps, mw);
|
||||
|
@ -2272,7 +2173,7 @@ void destroy_win_finish(session_t *ps, struct win *w) {
|
|||
// Drop w from all prev_trans to avoid accessing freed memory in
|
||||
// repair_win()
|
||||
// TODO(yshui) there can only be one prev_trans pointing to w
|
||||
win_stack_foreach_managed(w2, &ps->window_stack) {
|
||||
win_stack_foreach_managed(w2, wm_stack_end(ps->wm)) {
|
||||
if (mw == w2->prev_trans) {
|
||||
w2->prev_trans = NULL;
|
||||
}
|
||||
|
@ -2282,106 +2183,14 @@ void destroy_win_finish(session_t *ps, struct win *w) {
|
|||
free(w);
|
||||
}
|
||||
|
||||
/// Move window `w` so it's before `next` in the list
|
||||
static inline void restack_win(session_t *ps, struct win *w, struct list_node *next) {
|
||||
struct managed_win *mw = NULL;
|
||||
if (w->managed) {
|
||||
mw = (struct managed_win *)w;
|
||||
}
|
||||
|
||||
if (mw) {
|
||||
// This invalidates all reg_ignore below the new stack position of
|
||||
// `w`
|
||||
mw->reg_ignore_valid = false;
|
||||
rc_region_unref(&mw->reg_ignore);
|
||||
|
||||
// This invalidates all reg_ignore below the old stack position of
|
||||
// `w`
|
||||
auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
|
||||
if (next_w) {
|
||||
next_w->reg_ignore_valid = false;
|
||||
rc_region_unref(&next_w->reg_ignore);
|
||||
}
|
||||
}
|
||||
|
||||
list_move_before(&w->stack_neighbour, next);
|
||||
|
||||
// add damage for this window
|
||||
if (mw) {
|
||||
add_damage_from_win(ps, mw);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RESTACK
|
||||
log_trace("Window stack modified. Current stack:");
|
||||
for (auto c = ps->list; c; c = c->next) {
|
||||
const char *desc = "";
|
||||
if (c->state == WSTATE_DESTROYING) {
|
||||
desc = "(D) ";
|
||||
}
|
||||
log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Move window `w` so it's right above `below`
|
||||
void restack_above(session_t *ps, struct win *w, xcb_window_t below) {
|
||||
xcb_window_t old_below;
|
||||
|
||||
if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) {
|
||||
old_below = list_next_entry(w, stack_neighbour)->id;
|
||||
} else {
|
||||
old_below = XCB_NONE;
|
||||
}
|
||||
log_debug("Restack %#010x (%s), old_below: %#010x, new_below: %#010x", w->id,
|
||||
win_get_name_if_managed(w), old_below, below);
|
||||
|
||||
if (old_below != below) {
|
||||
struct list_node *new_next;
|
||||
if (!below) {
|
||||
new_next = &ps->window_stack;
|
||||
} else {
|
||||
struct win *tmp_w = NULL;
|
||||
HASH_FIND_INT(ps->windows, &below, tmp_w);
|
||||
|
||||
if (!tmp_w) {
|
||||
log_error("Failed to found new below window %#010x.", below);
|
||||
return;
|
||||
}
|
||||
|
||||
new_next = &tmp_w->stack_neighbour;
|
||||
}
|
||||
restack_win(ps, w, new_next);
|
||||
}
|
||||
}
|
||||
|
||||
void restack_bottom(session_t *ps, struct win *w) {
|
||||
restack_above(ps, w, 0);
|
||||
}
|
||||
|
||||
void restack_top(session_t *ps, struct win *w) {
|
||||
log_debug("Restack %#010x (%s) to top", w->id, win_get_name_if_managed(w));
|
||||
if (&w->stack_neighbour == ps->window_stack.next) {
|
||||
// already at top
|
||||
return;
|
||||
}
|
||||
restack_win(ps, w, ps->window_stack.next);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// A toplevel window is destroyed, all of its children lose their
|
||||
// subwin status.
|
||||
wm_subwin_remove_and_unsubscribe_for_toplevel(ps->wm, &ps->c, w->id);
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
log_debug("Destroying %#010x \"%s\", managed = %d", w->id,
|
||||
|
@ -2393,7 +2202,7 @@ void destroy_win_start(session_t *ps, struct win *w) {
|
|||
// stack if it's managed and mapped, since we might still need to render
|
||||
// it (e.g. fading out). Window will be removed from the stack when it
|
||||
// finishes destroying.
|
||||
HASH_DEL(ps->windows, w);
|
||||
wm_remove(ps->wm, w);
|
||||
|
||||
if (w->managed) {
|
||||
if (mw->state != WSTATE_UNMAPPED) {
|
||||
|
@ -2599,60 +2408,6 @@ void win_update_opacity_target(session_t *ps, struct managed_win *w) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a managed window from window id in window linked list of the session.
|
||||
*/
|
||||
struct win *find_win(session_t *ps, xcb_window_t id) {
|
||||
if (!id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct win *w = NULL;
|
||||
HASH_FIND_INT(ps->windows, &id, w);
|
||||
assert(w == NULL || !w->destroyed);
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a managed window from window id in window linked list of the session.
|
||||
*/
|
||||
struct managed_win *find_managed_win(session_t *ps, xcb_window_t id) {
|
||||
struct win *w = find_win(ps, id);
|
||||
if (!w || !w->managed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
assert(mw->state != WSTATE_DESTROYED);
|
||||
return mw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out the WM frame of a client window using existing data.
|
||||
*
|
||||
* @param id window ID
|
||||
* @return struct win object of the found window, NULL if not found
|
||||
*/
|
||||
struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) {
|
||||
if (!id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HASH_ITER2(ps->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
if (!w->managed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (mw->client_win == id) {
|
||||
return mw;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Set flags on a window. Some sanity checks are performed
|
||||
void win_set_flags(struct managed_win *w, uint64_t flags) {
|
||||
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
|
||||
|
@ -2774,16 +2529,3 @@ bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *
|
|||
bool win_is_focused_raw(const struct managed_win *w) {
|
||||
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && w->is_ewmh_focused;
|
||||
}
|
||||
|
||||
// Find the managed window immediately below `i` in the window stack
|
||||
struct managed_win *
|
||||
win_stack_find_next_managed(const session_t *ps, const struct list_node *w) {
|
||||
while (!list_node_is_last(&ps->window_stack, w)) {
|
||||
auto next = list_entry(w->next, struct win, stack_neighbour);
|
||||
if (next->managed) {
|
||||
return (struct managed_win *)next;
|
||||
}
|
||||
w = &next->stack_neighbour;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
52
src/win.h
52
src/win.h
|
@ -49,7 +49,7 @@ typedef struct {
|
|||
int height;
|
||||
} glx_blur_cache_t;
|
||||
#endif
|
||||
|
||||
struct wm;
|
||||
/// An entry in the window stack. May or may not correspond to a window we know about.
|
||||
struct window_stack_entry {
|
||||
struct list_node stack_neighbour;
|
||||
|
@ -89,14 +89,6 @@ 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;
|
||||
|
@ -381,46 +373,18 @@ void win_get_region_noframe_local_without_corners(const struct managed_win *w, r
|
|||
void win_get_region_frame_local(const struct managed_win *w, region_t *res);
|
||||
/// Get the region for the frame of the window, by value
|
||||
region_t win_get_region_frame_local_by_val(const struct managed_win *w);
|
||||
/// Insert a new window above window with id `below`, if there is no window, add to top
|
||||
/// New window will be in unmapped state
|
||||
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);
|
||||
/// Move window `w` to be right above `below`
|
||||
void restack_above(session_t *ps, struct win *w, xcb_window_t below);
|
||||
/// Move window `w` to the bottom of the stack
|
||||
void restack_bottom(session_t *ps, struct win *w);
|
||||
/// Move window `w` to the top of the stack
|
||||
void restack_top(session_t *ps, struct win *w);
|
||||
|
||||
/**
|
||||
* Release a destroyed window that is no longer needed.
|
||||
*/
|
||||
void destroy_win_finish(session_t *ps, struct win *w);
|
||||
|
||||
// Stop receiving events (except ConfigureNotify, XXX why?) from a window
|
||||
void win_ev_stop(session_t *ps, const struct win *w);
|
||||
|
||||
/// Skip the current in progress fading of window,
|
||||
/// transition the window straight to its end state
|
||||
void win_skip_fading(struct managed_win *w);
|
||||
/**
|
||||
* Find a managed window from window id in window linked list of the session.
|
||||
*/
|
||||
struct managed_win *find_managed_win(session_t *ps, xcb_window_t id);
|
||||
struct win *find_win(session_t *ps, xcb_window_t id);
|
||||
struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
|
||||
|
||||
/**
|
||||
* Check if a window is focused, without using any focus rules or forced focus settings
|
||||
|
@ -442,15 +406,19 @@ static inline struct managed_win *win_as_managed(struct win *w) {
|
|||
return (struct managed_win *)w;
|
||||
}
|
||||
|
||||
static inline const char *win_get_name_if_managed(const struct win *w) {
|
||||
if (!w->managed) {
|
||||
return "(unmanaged)";
|
||||
}
|
||||
auto mw = (struct managed_win *)w;
|
||||
return mw->name;
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Whether a given window is mapped on the X server side
|
||||
bool win_is_mapped_in_x(const struct managed_win *w);
|
||||
|
||||
// Find the managed window immediately below `w` in the window stack
|
||||
struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps,
|
||||
const struct list_node *w);
|
||||
/// Set flags on a window. Some sanity checks are performed
|
||||
void win_set_flags(struct managed_win *w, uint64_t flags);
|
||||
/// Clear flags on a window. Some sanity checks are performed
|
||||
|
@ -462,7 +430,7 @@ 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 subwin *subwins,
|
||||
xcb_window_t win_get_client_window(struct x_connection *c, struct wm *wm,
|
||||
struct atom *atoms, const struct managed_win *w);
|
||||
bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct managed_win *w);
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) Yuxuan Shui
|
||||
|
||||
#include <uthash.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "uthash_extra.h"
|
||||
#include "win.h"
|
||||
#include "wm.h"
|
||||
#include "x.h"
|
||||
|
||||
struct wm {
|
||||
/// A hash table of all windows.
|
||||
struct win *windows;
|
||||
/// Windows in their stacking order
|
||||
struct list_node window_stack;
|
||||
/// Pointer to <code>win</code> of current active window. Used by
|
||||
/// EWMH <code>_NET_ACTIVE_WINDOW</code> focus detection. In theory,
|
||||
/// it's more reliable to store the window ID directly here, just in
|
||||
/// case the WM does something extraordinary, but caching the pointer
|
||||
/// means another layer of complexity.
|
||||
struct managed_win *active_win;
|
||||
/// Window ID of leader window of currently active window. Used for
|
||||
/// subsidiary window detection.
|
||||
xcb_window_t active_leader;
|
||||
struct subwin *subwins;
|
||||
};
|
||||
|
||||
unsigned int wm_get_window_count(struct wm *wm) {
|
||||
unsigned int count = 0;
|
||||
HASH_ITER2(wm->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
struct managed_win *wm_active_win(struct wm *wm) {
|
||||
return wm->active_win;
|
||||
}
|
||||
|
||||
void wm_set_active_win(struct wm *wm, struct managed_win *w) {
|
||||
wm->active_win = w;
|
||||
}
|
||||
|
||||
xcb_window_t wm_active_leader(struct wm *wm) {
|
||||
return wm->active_leader;
|
||||
}
|
||||
|
||||
void wm_set_active_leader(struct wm *wm, xcb_window_t leader) {
|
||||
wm->active_leader = leader;
|
||||
}
|
||||
|
||||
struct win *wm_stack_next(struct wm *wm, struct list_node *cursor) {
|
||||
if (!list_node_is_last(&wm->window_stack, cursor)) {
|
||||
return list_entry(cursor->next, struct win, stack_neighbour);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find the managed window immediately below `i` in the window stack
|
||||
struct managed_win *
|
||||
wm_stack_next_managed(const struct wm *wm, const struct list_node *cursor) {
|
||||
while (!list_node_is_last(&wm->window_stack, cursor)) {
|
||||
auto next = list_entry(cursor->next, struct win, stack_neighbour);
|
||||
if (next->managed) {
|
||||
return (struct managed_win *)next;
|
||||
}
|
||||
cursor = &next->stack_neighbour;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Find a managed window from window id in window linked list of the session.
|
||||
struct win *wm_find(struct wm *wm, xcb_window_t id) {
|
||||
if (!id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct win *w = NULL;
|
||||
HASH_FIND_INT(wm->windows, &id, w);
|
||||
assert(w == NULL || !w->destroyed);
|
||||
return w;
|
||||
}
|
||||
|
||||
void wm_remove(struct wm *wm, struct win *w) {
|
||||
HASH_DEL(wm->windows, w);
|
||||
}
|
||||
|
||||
int wm_foreach(struct wm *wm, int (*func)(struct win *, void *), void *data) {
|
||||
HASH_ITER2(wm->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
int ret = func(w, data);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wm_stack_replace(struct wm *wm, struct win *old, struct win *new_) {
|
||||
list_replace(&old->stack_neighbour, &new_->stack_neighbour);
|
||||
struct win *replaced = NULL;
|
||||
HASH_REPLACE_INT(wm->windows, id, new_, replaced);
|
||||
assert(replaced == old);
|
||||
free(old);
|
||||
}
|
||||
|
||||
/// Insert a new window after list_node `prev`
|
||||
/// New window will be in unmapped state
|
||||
static struct win *
|
||||
wm_stack_insert_after(struct wm *wm, xcb_window_t id, struct list_node *prev) {
|
||||
log_debug("Adding window %#010x", id);
|
||||
struct win *old_w = NULL;
|
||||
HASH_FIND_INT(wm->windows, &id, old_w);
|
||||
assert(old_w == NULL);
|
||||
|
||||
auto new_w = cmalloc(struct win);
|
||||
list_insert_after(prev, &new_w->stack_neighbour);
|
||||
new_w->id = id;
|
||||
new_w->managed = false;
|
||||
new_w->is_new = true;
|
||||
new_w->destroyed = false;
|
||||
|
||||
HASH_ADD_INT(wm->windows, id, new_w);
|
||||
return new_w;
|
||||
}
|
||||
|
||||
struct win *wm_stack_add_top(struct wm *wm, xcb_window_t id) {
|
||||
return wm_stack_insert_after(wm, id, &wm->window_stack);
|
||||
}
|
||||
|
||||
struct win *wm_stack_add_above(struct wm *wm, xcb_window_t id, xcb_window_t below) {
|
||||
struct win *w = NULL;
|
||||
HASH_FIND_INT(wm->windows, &below, w);
|
||||
if (!w) {
|
||||
if (!list_is_empty(&wm->window_stack)) {
|
||||
// `below` window is not found even if the window stack is
|
||||
// not empty
|
||||
return NULL;
|
||||
}
|
||||
return wm_stack_add_top(wm, id);
|
||||
}
|
||||
// we found something from the hash table, so if the stack is
|
||||
// empty, we are in an inconsistent state.
|
||||
assert(!list_is_empty(&wm->window_stack));
|
||||
return wm_stack_insert_after(wm, id, w->stack_neighbour.prev);
|
||||
}
|
||||
|
||||
/// Move window `w` so it's before `next` in the list
|
||||
static inline void wm_stack_move_before(struct wm *wm, struct win *w, struct list_node *next) {
|
||||
struct managed_win *mw = NULL;
|
||||
if (w->managed) {
|
||||
mw = (struct managed_win *)w;
|
||||
}
|
||||
|
||||
if (mw) {
|
||||
// This invalidates all reg_ignore below the new stack position of
|
||||
// `w`
|
||||
mw->reg_ignore_valid = false;
|
||||
rc_region_unref(&mw->reg_ignore);
|
||||
|
||||
// This invalidates all reg_ignore below the old stack position of
|
||||
// `w`
|
||||
auto next_w = wm_stack_next_managed(wm, &w->stack_neighbour);
|
||||
if (next_w) {
|
||||
next_w->reg_ignore_valid = false;
|
||||
rc_region_unref(&next_w->reg_ignore);
|
||||
}
|
||||
}
|
||||
|
||||
list_move_before(&w->stack_neighbour, next);
|
||||
|
||||
#ifdef DEBUG_RESTACK
|
||||
log_trace("Window stack modified. Current stack:");
|
||||
for (auto c = &wm->window_stack; c; c = c->next) {
|
||||
const char *desc = "";
|
||||
if (c->state == WSTATE_DESTROYING) {
|
||||
desc = "(D) ";
|
||||
}
|
||||
log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct list_node *wm_stack_end(struct wm *wm) {
|
||||
return &wm->window_stack;
|
||||
}
|
||||
|
||||
/// Move window `w` so it's right above `below`, if `below` is 0, `w` is moved
|
||||
/// to the bottom of the stack
|
||||
void wm_stack_move_above(struct wm *wm, struct win *w, xcb_window_t below) {
|
||||
xcb_window_t old_below;
|
||||
|
||||
if (!list_node_is_last(&wm->window_stack, &w->stack_neighbour)) {
|
||||
old_below = list_next_entry(w, stack_neighbour)->id;
|
||||
} else {
|
||||
old_below = XCB_NONE;
|
||||
}
|
||||
log_debug("Restack %#010x (%s), old_below: %#010x, new_below: %#010x", w->id,
|
||||
win_get_name_if_managed(w), old_below, below);
|
||||
|
||||
if (old_below != below) {
|
||||
struct list_node *new_next;
|
||||
if (!below) {
|
||||
new_next = &wm->window_stack;
|
||||
} else {
|
||||
struct win *tmp_w = NULL;
|
||||
HASH_FIND_INT(wm->windows, &below, tmp_w);
|
||||
|
||||
if (!tmp_w) {
|
||||
log_error("Failed to found new below window %#010x.", below);
|
||||
return;
|
||||
}
|
||||
|
||||
new_next = &tmp_w->stack_neighbour;
|
||||
}
|
||||
wm_stack_move_before(wm, w, new_next);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_stack_move_to_top(struct wm *wm, struct win *w) {
|
||||
if (&w->stack_neighbour == wm->window_stack.next) {
|
||||
// already at top
|
||||
return;
|
||||
}
|
||||
wm_stack_move_before(wm, w, wm->window_stack.next);
|
||||
}
|
||||
|
||||
struct managed_win *wm_find_by_client(struct wm *wm, xcb_window_t client) {
|
||||
if (!client) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HASH_ITER2(wm->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
if (!w->managed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (mw->client_win == client) {
|
||||
return mw;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct managed_win *wm_find_managed(struct wm *wm, xcb_window_t id) {
|
||||
struct win *w = wm_find(wm, id);
|
||||
if (!w || !w->managed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
assert(mw->state != WSTATE_DESTROYED);
|
||||
return mw;
|
||||
}
|
||||
|
||||
unsigned wm_num_windows(const struct wm *wm) {
|
||||
return HASH_COUNT(wm->windows);
|
||||
}
|
||||
|
||||
struct subwin *wm_subwin_add_and_subscribe(struct wm *wm, struct x_connection *c,
|
||||
xcb_window_t id, xcb_window_t parent) {
|
||||
struct subwin *subwin = NULL;
|
||||
HASH_FIND_INT(wm->subwins, &id, subwin);
|
||||
BUG_ON(subwin != NULL);
|
||||
|
||||
subwin = ccalloc(1, struct subwin);
|
||||
subwin->id = id;
|
||||
subwin->toplevel = parent;
|
||||
HASH_ADD_INT(wm->subwins, id, subwin);
|
||||
|
||||
log_debug("Allocated subwin %p for window %#010x, toplevel %#010x, total: %d",
|
||||
subwin, id, parent, HASH_COUNT(wm->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 *wm_subwin_find(struct wm *wm, xcb_window_t id) {
|
||||
struct subwin *subwin = NULL;
|
||||
HASH_FIND_INT(wm->subwins, &id, subwin);
|
||||
return subwin;
|
||||
}
|
||||
|
||||
void wm_subwin_remove(struct wm *wm, struct subwin *subwin) {
|
||||
log_debug("Freeing subwin %p for window %#010x, toplevel %#010x", subwin,
|
||||
subwin->id, subwin->toplevel);
|
||||
HASH_DEL(wm->subwins, subwin);
|
||||
free(subwin);
|
||||
}
|
||||
|
||||
void wm_subwin_remove_and_unsubscribe(struct wm *wm, 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});
|
||||
wm_subwin_remove(wm, subwin);
|
||||
}
|
||||
void wm_subwin_remove_and_unsubscribe_for_toplevel(struct wm *wm, struct x_connection *c,
|
||||
xcb_window_t toplevel) {
|
||||
struct subwin *subwin, *next_subwin;
|
||||
HASH_ITER(hh, wm->subwins, subwin, next_subwin) {
|
||||
if (subwin->toplevel == toplevel) {
|
||||
wm_subwin_remove_and_unsubscribe(wm, c, subwin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct wm *wm_new(void) {
|
||||
auto wm = ccalloc(1, struct wm);
|
||||
list_init_head(&wm->window_stack);
|
||||
return wm;
|
||||
}
|
||||
|
||||
void wm_free(struct wm *wm, struct x_connection *c) {
|
||||
list_foreach_safe(struct win, w, &wm->window_stack, stack_neighbour) {
|
||||
if (w->managed) {
|
||||
XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, w->id,
|
||||
XCB_CW_EVENT_MASK, (const uint32_t[]){0});
|
||||
}
|
||||
if (!w->destroyed) {
|
||||
HASH_DEL(wm->windows, w);
|
||||
}
|
||||
free(w);
|
||||
}
|
||||
list_init_head(&wm->window_stack);
|
||||
|
||||
struct subwin *subwin, *next_subwin;
|
||||
HASH_ITER(hh, wm->subwins, subwin, next_subwin) {
|
||||
wm_subwin_remove_and_unsubscribe(wm, c, subwin);
|
||||
}
|
||||
|
||||
free(wm);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) Yuxuan Shui
|
||||
|
||||
/// Yeah... We have our own window manager inside the compositor. As a compositor, we do
|
||||
/// need to do a little bit of what a window manager does, to correctly render windows.
|
||||
/// But our window manager is a lot less sophisticated than a average window manager. We
|
||||
/// only keep track of a list of top-level windows, and the order they are stacked.
|
||||
/// But OTOH doing window managing here is also somewhat more challenging. As we are not
|
||||
/// a window manager, we don't actually know what window is an application window, what
|
||||
/// is not. We have to rely on the real window manager playing nice and following the
|
||||
/// ICCCM and EWMH standards.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <uthash.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct wm;
|
||||
struct managed_win;
|
||||
struct list_node;
|
||||
struct x_connection;
|
||||
|
||||
/// Direct children of a toplevel.
|
||||
struct subwin {
|
||||
xcb_window_t id;
|
||||
xcb_window_t toplevel;
|
||||
enum tristate has_wm_state;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
struct wm *wm_new(void);
|
||||
void wm_free(struct wm *wm, struct x_connection *c);
|
||||
|
||||
struct managed_win *wm_active_win(struct wm *wm);
|
||||
void wm_set_active_win(struct wm *wm, struct managed_win *w);
|
||||
xcb_window_t wm_active_leader(struct wm *wm);
|
||||
void wm_set_active_leader(struct wm *wm, xcb_window_t leader);
|
||||
|
||||
// Note: `wm` keeps track of 2 lists of windows. One is the window stack, which includes
|
||||
// all windows that might need to be rendered, which means it would include destroyed
|
||||
// windows in case they need to be faded out. This list is accessed by `wm_stack_*` series
|
||||
// of functions. The other is a hash table of windows, which does not include destroyed
|
||||
// windows. This list is accessed by `wm_find_*`, `wm_foreach`, and `wm_num_windows`.
|
||||
// Adding a window to the window stack also automatically adds it to the hash table.
|
||||
|
||||
/// Find a window in the hash table from window id.
|
||||
struct win *wm_find(struct wm *wm, xcb_window_t id);
|
||||
/// Remove a window from the hash table.
|
||||
void wm_remove(struct wm *wm, struct win *w);
|
||||
/// Find a managed window from window id in window linked list of the session.
|
||||
struct managed_win *wm_find_managed(struct wm *wm, xcb_window_t id);
|
||||
// Find the WM frame of a client window. `id` is the client window id.
|
||||
struct managed_win *wm_find_by_client(struct wm *wm, xcb_window_t client);
|
||||
/// Call `func` on each toplevel window. `func` should return 0 if the iteration
|
||||
/// should continue. If it returns anything else, the iteration will stop and the
|
||||
/// return value will be returned from `wm_foreach`. If the iteration finishes
|
||||
/// naturally, 0 will be returned.
|
||||
int wm_foreach(struct wm *wm, int (*func)(struct win *, void *), void *data);
|
||||
/// Returns the number of windows in the hash table.
|
||||
unsigned attr_const wm_num_windows(const struct wm *wm);
|
||||
|
||||
/// Returns the cursor past the last window in the stack (the `end`). The window stack is
|
||||
/// a cyclic linked list, so the next element after `end` is the first element. The `end`
|
||||
/// itself does not point to a valid window. The address of `end` is stable as long as
|
||||
/// the `struct wm` itself is not freed.
|
||||
struct list_node *attr_const wm_stack_end(struct wm *wm);
|
||||
/// Insert a new win entry at the top of the stack
|
||||
struct win *wm_stack_add_top(struct wm *wm, xcb_window_t id);
|
||||
/// Insert a new window above window with id `below`, if there is no window, add
|
||||
/// to top New window will be in unmapped state
|
||||
struct win *wm_stack_add_above(struct wm *wm, xcb_window_t id, xcb_window_t below);
|
||||
// Find the managed window immediately below `i` in the window stack
|
||||
struct managed_win *attr_pure wm_stack_next_managed(const struct wm *wm,
|
||||
const struct list_node *cursor);
|
||||
/// Move window `w` so it's right above `below`, if `below` is 0, `w` is moved
|
||||
/// to the bottom of the stack
|
||||
void wm_stack_move_above(struct wm *wm, struct win *w, xcb_window_t below);
|
||||
/// Move window `w` to the bottom of the stack.
|
||||
static inline void wm_stack_move_to_bottom(struct wm *wm, struct win *w) {
|
||||
wm_stack_move_above(wm, w, 0);
|
||||
}
|
||||
/// Move window `w` to the top of the stack.
|
||||
void wm_stack_move_to_top(struct wm *wm, struct win *w);
|
||||
/// Replace window `old` with `new_` in the stack, also replace the window in the hash
|
||||
/// table. `old` will be freed.
|
||||
void wm_stack_replace(struct wm *wm, struct win *old, struct win *new_);
|
||||
|
||||
struct subwin *wm_subwin_add_and_subscribe(struct wm *wm, struct x_connection *c,
|
||||
xcb_window_t id, xcb_window_t parent);
|
||||
struct subwin *wm_subwin_find(struct wm *wm, xcb_window_t id);
|
||||
void wm_subwin_remove(struct wm *wm, struct subwin *subwin);
|
||||
void wm_subwin_remove_and_unsubscribe(struct wm *wm, struct x_connection *c,
|
||||
struct subwin *subwin);
|
||||
/// Remove all subwins associated with a toplevel window
|
||||
void wm_subwin_remove_and_unsubscribe_for_toplevel(struct wm *wm, struct x_connection *c,
|
||||
xcb_window_t toplevel);
|
Loading…
Reference in New Issue