mirror of
https://github.com/yshui/picom.git
synced 2025-04-14 17:53:25 -04:00
core: replace window table and window stack with wm_tree
Because wm_tree tracks the entire window tree, it's able to replace several old data structures we used to track windows: the window hash table, the window stack, and the subwin table. So we got rid of those, and fully switched to wm_tree. Fixes window rules for window managers that don't put client window directly under toplevel windows. This includes, according to people's reports, at least i3 and KDE. Fixed a couple small bugs: * dbus was returning window ID as a boolean. * window frame extents not cleared after its client window disappears. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
c96ca0a40a
commit
518f63b920
22 changed files with 1040 additions and 1631 deletions
|
@ -31,7 +31,7 @@ struct xvisual_info {
|
|||
};
|
||||
|
||||
typedef struct session session_t;
|
||||
struct managed_win;
|
||||
struct win;
|
||||
|
||||
struct ev_loop;
|
||||
struct backend_operations;
|
||||
|
|
60
src/c2.c
60
src/c2.c
|
@ -314,7 +314,7 @@ static inline c2_ptr_t c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
|
|||
static inline int c2h_b_opp(c2_b_op_t op) {
|
||||
switch (op) {
|
||||
case C2_B_OAND: return 2;
|
||||
case C2_B_OOR: return 1;
|
||||
case C2_B_OOR:
|
||||
case C2_B_OXOR: return 1;
|
||||
default: break;
|
||||
}
|
||||
|
@ -441,8 +441,12 @@ TEST_CASE(c2_parse) {
|
|||
size_t len = c2_condition_to_str(cond->ptr, str, sizeof(str));
|
||||
TEST_STREQUAL3(str, "name = \"xterm\"", len);
|
||||
|
||||
struct managed_win test_win = {
|
||||
struct wm *wm = wm_new();
|
||||
wm_import_incomplete(wm, 1, XCB_NONE);
|
||||
|
||||
struct win test_win = {
|
||||
.name = "xterm",
|
||||
.tree_ref = wm_find(wm, 1),
|
||||
};
|
||||
TEST_TRUE(c2_match(state, &test_win, cond, NULL));
|
||||
c2_list_postprocess(state, NULL, cond);
|
||||
|
@ -563,6 +567,8 @@ TEST_CASE(c2_parse) {
|
|||
len = c2_condition_to_str(cond->ptr, str, sizeof(str));
|
||||
TEST_STREQUAL3(str, rule, len);
|
||||
c2_list_free(&cond, NULL);
|
||||
|
||||
wm_free(wm);
|
||||
}
|
||||
|
||||
#define c2_error(format, ...) \
|
||||
|
@ -1562,8 +1568,9 @@ static inline bool c2_int_op(const c2_l_t *leaf, int64_t target) {
|
|||
unreachable();
|
||||
}
|
||||
|
||||
static bool c2_match_once_leaf_int(const struct managed_win *w, const c2_l_t *leaf) {
|
||||
const xcb_window_t wid = (leaf->target_on_client ? w->client_win : w->base.id);
|
||||
static bool c2_match_once_leaf_int(const struct win *w, const c2_l_t *leaf) {
|
||||
auto const client_win = win_client_id(w, /*fallback_to_self=*/true);
|
||||
const xcb_window_t wid = (leaf->target_on_client ? client_win : win_id(w));
|
||||
|
||||
// Get the value
|
||||
if (leaf->predef != C2_L_PUNDEFINED) {
|
||||
|
@ -1586,7 +1593,7 @@ static bool c2_match_once_leaf_int(const struct managed_win *w, const c2_l_t *le
|
|||
case C2_L_PWMWIN: predef_target = win_is_wmwin(w); break;
|
||||
case C2_L_PBSHAPED: predef_target = w->bounding_shaped; break;
|
||||
case C2_L_PROUNDED: predef_target = w->rounded_corners; break;
|
||||
case C2_L_PCLIENT: predef_target = w->client_win; break;
|
||||
case C2_L_PCLIENT: predef_target = client_win; break;
|
||||
case C2_L_PLEADER: predef_target = w->leader; break;
|
||||
case C2_L_POVREDIR:
|
||||
// When user wants to check override-redirect, they almost always
|
||||
|
@ -1594,9 +1601,8 @@ static bool c2_match_once_leaf_int(const struct managed_win *w, const c2_l_t *le
|
|||
// don't track the override-redirect state of the client window
|
||||
// directly, however we can assume if a window has a window
|
||||
// manager frame around it, it's not override-redirect.
|
||||
predef_target =
|
||||
w->a.override_redirect && (w->client_win == w->base.id ||
|
||||
w->client_win == XCB_WINDOW_NONE);
|
||||
predef_target = w->a.override_redirect &&
|
||||
wm_ref_client_of(w->tree_ref) == NULL;
|
||||
break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
@ -1608,7 +1614,7 @@ static bool c2_match_once_leaf_int(const struct managed_win *w, const c2_l_t *le
|
|||
assert(!values->needs_update);
|
||||
if (!values->valid) {
|
||||
log_verbose("Property %s not found on window %#010x (%s)", leaf->tgt,
|
||||
w->client_win, w->name);
|
||||
client_win, w->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1670,8 +1676,8 @@ static bool c2_string_op(const c2_l_t *leaf, const char *target) {
|
|||
unreachable();
|
||||
}
|
||||
|
||||
static bool c2_match_once_leaf_string(struct atom *atoms, const struct managed_win *w,
|
||||
const c2_l_t *leaf) {
|
||||
static bool
|
||||
c2_match_once_leaf_string(struct atom *atoms, const struct win *w, const c2_l_t *leaf) {
|
||||
|
||||
// A predefined target
|
||||
const char *predef_target = NULL;
|
||||
|
@ -1696,8 +1702,8 @@ static bool c2_match_once_leaf_string(struct atom *atoms, const struct managed_w
|
|||
auto values = &w->c2_state.values[leaf->target_id];
|
||||
assert(!values->needs_update);
|
||||
if (!values->valid) {
|
||||
log_verbose("Property %s not found on window %#010x (%s)", leaf->tgt,
|
||||
w->client_win, w->name);
|
||||
log_verbose("Property %s not found on window %#010x, client %#010x (%s)",
|
||||
leaf->tgt, win_id(w), win_client_id(w, false), w->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1752,10 +1758,11 @@ static bool c2_match_once_leaf_string(struct atom *atoms, const struct managed_w
|
|||
* For internal use.
|
||||
*/
|
||||
static inline bool
|
||||
c2_match_once_leaf(struct c2_state *state, const struct managed_win *w, const c2_l_t *leaf) {
|
||||
c2_match_once_leaf(struct c2_state *state, const struct win *w, const c2_l_t *leaf) {
|
||||
assert(leaf);
|
||||
|
||||
const xcb_window_t wid = (leaf->target_on_client ? w->client_win : w->base.id);
|
||||
const xcb_window_t wid =
|
||||
(leaf->target_on_client ? win_client_id(w, /*fallback_to_self=*/true) : win_id(w));
|
||||
|
||||
// Return if wid is missing
|
||||
if (leaf->predef == C2_L_PUNDEFINED && !wid) {
|
||||
|
@ -1790,8 +1797,7 @@ c2_match_once_leaf(struct c2_state *state, const struct managed_win *w, const c2
|
|||
*
|
||||
* @return true if matched, false otherwise.
|
||||
*/
|
||||
static bool
|
||||
c2_match_once(struct c2_state *state, const struct managed_win *w, const c2_ptr_t cond) {
|
||||
static bool c2_match_once(struct c2_state *state, const struct win *w, const c2_ptr_t cond) {
|
||||
bool result = false;
|
||||
|
||||
if (cond.isbranch) {
|
||||
|
@ -1802,8 +1808,8 @@ c2_match_once(struct c2_state *state, const struct managed_win *w, const c2_ptr_
|
|||
return false;
|
||||
}
|
||||
|
||||
log_verbose("Matching window %#010x (%s) against condition %s",
|
||||
w->base.id, w->name, c2_condition_to_str2(cond));
|
||||
log_verbose("Matching window %#010x (%s) against condition %s", win_id(w),
|
||||
w->name, c2_condition_to_str2(cond));
|
||||
|
||||
switch (pb->op) {
|
||||
case C2_B_OAND:
|
||||
|
@ -1821,7 +1827,7 @@ c2_match_once(struct c2_state *state, const struct managed_win *w, const c2_ptr_
|
|||
default: unreachable();
|
||||
}
|
||||
|
||||
log_debug("(%#010x): branch: result = %d, pattern = %s", w->base.id,
|
||||
log_debug("(%#010x): branch: result = %d, pattern = %s", win_id(w),
|
||||
result, c2_condition_to_str2(cond));
|
||||
} else {
|
||||
// A leaf
|
||||
|
@ -1833,9 +1839,9 @@ c2_match_once(struct c2_state *state, const struct managed_win *w, const c2_ptr_
|
|||
|
||||
result = c2_match_once_leaf(state, w, pleaf);
|
||||
|
||||
log_debug("(%#010x): leaf: result = %d, client = %#010x, "
|
||||
"pattern = %s",
|
||||
w->base.id, result, w->client_win, c2_condition_to_str2(cond));
|
||||
log_debug("(%#010x): leaf: result = %d, client = %#010x, pattern = %s",
|
||||
win_id(w), result, win_client_id(w, false),
|
||||
c2_condition_to_str2(cond));
|
||||
}
|
||||
|
||||
// Postprocess the result
|
||||
|
@ -1853,8 +1859,8 @@ c2_match_once(struct c2_state *state, const struct managed_win *w, const c2_ptr_
|
|||
* @param pdata a place to return the data
|
||||
* @return true if matched, false otherwise.
|
||||
*/
|
||||
bool c2_match(struct c2_state *state, const struct managed_win *w,
|
||||
const c2_lptr_t *condlst, void **pdata) {
|
||||
bool c2_match(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst,
|
||||
void **pdata) {
|
||||
// Then go through the whole linked list
|
||||
for (; condlst; condlst = condlst->next) {
|
||||
if (c2_match_once(state, w, condlst->ptr)) {
|
||||
|
@ -1869,8 +1875,8 @@ bool c2_match(struct c2_state *state, const struct managed_win *w,
|
|||
}
|
||||
|
||||
/// Match a window against the first condition in a condition linked list.
|
||||
bool c2_match_one(struct c2_state *state, const struct managed_win *w,
|
||||
const c2_lptr_t *condlst, void **pdata) {
|
||||
bool c2_match_one(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst,
|
||||
void **pdata) {
|
||||
if (!condlst) {
|
||||
return false;
|
||||
}
|
||||
|
|
10
src/c2.h
10
src/c2.h
|
@ -18,7 +18,7 @@ struct c2_window_state {
|
|||
struct c2_property_value *values;
|
||||
};
|
||||
struct atom;
|
||||
struct managed_win;
|
||||
struct win;
|
||||
|
||||
typedef void (*c2_userdata_free)(void *);
|
||||
c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
|
||||
|
@ -50,10 +50,10 @@ void c2_window_state_update(struct c2_state *state, struct c2_window_state *wind
|
|||
xcb_connection_t *c, xcb_window_t client_win,
|
||||
xcb_window_t frame_win);
|
||||
|
||||
bool c2_match(struct c2_state *state, const struct managed_win *w,
|
||||
const c2_lptr_t *condlst, void **pdata);
|
||||
bool c2_match_one(struct c2_state *state, const struct managed_win *w,
|
||||
const c2_lptr_t *condlst, void **pdata);
|
||||
bool c2_match(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst,
|
||||
void **pdata);
|
||||
bool c2_match_one(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst,
|
||||
void **pdata);
|
||||
|
||||
bool c2_list_postprocess(struct c2_state *state, xcb_connection_t *c, c2_lptr_t *list);
|
||||
typedef bool (*c2_list_foreach_cb_t)(const c2_lptr_t *cond, void *data);
|
||||
|
|
147
src/dbus.c
147
src/dbus.c
|
@ -20,7 +20,6 @@
|
|||
#include "compiler.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "utils/list.h"
|
||||
#include "utils/misc.h"
|
||||
#include "utils/str.h"
|
||||
#include "wm/defs.h"
|
||||
|
@ -464,24 +463,8 @@ 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 = wm_num_windows(ps->wm);
|
||||
if (!count) {
|
||||
// Nothing to append
|
||||
return true;
|
||||
}
|
||||
|
||||
DBusMessageIter it, subit;
|
||||
dbus_message_iter_init_append(msg, &it);
|
||||
if (!dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY,
|
||||
|
@ -490,12 +473,27 @@ static bool cdbus_append_wids(DBusMessage *msg, session_t *ps) {
|
|||
return false;
|
||||
}
|
||||
|
||||
auto result = wm_foreach(ps->wm, cdbus_append_wids_callback, &subit);
|
||||
bool failed = false;
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
if (wm_ref_is_zombie(cursor)) {
|
||||
continue;
|
||||
}
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto wid = win_id(w);
|
||||
if (!dbus_message_iter_append_basic(&subit, CDBUS_TYPE_WINDOW, &wid)) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dbus_message_iter_close_container(&it, &subit)) {
|
||||
log_error("Failed to close container.");
|
||||
return false;
|
||||
}
|
||||
if (result != 0) {
|
||||
if (failed) {
|
||||
log_error("Failed to append argument.");
|
||||
return false;
|
||||
}
|
||||
|
@ -581,14 +579,20 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
if (!w) {
|
||||
auto cursor = wm_find(ps->wm, wid);
|
||||
if (cursor == NULL) {
|
||||
log_debug("Window %#010x not found.", wid);
|
||||
dbus_set_error(e, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
log_debug("Window %#010x is not managed.", wid);
|
||||
dbus_set_error(e, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
#define append(tgt, type, expr) \
|
||||
if (!strcmp(#tgt, target)) { \
|
||||
if (!cdbus_append_##type(reply, expr)) { \
|
||||
|
@ -599,19 +603,18 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
|
|||
#define append_win_property(name, member, type) append(name, type, w->member)
|
||||
|
||||
append(Mapped, bool_variant, w->state == WSTATE_MAPPED);
|
||||
append(Id, wid_variant, w->base.id);
|
||||
append(Id, wid_variant, win_id(w));
|
||||
append(Type, string_variant, WINTYPES[w->window_type].name);
|
||||
append(RawFocused, bool_variant, win_is_focused_raw(w));
|
||||
append_win_property(ClientWin, client_win, wid_variant);
|
||||
append(ClientWin, wid_variant, win_client_id(w, /*fallback_to_self=*/true));
|
||||
append_win_property(Leader, leader, wid_variant);
|
||||
append_win_property(Name, name, string_variant);
|
||||
|
||||
if (!strcmp("Next", target)) {
|
||||
cdbus_window_t next_id = 0;
|
||||
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;
|
||||
auto below = wm_ref_below(cursor);
|
||||
if (below != NULL) {
|
||||
next_id = wm_ref_win_id(below);
|
||||
}
|
||||
if (!cdbus_append_wid_variant(reply, next_id)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
@ -668,14 +671,20 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
if (!w) {
|
||||
auto cursor = wm_find(ps->wm, wid);
|
||||
if (cursor == NULL) {
|
||||
log_debug("Window %#010x not found.", wid);
|
||||
dbus_set_error(err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
log_debug("Window %#010x is not managed.", wid);
|
||||
dbus_set_error(err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
#define append(tgt, type, expr) \
|
||||
if (strcmp(#tgt, target) == 0) { \
|
||||
if (!cdbus_append_##type(reply, expr)) { \
|
||||
|
@ -686,18 +695,16 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
#define append_win_property(tgt, type) append(tgt, type, w->tgt)
|
||||
|
||||
if (!strcmp("next", target)) {
|
||||
xcb_window_t next_id =
|
||||
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;
|
||||
auto below = wm_ref_below(cursor);
|
||||
xcb_window_t next_id = below ? wm_ref_win_id(below) : XCB_NONE;
|
||||
if (!cdbus_append_wid(reply, next_id)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
append(id, boolean, w->base.id);
|
||||
append(id, wid, win_id(w));
|
||||
append(client_win, wid, win_client_id(w, /*fallback_to_self=*/true));
|
||||
append(map_state, boolean, w->a.map_state);
|
||||
append(wmwin, boolean, win_is_wmwin(w));
|
||||
append(focused_raw, boolean, win_is_focused_raw(w));
|
||||
|
@ -708,7 +715,6 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
|
||||
append_win_property(mode, enum);
|
||||
append_win_property(opacity, double);
|
||||
append_win_property(client_win, wid);
|
||||
append_win_property(ever_damaged, boolean);
|
||||
append_win_property(window_type, enum);
|
||||
append_win_property(leader, wid);
|
||||
|
@ -753,13 +759,19 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
|
||||
if (!w) {
|
||||
auto cursor = wm_find(ps->wm, wid);
|
||||
if (cursor == NULL) {
|
||||
log_debug("Window %#010x not found.", wid);
|
||||
dbus_set_error(err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
log_debug("Window %#010x is not managed.", wid);
|
||||
dbus_set_error(err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
cdbus_enum_t val = UNSET;
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) {
|
||||
dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
|
||||
|
@ -812,13 +824,13 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
|
|||
}
|
||||
auto w = wm_find_by_client(ps->wm, client);
|
||||
if (w) {
|
||||
wid = w->base.id;
|
||||
wid = wm_ref_win_id(w);
|
||||
}
|
||||
} else if (!strcmp("focused", target)) {
|
||||
// Find focused window
|
||||
auto active_win = wm_active_win(ps->wm);
|
||||
if (active_win && active_win->state != WSTATE_UNMAPPED) {
|
||||
wid = active_win->base.id;
|
||||
wid = win_id(active_win);
|
||||
}
|
||||
} else {
|
||||
log_debug(CDBUS_ERROR_BADTGT_S, target);
|
||||
|
@ -1072,21 +1084,6 @@ 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.
|
||||
*/
|
||||
|
@ -1109,8 +1106,18 @@ cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *reply) {
|
|||
|
||||
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;
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
char *tmp = NULL;
|
||||
if (asprintf(&tmp, "<node name='%#010x'/>\n", win_id(w)) < 0) {
|
||||
log_fatal("Failed to allocate memory.");
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
mstrextend(&introspect, tmp);
|
||||
free(tmp);
|
||||
}
|
||||
mstrextend(&introspect, "</node>");
|
||||
if (!cdbus_append_string(reply, introspect)) {
|
||||
|
@ -1424,41 +1431,41 @@ static bool cdbus_signal_wid(struct cdbus_data *cd, const char *interface,
|
|||
///@{
|
||||
void cdbus_ev_win_added(struct cdbus_data *cd, struct win *w) {
|
||||
if (cd->dbus_conn) {
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_added", w->id);
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinAdded", w->id);
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_added", win_id(w));
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinAdded", win_id(w));
|
||||
}
|
||||
}
|
||||
|
||||
void cdbus_ev_win_destroyed(struct cdbus_data *cd, struct win *w) {
|
||||
if (cd->dbus_conn) {
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_destroyed", w->id);
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinDestroyed", w->id);
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_destroyed", win_id(w));
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinDestroyed", win_id(w));
|
||||
}
|
||||
}
|
||||
|
||||
void cdbus_ev_win_mapped(struct cdbus_data *cd, struct win *w) {
|
||||
if (cd->dbus_conn) {
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_mapped", w->id);
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinMapped", w->id);
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_mapped", win_id(w));
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinMapped", win_id(w));
|
||||
}
|
||||
}
|
||||
|
||||
void cdbus_ev_win_unmapped(struct cdbus_data *cd, struct win *w) {
|
||||
if (cd->dbus_conn) {
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_unmapped", w->id);
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinUnmapped", w->id);
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_unmapped", win_id(w));
|
||||
cdbus_signal_wid(cd, PICOM_COMPOSITOR_INTERFACE, "WinUnmapped", win_id(w));
|
||||
}
|
||||
}
|
||||
|
||||
void cdbus_ev_win_focusout(struct cdbus_data *cd, struct win *w) {
|
||||
if (cd->dbus_conn) {
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_focusout", w->id);
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_focusout", win_id(w));
|
||||
}
|
||||
}
|
||||
|
||||
void cdbus_ev_win_focusin(struct cdbus_data *cd, struct win *w) {
|
||||
if (cd->dbus_conn) {
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_focusin", w->id);
|
||||
cdbus_signal_wid(cd, CDBUS_INTERFACE_NAME, "win_focusin", win_id(w));
|
||||
}
|
||||
}
|
||||
//!@}
|
||||
|
|
444
src/event.c
444
src/event.c
|
@ -74,11 +74,12 @@ static inline const char *ev_window_name(session_t *ps, xcb_window_t wid) {
|
|||
} else if (ps->overlay == wid) {
|
||||
name = "(Overlay)";
|
||||
} else {
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
if (!w) {
|
||||
w = wm_find_by_client(ps->wm, wid);
|
||||
auto cursor = wm_find(ps->wm, wid);
|
||||
if (!cursor || !wm_ref_deref(cursor)) {
|
||||
cursor = wm_find_by_client(ps->wm, wid);
|
||||
}
|
||||
|
||||
auto w = cursor ? wm_ref_deref(cursor) : NULL;
|
||||
if (w && w->name) {
|
||||
name = w->name;
|
||||
}
|
||||
|
@ -204,154 +205,124 @@ 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) {
|
||||
wm_import_incomplete(ps->wm, ev->window, ev->parent);
|
||||
|
||||
if (ev->parent == ps->c.screen_info->root) {
|
||||
wm_stack_add_top(ps->wm, ev->window);
|
||||
ps->pending_updates = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
// 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.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle configure event of a regular window
|
||||
static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||
auto w = wm_find(ps->wm, ce->window);
|
||||
auto cursor = wm_find(ps->wm, ce->window);
|
||||
auto below = wm_find(ps->wm, ce->above_sibling);
|
||||
|
||||
if (!cursor) {
|
||||
log_error("Configure event received for unknown window %#010x", ce->window);
|
||||
return;
|
||||
}
|
||||
|
||||
if (below == NULL && ce->above_sibling != XCB_NONE) {
|
||||
log_error("Configure event received for window %#010x, but its sibling "
|
||||
"window %#010x is not in our tree. Expect malfunction.",
|
||||
ce->window, ce->above_sibling);
|
||||
} else if (below != NULL) {
|
||||
wm_stack_move_to_above(ps->wm, cursor, below);
|
||||
} else {
|
||||
// above_sibling being XCB_NONE means the window is put at the bottom.
|
||||
wm_stack_move_to_end(ps->wm, cursor, true);
|
||||
}
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
|
||||
wm_stack_move_above(ps->wm, w, ce->above_sibling);
|
||||
|
||||
if (!w->managed) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
add_damage_from_win(ps, mw);
|
||||
add_damage_from_win(ps, w);
|
||||
|
||||
// We check against pending_g here, because there might have been multiple
|
||||
// configure notifies in this cycle, or the window could receive multiple updates
|
||||
// while it's unmapped.
|
||||
bool position_changed = mw->pending_g.x != ce->x || mw->pending_g.y != ce->y;
|
||||
bool size_changed = mw->pending_g.width != ce->width ||
|
||||
mw->pending_g.height != ce->height ||
|
||||
mw->pending_g.border_width != ce->border_width;
|
||||
bool position_changed = w->pending_g.x != ce->x || w->pending_g.y != ce->y;
|
||||
bool size_changed = w->pending_g.width != ce->width ||
|
||||
w->pending_g.height != ce->height ||
|
||||
w->pending_g.border_width != ce->border_width;
|
||||
if (position_changed || size_changed) {
|
||||
// Queue pending updates
|
||||
win_set_flags(mw, WIN_FLAGS_FACTOR_CHANGED);
|
||||
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
||||
// TODO(yshui) don't set pending_updates if the window is not
|
||||
// visible/mapped
|
||||
ps->pending_updates = true;
|
||||
|
||||
// At least one of the following if's is true
|
||||
if (position_changed) {
|
||||
log_trace("Window position changed, %dx%d -> %dx%d", mw->g.x,
|
||||
mw->g.y, ce->x, ce->y);
|
||||
mw->pending_g.x = ce->x;
|
||||
mw->pending_g.y = ce->y;
|
||||
win_set_flags(mw, WIN_FLAGS_POSITION_STALE);
|
||||
log_trace("Window position changed, %dx%d -> %dx%d", w->g.x,
|
||||
w->g.y, ce->x, ce->y);
|
||||
w->pending_g.x = ce->x;
|
||||
w->pending_g.y = ce->y;
|
||||
win_set_flags(w, WIN_FLAGS_POSITION_STALE);
|
||||
}
|
||||
|
||||
if (size_changed) {
|
||||
log_trace("Window size changed, %dx%d -> %dx%d", mw->g.width,
|
||||
mw->g.height, ce->width, ce->height);
|
||||
mw->pending_g.width = ce->width;
|
||||
mw->pending_g.height = ce->height;
|
||||
mw->pending_g.border_width = ce->border_width;
|
||||
win_set_flags(mw, WIN_FLAGS_SIZE_STALE);
|
||||
log_trace("Window size changed, %dx%d -> %dx%d", w->g.width,
|
||||
w->g.height, ce->width, ce->height);
|
||||
w->pending_g.width = ce->width;
|
||||
w->pending_g.height = ce->height;
|
||||
w->pending_g.border_width = ce->border_width;
|
||||
win_set_flags(w, WIN_FLAGS_SIZE_STALE);
|
||||
}
|
||||
|
||||
// Recalculate which monitor this window is on
|
||||
win_update_monitor(&ps->monitors, mw);
|
||||
win_update_monitor(&ps->monitors, w);
|
||||
}
|
||||
|
||||
// override_redirect flag cannot be changed after window creation, as far
|
||||
// as I know, so there's no point to re-match windows here.
|
||||
mw->a.override_redirect = ce->override_redirect;
|
||||
w->a.override_redirect = ce->override_redirect;
|
||||
}
|
||||
|
||||
static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) {
|
||||
log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
|
||||
log_debug("{ event: %#010x, id: %#010x, above: %#010x, override_redirect: %d }",
|
||||
ev->event, ev->window, ev->above_sibling, ev->override_redirect);
|
||||
if (ev->window == ps->c.screen_info->root) {
|
||||
set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
|
||||
} else {
|
||||
} else if (!wm_is_wid_masked(ps->wm, ev->event)) {
|
||||
configure_win(ps, ev);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
|
||||
log_debug("{ event: %#010x, id: %#010x }", ev->event, ev->window);
|
||||
wm_destroy(ps->wm, ev->window);
|
||||
|
||||
auto subwin = wm_subwin_find(ps->wm, ev->window);
|
||||
if (subwin) {
|
||||
wm_subwin_remove(ps->wm, subwin);
|
||||
}
|
||||
|
||||
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);
|
||||
mw = NULL;
|
||||
}
|
||||
|
||||
// A window can't be a client window and a top-level window at the same time,
|
||||
// so only one of `w` and `mw` can be non-NULL
|
||||
assert(w == NULL || mw == NULL);
|
||||
|
||||
if (w != NULL) {
|
||||
destroy_win_start(ps, w);
|
||||
if (!w->managed) {
|
||||
// If the window wasn't managed, we can release it immediately
|
||||
destroy_win_finish(ps, w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mw != NULL) {
|
||||
win_unmark_client(mw);
|
||||
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
return;
|
||||
}
|
||||
log_debug("Received a destroy notify from an unknown window, %#010x", ev->window);
|
||||
}
|
||||
|
||||
static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
|
||||
// Unmap overlay window if it got mapped but we are currently not
|
||||
// in redirected state.
|
||||
if (ps->overlay && ev->window == ps->overlay && !ps->redirected) {
|
||||
log_debug("Overlay is mapped while we are not redirected");
|
||||
auto e = xcb_request_check(
|
||||
ps->c.c, xcb_unmap_window_checked(ps->c.c, ps->overlay));
|
||||
if (e) {
|
||||
log_error("Failed to unmap the overlay window");
|
||||
free(e);
|
||||
if (ps->overlay && ev->window == ps->overlay) {
|
||||
if (!ps->redirected) {
|
||||
log_debug("Overlay is mapped while we are not redirected");
|
||||
auto succeeded =
|
||||
XCB_AWAIT_VOID(xcb_unmap_window, ps->c.c, ps->overlay);
|
||||
if (!succeeded) {
|
||||
log_error("Failed to unmap the overlay window");
|
||||
}
|
||||
}
|
||||
// We don't track the overlay window, so we can return
|
||||
return;
|
||||
}
|
||||
|
||||
auto w = wm_find_managed(ps->wm, ev->window);
|
||||
if (!w) {
|
||||
if (wm_is_wid_masked(ps->wm, ev->event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cursor = wm_find(ps->wm, ev->window);
|
||||
if (cursor == NULL) {
|
||||
log_error("Map event received for unknown window %#010x, overlay is "
|
||||
"%#010x",
|
||||
ev->window, ps->overlay);
|
||||
return;
|
||||
}
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
return;
|
||||
}
|
||||
win_set_flags(w, WIN_FLAGS_MAPPED);
|
||||
// We set `ever_damaged` to false here, instead of in `map_win_start`,
|
||||
// because we might receive damage events before that function is called
|
||||
|
@ -367,8 +338,21 @@ 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 = wm_find_managed(ps->wm, ev->window);
|
||||
if (w) {
|
||||
if (ps->overlay && ev->window == ps->overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wm_is_wid_masked(ps->wm, ev->event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cursor = wm_find(ps->wm, ev->window);
|
||||
if (cursor == NULL) {
|
||||
log_error("Unmap event received for unknown window %#010x", ev->window);
|
||||
return;
|
||||
}
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
unmap_win_start(w);
|
||||
}
|
||||
}
|
||||
|
@ -377,135 +361,28 @@ 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, "
|
||||
"send_event: %#010x",
|
||||
ev->window, ev->parent, ev->override_redirect, ev->event);
|
||||
|
||||
wm_reparent(ps->wm, ev->window, 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;
|
||||
} else if (old_w) {
|
||||
old_parent = ps->c.screen_info->root;
|
||||
}
|
||||
if (old_toplevel && old_toplevel->client_win == old_toplevel->base.id) {
|
||||
// We only want _real_ frame window, meaning old_toplevel must be
|
||||
// a parent of `ev->window`.
|
||||
old_toplevel = NULL;
|
||||
}
|
||||
// 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
|
||||
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);
|
||||
if (old_w->managed) {
|
||||
add_damage_from_win(ps, win_as_managed(old_w));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (old_toplevel) {
|
||||
assert(old_subwin != NULL);
|
||||
assert(old_subwin->toplevel == old_toplevel->base.id);
|
||||
win_unmark_client(old_toplevel);
|
||||
win_set_flags(old_toplevel, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
|
||||
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(mw);
|
||||
}
|
||||
|
||||
// If an animation is running, the best we could do is stopping
|
||||
// it.
|
||||
free(mw->running_animation);
|
||||
mw->running_animation = NULL;
|
||||
}
|
||||
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) {
|
||||
wm_subwin_remove_and_unsubscribe(ps->wm, &ps->c, new_subwin);
|
||||
new_subwin = NULL;
|
||||
} else if (new_subwin == NULL && new_toplevel != NULL) {
|
||||
new_subwin =
|
||||
wm_subwin_add_and_subscribe(ps->wm, &ps->c, ev->window, ev->parent);
|
||||
}
|
||||
if (new_subwin) {
|
||||
new_subwin->toplevel = new_toplevel->base.id;
|
||||
if (new_toplevel->client_win == XCB_NONE ||
|
||||
new_toplevel->client_win == new_toplevel->base.id) {
|
||||
win_set_flags(new_toplevel, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->parent == ps->c.screen_info->root) {
|
||||
// New parent is root, add a toplevel;
|
||||
assert(old_w == NULL);
|
||||
assert(new_toplevel == NULL);
|
||||
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 = wm_find(ps->wm, ev->window);
|
||||
|
||||
if (!w) {
|
||||
if (wm_is_wid_masked(ps->wm, ev->event)) {
|
||||
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) {
|
||||
wm_stack_move_to_top(ps->wm, w);
|
||||
} else {
|
||||
wm_stack_move_to_bottom(ps->wm, w);
|
||||
auto cursor = wm_find(ps->wm, ev->window);
|
||||
|
||||
if (cursor == NULL) {
|
||||
log_error("Circulate event received for unknown window %#010x", ev->window);
|
||||
return;
|
||||
}
|
||||
if (w->managed) {
|
||||
add_damage_from_win(ps, win_as_managed(w));
|
||||
|
||||
log_debug("Moving window %#010x (%s) to the %s", ev->window,
|
||||
ev_window_name(ps, ev->window), ev->place == PlaceOnTop ? "top" : "bottom");
|
||||
wm_stack_move_to_end(ps->wm, cursor, ev->place == XCB_PLACE_ON_BOTTOM);
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
add_damage_from_win(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -536,50 +413,6 @@ 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 = wm_subwin_find(ps->wm, 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 = wm_find(ps->wm, 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
|
||||
|
@ -611,10 +444,31 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
return;
|
||||
}
|
||||
|
||||
if (wm_is_wid_masked(ps->wm, ev->window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ps->pending_updates = true;
|
||||
auto w = wm_find_by_client(ps->wm, ev->window);
|
||||
auto cursor = wm_find(ps->wm, ev->window);
|
||||
if (cursor == NULL) {
|
||||
log_error("Property notify received for unknown window %#010x", ev->window);
|
||||
return;
|
||||
}
|
||||
|
||||
auto toplevel_cursor = wm_ref_toplevel_of(cursor);
|
||||
if (ev->atom == ps->atoms->aWM_STATE) {
|
||||
ev_subwin_wm_state_changed(ps, ev);
|
||||
log_debug("WM_STATE changed for window %#010x (%s): %s", ev->window,
|
||||
ev_window_name(ps, ev->window),
|
||||
ev->state == XCB_PROPERTY_DELETE ? "deleted" : "set");
|
||||
wm_set_has_wm_state(ps->wm, cursor, ev->state != XCB_PROPERTY_DELETE);
|
||||
}
|
||||
|
||||
// We only care if the property is set on the toplevel itself, or on its
|
||||
// client window if it has one. WM_STATE is an exception, it is handled
|
||||
// always because it is what determines if a window is a client window.
|
||||
auto client_cursor = wm_ref_client_of(toplevel_cursor) ?: toplevel_cursor;
|
||||
if (cursor != client_cursor && cursor != toplevel_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
|
||||
|
@ -622,41 +476,34 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
queue_redraw(ps);
|
||||
}
|
||||
|
||||
if (w) {
|
||||
win_set_property_stale(w, ev->atom);
|
||||
auto toplevel = wm_ref_deref(toplevel_cursor);
|
||||
if (toplevel) {
|
||||
win_set_property_stale(toplevel, ev->atom);
|
||||
}
|
||||
|
||||
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY) {
|
||||
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY && toplevel != NULL) {
|
||||
// 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 = wm_find_managed(ps->wm, ev->window);
|
||||
if (toplevel) {
|
||||
win_set_property_stale(toplevel, ev->atom);
|
||||
}
|
||||
win_set_property_stale(toplevel, ev->atom);
|
||||
}
|
||||
|
||||
// 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 = wm_find_managed(ps->wm, ev->window);
|
||||
if (!w) {
|
||||
w = wm_find_by_client(ps->wm, ev->window);
|
||||
change_is_on_client = true;
|
||||
}
|
||||
if (w) {
|
||||
c2_window_state_mark_dirty(ps->c2_state, &w->c2_state, ev->atom,
|
||||
change_is_on_client);
|
||||
bool change_is_on_client = cursor == client_cursor;
|
||||
if (toplevel) {
|
||||
c2_window_state_mark_dirty(ps->c2_state, &toplevel->c2_state,
|
||||
ev->atom, change_is_on_client);
|
||||
// Set FACTOR_CHANGED so rules based on properties will be
|
||||
// re-evaluated.
|
||||
// Don't need to set property stale here, since that only
|
||||
// concerns properties we explicitly check.
|
||||
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
||||
win_set_flags(toplevel, WIN_FLAGS_FACTOR_CHANGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void repair_win(session_t *ps, struct managed_win *w) {
|
||||
static inline void repair_win(session_t *ps, struct win *w) {
|
||||
// Only mapped window can receive damages
|
||||
assert(w->state == WSTATE_MAPPED || win_check_flags_all(w, WIN_FLAGS_MAPPED));
|
||||
|
||||
|
@ -681,8 +528,8 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
|
|||
free(e);
|
||||
}
|
||||
win_extents(w, &parts);
|
||||
log_debug("Window %#010x (%s) has been damaged the first time",
|
||||
w->base.id, w->name);
|
||||
log_debug("Window %#010x (%s) has been damaged the first time", win_id(w),
|
||||
w->name);
|
||||
} else {
|
||||
auto cookie = xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE,
|
||||
ps->damage_ring.x_region);
|
||||
|
@ -694,7 +541,7 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
|
|||
w->g.y + w->g.border_width);
|
||||
}
|
||||
|
||||
log_trace("Mark window %#010x (%s) as having received damage", w->base.id, w->name);
|
||||
log_trace("Mark window %#010x (%s) as having received damage", win_id(w), w->name);
|
||||
w->ever_damaged = true;
|
||||
w->pixmap_damaged = true;
|
||||
|
||||
|
@ -721,24 +568,29 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
|
|||
}
|
||||
|
||||
static inline void ev_damage_notify(session_t *ps, xcb_damage_notify_event_t *de) {
|
||||
/*
|
||||
if (ps->root == de->drawable) {
|
||||
root_damaged();
|
||||
return;
|
||||
} */
|
||||
auto cursor = wm_find(ps->wm, de->drawable);
|
||||
|
||||
auto w = wm_find_managed(ps->wm, de->drawable);
|
||||
|
||||
if (!w) {
|
||||
if (cursor == NULL) {
|
||||
log_error("Damage notify received for unknown window %#010x", de->drawable);
|
||||
return;
|
||||
}
|
||||
|
||||
repair_win(ps, w);
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
repair_win(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev) {
|
||||
auto w = wm_find_managed(ps->wm, ev->affected_window);
|
||||
if (!w || w->a.map_state == XCB_MAP_STATE_UNMAPPED) {
|
||||
auto cursor = wm_find(ps->wm, ev->affected_window);
|
||||
if (cursor == NULL) {
|
||||
log_error("Shape notify received for unknown window %#010x",
|
||||
ev->affected_window);
|
||||
return;
|
||||
}
|
||||
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL || w->a.map_state == XCB_MAP_STATE_UNMAPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,22 +23,32 @@
|
|||
#include "wm/win.h"
|
||||
#include "x.h"
|
||||
|
||||
static struct managed_win *
|
||||
static struct win *
|
||||
setup_window(struct x_connection *c, struct atom *atoms, struct options *options,
|
||||
struct c2_state *state, xcb_window_t target) {
|
||||
// Pretend we are the compositor, and build up the window state
|
||||
struct managed_win *w = ccalloc(1, struct managed_win);
|
||||
struct wm *wm = wm_new();
|
||||
wm_import_incomplete(wm, target, XCB_NONE);
|
||||
wm_complete_import(wm, c, atoms);
|
||||
|
||||
auto cursor = wm_find(wm, target);
|
||||
if (cursor == NULL) {
|
||||
log_fatal("Could not find window %#010x", target);
|
||||
wm_free(wm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct win *w = ccalloc(1, struct win);
|
||||
w->state = WSTATE_MAPPED;
|
||||
w->base.id = target;
|
||||
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);
|
||||
win_update_frame_extents(c, atoms, w, win_client_id(w, /*fallback_to_self=*/true),
|
||||
options->frame_opacity);
|
||||
// TODO(yshui) get leader
|
||||
win_update_name(c, atoms, w);
|
||||
win_update_class(c, atoms, w);
|
||||
win_update_role(c, atoms, w);
|
||||
|
||||
auto geometry_reply = XCB_AWAIT(xcb_get_geometry, c->c, w->base.id);
|
||||
auto geometry_reply = XCB_AWAIT(xcb_get_geometry, c->c, win_id(w));
|
||||
w->g = (struct win_geometry){
|
||||
.x = geometry_reply->x,
|
||||
.y = geometry_reply->y,
|
||||
|
@ -68,17 +78,18 @@ setup_window(struct x_connection *c, struct atom *atoms, struct options *options
|
|||
free(reply);
|
||||
}
|
||||
}
|
||||
if (wid == w->base.id || wid == w->client_win) {
|
||||
if (wid == win_id(w) || wid == win_client_id(w, /*fallback_to_self=*/false)) {
|
||||
w->focused = true;
|
||||
}
|
||||
|
||||
auto attributes_reply = XCB_AWAIT(xcb_get_window_attributes, c->c, w->base.id);
|
||||
auto attributes_reply = XCB_AWAIT(xcb_get_window_attributes, c->c, win_id(w));
|
||||
w->a = *attributes_reply;
|
||||
w->pictfmt = x_get_pictform_for_visual(c, w->a.visual);
|
||||
free(attributes_reply);
|
||||
|
||||
c2_window_state_init(state, &w->c2_state);
|
||||
c2_window_state_update(state, &w->c2_state, c->c, w->client_win, w->base.id);
|
||||
c2_window_state_update(state, &w->c2_state, c->c,
|
||||
win_client_id(w, /*fallback_to_self=*/true), win_id(w));
|
||||
return w;
|
||||
}
|
||||
|
||||
|
@ -140,7 +151,7 @@ xcb_window_t select_window(struct x_connection *c) {
|
|||
|
||||
struct c2_match_state {
|
||||
struct c2_state *state;
|
||||
struct managed_win *w;
|
||||
struct win *w;
|
||||
bool print_value;
|
||||
};
|
||||
|
||||
|
|
15
src/opengl.c
15
src/opengl.c
|
@ -233,8 +233,11 @@ void glx_destroy(session_t *ps) {
|
|||
}
|
||||
|
||||
// Free all GLX resources of windows
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
free_win_res_glx(ps, w);
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
free_win_res_glx(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
// Free GLSL shaders/programs
|
||||
|
@ -1160,9 +1163,9 @@ void glx_read_border_pixel(int root_height, int root_width, int x, int y, int wi
|
|||
gl_check_err();
|
||||
}
|
||||
|
||||
bool glx_round_corners_dst(session_t *ps, struct managed_win *w,
|
||||
const glx_texture_t *ptex, int dx, int dy, int width,
|
||||
int height, float z, float cr, const region_t *reg_tgt) {
|
||||
bool glx_round_corners_dst(session_t *ps, struct win *w, const glx_texture_t *ptex,
|
||||
int dx, int dy, int width, int height, float z, float cr,
|
||||
const region_t *reg_tgt) {
|
||||
assert(ps->psglx->round_passes->prog);
|
||||
bool ret = false;
|
||||
|
||||
|
@ -1533,7 +1536,7 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
|
|||
/**
|
||||
* Free GLX part of win.
|
||||
*/
|
||||
void free_win_res_glx(session_t *ps, struct managed_win *w) {
|
||||
void free_win_res_glx(session_t *ps, struct win *w) {
|
||||
free_paint_glx(ps, &w->paint);
|
||||
free_paint_glx(ps, &w->shadow_paint);
|
||||
free_glx_bc(ps, &w->glx_blur_cache);
|
||||
|
|
|
@ -122,9 +122,9 @@ void glx_set_clip(session_t *ps, const region_t *reg);
|
|||
bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc);
|
||||
|
||||
bool glx_round_corners_dst(session_t *ps, struct managed_win *w,
|
||||
const glx_texture_t *ptex, int dx, int dy, int width,
|
||||
int height, float z, float cr, const region_t *reg_tgt);
|
||||
bool glx_round_corners_dst(session_t *ps, struct win *w, const glx_texture_t *ptex,
|
||||
int dx, int dy, int width, int height, float z, float cr,
|
||||
const region_t *reg_tgt);
|
||||
|
||||
GLuint glx_create_shader(GLenum shader_type, const char *shader_str);
|
||||
|
||||
|
@ -224,4 +224,4 @@ static inline void free_paint_glx(session_t *ps, paint_t *ppaint) {
|
|||
/**
|
||||
* Free GLX part of win.
|
||||
*/
|
||||
void free_win_res_glx(session_t *ps, struct managed_win *w);
|
||||
void free_win_res_glx(session_t *ps, struct win *w);
|
||||
|
|
289
src/picom.c
289
src/picom.c
|
@ -460,7 +460,8 @@ 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 = wm_find_by_client(ps->wm, wid);
|
||||
auto cursor = wm_find_by_client(ps->wm, wid);
|
||||
auto w = cursor ? wm_ref_deref(cursor) : NULL;
|
||||
|
||||
// Mark the window focused. No need to unfocus the previous one.
|
||||
if (w) {
|
||||
|
@ -501,36 +502,20 @@ static void recheck_focus(session_t *ps) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Trace upwards until we reach the toplevel containing the focus window.
|
||||
while (true) {
|
||||
auto tree = xcb_query_tree_reply(ps->c.c, xcb_query_tree(ps->c.c, wid), &e);
|
||||
if (tree == NULL) {
|
||||
// xcb_query_tree probably fails if you run picom when X is
|
||||
// somehow initializing (like add it in .xinitrc). In this case
|
||||
// just leave it alone.
|
||||
log_error_x_error(e, "Failed to query window tree.");
|
||||
free(e);
|
||||
return;
|
||||
}
|
||||
|
||||
auto parent = tree->parent;
|
||||
free(tree);
|
||||
|
||||
if (parent == ps->c.screen_info->root) {
|
||||
break;
|
||||
}
|
||||
wid = parent;
|
||||
auto cursor = wm_find(ps->wm, wid);
|
||||
if (cursor == NULL) {
|
||||
log_error("Window %#010x not found in window tree.", wid);
|
||||
return;
|
||||
}
|
||||
|
||||
auto w = wm_find_managed(ps->wm, wid);
|
||||
cursor = wm_ref_toplevel_of(cursor);
|
||||
|
||||
// And we set the focus state here
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w) {
|
||||
log_debug("%#010" PRIx32 " (%#010" PRIx32 " \"%s\") focused.", wid,
|
||||
w->base.id, w->name);
|
||||
win_id(w), w->name);
|
||||
win_set_focused(ps, w);
|
||||
} else {
|
||||
log_warn("Focus window %#010" PRIx32 " not found.", wid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -543,7 +528,11 @@ static void rebuild_screen_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, wm_stack_end(ps->wm)) {
|
||||
wm_stack_foreach_safe(ps->wm, cursor, next_cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
// An unmapped window shouldn't have a pixmap, unless it has animation
|
||||
// running. (`w->previous.state != w->state` means there might be
|
||||
// animation but we haven't had a chance to start it because
|
||||
|
@ -569,7 +558,7 @@ static void destroy_backend(session_t *ps) {
|
|||
free_paint(ps, &w->paint);
|
||||
|
||||
if (w->state == WSTATE_DESTROYED) {
|
||||
destroy_win_finish(ps, &w->base);
|
||||
win_destroy_finish(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -640,20 +629,6 @@ 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.use_legacy_backends) {
|
||||
|
@ -701,9 +676,18 @@ static bool initialize_backend(session_t *ps) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
assert(w->state != WSTATE_DESTROYED);
|
||||
// We need to reacquire image
|
||||
log_debug("Marking window %#010x (%s) for update after "
|
||||
"redirection",
|
||||
win_id(w), w->name);
|
||||
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
}
|
||||
ps->renderer = renderer_new(ps->backend_data, ps->o.shadow_radius,
|
||||
(struct color){.alpha = ps->o.shadow_opacity,
|
||||
.red = ps->o.shadow_red,
|
||||
|
@ -725,6 +709,18 @@ err:
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline void invalidate_reg_ignore(session_t *ps) {
|
||||
// Invalidate reg_ignore from the top
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto top_w = wm_ref_deref(cursor);
|
||||
if (top_w != NULL) {
|
||||
rc_region_unref(&top_w->reg_ignore);
|
||||
top_w->reg_ignore_valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle configure event of the root window
|
||||
static void configure_root(session_t *ps) {
|
||||
// TODO(yshui) re-initializing backend should be done outside of the
|
||||
|
@ -760,19 +756,16 @@ static void configure_root(session_t *ps) {
|
|||
free(r);
|
||||
|
||||
rebuild_screen_reg(ps);
|
||||
|
||||
// Invalidate reg_ignore from the top
|
||||
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;
|
||||
}
|
||||
invalidate_reg_ignore(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, wm_stack_end(ps->wm)) {
|
||||
win_update_is_fullscreen(ps, w);
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
win_update_is_fullscreen(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
if (ps->redirected) {
|
||||
|
@ -829,17 +822,22 @@ static void handle_root_flags(session_t *ps) {
|
|||
*
|
||||
* @return whether the operation succeeded
|
||||
*/
|
||||
static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win **out_bottom) {
|
||||
static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bottom) {
|
||||
// XXX need better, more general name for `fade_running`. It really
|
||||
// means if fade is still ongoing after the current frame is rendered
|
||||
struct managed_win *bottom = NULL;
|
||||
struct win *bottom = NULL;
|
||||
*animation = false;
|
||||
*out_bottom = NULL;
|
||||
|
||||
// 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, wm_stack_end(ps->wm)) {
|
||||
wm_stack_foreach_safe(ps->wm, cursor, tmp) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const winmode_t mode_old = w->mode;
|
||||
const bool was_painted = w->to_paint;
|
||||
|
||||
|
@ -887,8 +885,13 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win
|
|||
// Track whether it's the highest window to paint
|
||||
bool is_highest = true;
|
||||
bool reg_ignore_valid = true;
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
wm_stack_foreach_safe(ps->wm, cursor, next_cursor) {
|
||||
__label__ skip_window;
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool to_paint = true;
|
||||
// w->to_paint remembers whether this window is painted last time
|
||||
const bool was_painted = w->to_paint;
|
||||
|
@ -902,7 +905,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win
|
|||
|
||||
// log_trace("%d %d %s", w->a.map_state, w->ever_damaged, w->name);
|
||||
log_trace("Checking whether window %#010x (%s) should be painted",
|
||||
w->base.id, w->name);
|
||||
win_id(w), w->name);
|
||||
|
||||
// Give up if it's not damaged or invisible, or it's unmapped and its
|
||||
// pixmap is gone (for example due to a ConfigureNotify), or when it's
|
||||
|
@ -912,8 +915,8 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win
|
|||
log_trace("|- is unmapped");
|
||||
to_paint = false;
|
||||
} else if (unlikely(ps->debug_window != XCB_NONE) &&
|
||||
(w->base.id == ps->debug_window ||
|
||||
w->client_win == ps->debug_window)) {
|
||||
(win_id(w) == ps->debug_window ||
|
||||
win_client_id(w, /*fallback_to_self=*/false) == ps->debug_window)) {
|
||||
log_trace("|- is the debug window");
|
||||
to_paint = false;
|
||||
} else if (!w->ever_damaged) {
|
||||
|
@ -954,7 +957,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win
|
|||
}
|
||||
|
||||
log_trace("|- will be painted");
|
||||
log_verbose("Window %#010x (%s) will be painted", w->base.id, w->name);
|
||||
log_verbose("Window %#010x (%s) will be painted", win_id(w), w->name);
|
||||
|
||||
// Generate ignore region for painting to reduce GPU load
|
||||
if (!w->reg_ignore) {
|
||||
|
@ -1007,11 +1010,6 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win
|
|||
}
|
||||
|
||||
w->prev_trans = bottom;
|
||||
if (bottom) {
|
||||
w->stacking_rank = bottom->stacking_rank + 1;
|
||||
} else {
|
||||
w->stacking_rank = 0;
|
||||
}
|
||||
bottom = w;
|
||||
|
||||
// If the screen is not redirected and the window has redir_ignore set,
|
||||
|
@ -1027,7 +1025,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct managed_win
|
|||
if (w->state == WSTATE_DESTROYED && w->running_animation == NULL) {
|
||||
// the window should be destroyed because it was destroyed
|
||||
// by X server and now its animations are finished
|
||||
destroy_win_finish(ps, &w->base);
|
||||
win_destroy_finish(ps, w);
|
||||
w = NULL;
|
||||
}
|
||||
|
||||
|
@ -1574,42 +1572,69 @@ static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents
|
|||
static void handle_new_windows(session_t *ps) {
|
||||
wm_complete_import(ps->wm, &ps->c, ps->atoms);
|
||||
|
||||
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) {
|
||||
continue;
|
||||
// Check tree changes first, because later property updates need accurate
|
||||
// client window information
|
||||
struct win *w = NULL;
|
||||
while (true) {
|
||||
auto wm_change = wm_dequeue_change(ps->wm);
|
||||
if (wm_change.type == WM_TREE_CHANGE_NONE) {
|
||||
break;
|
||||
}
|
||||
switch (wm_change.type) {
|
||||
case WM_TREE_CHANGE_TOPLEVEL_NEW:
|
||||
w = win_maybe_allocate(ps, wm_change.toplevel);
|
||||
if (w != NULL && w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
win_map_start(w);
|
||||
}
|
||||
|
||||
assert(new_w->managed);
|
||||
wm_stack_replace(ps->wm, w, new_w);
|
||||
|
||||
auto mw = (struct managed_win *)new_w;
|
||||
if (mw->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
win_set_flags(mw, WIN_FLAGS_MAPPED);
|
||||
|
||||
// This window might be damaged before we called fill_win
|
||||
// and created the damage handle. And there is no way for
|
||||
// us to find out. So just blindly mark it damaged
|
||||
mw->ever_damaged = true;
|
||||
break;
|
||||
case WM_TREE_CHANGE_TOPLEVEL_KILLED:
|
||||
w = wm_ref_deref(wm_change.toplevel);
|
||||
if (w != NULL) {
|
||||
// Pointing the window tree_ref to the zombie.
|
||||
w->tree_ref = wm_change.toplevel;
|
||||
win_destroy_start(ps, w);
|
||||
} else {
|
||||
// This window is not managed, no point keeping the zombie
|
||||
// around.
|
||||
wm_reap_zombie(wm_change.toplevel);
|
||||
}
|
||||
// Send D-Bus signal
|
||||
if (ps->o.dbus) {
|
||||
cdbus_ev_win_added(session_get_cdbus(ps), new_w);
|
||||
break;
|
||||
case WM_TREE_CHANGE_CLIENT:
|
||||
log_debug("Client window for window %#010x changed from "
|
||||
"%#010x to %#010x",
|
||||
wm_ref_win_id(wm_change.toplevel),
|
||||
wm_change.client.old.x, wm_change.client.new_.x);
|
||||
w = wm_ref_deref(wm_change.toplevel);
|
||||
if (w != NULL) {
|
||||
win_set_flags(w, WIN_FLAGS_CLIENT_STALE);
|
||||
} else {
|
||||
log_debug("An unmanaged window %#010x has a new client "
|
||||
"%#010x",
|
||||
wm_ref_win_id(wm_change.toplevel),
|
||||
wm_change.client.new_.x);
|
||||
}
|
||||
break;
|
||||
case WM_TREE_CHANGE_TOPLEVEL_RESTACKED: invalidate_reg_ignore(ps); break;
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_windows(session_t *ps) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
win_process_update_flags(ps, w);
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
win_process_update_flags(ps, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_images(session_t *ps) {
|
||||
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
||||
win_process_image_flags(ps, w);
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
win_process_image_flags(ps, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1641,30 +1666,6 @@ static void handle_pending_updates(EV_P_ struct session *ps, double delta_t) {
|
|||
// Process new windows, and maybe allocate struct managed_win for them
|
||||
handle_new_windows(ps);
|
||||
|
||||
while (true) {
|
||||
auto wm_change = wm_dequeue_change(ps->wm);
|
||||
if (wm_change.type == WM_TREE_CHANGE_NONE) {
|
||||
break;
|
||||
}
|
||||
switch (wm_change.type) {
|
||||
case WM_TREE_CHANGE_TOPLEVEL_NEW:
|
||||
log_debug("New window %#010x",
|
||||
wm_ref_win_id(wm_change.toplevel));
|
||||
break;
|
||||
case WM_TREE_CHANGE_TOPLEVEL_KILLED:
|
||||
log_debug("Destroying window %#010x",
|
||||
wm_ref_win_id(wm_change.toplevel));
|
||||
break;
|
||||
case WM_TREE_CHANGE_CLIENT:
|
||||
log_debug("Client message for window %#010x changed from "
|
||||
"%#010x to %#010x",
|
||||
wm_ref_win_id(wm_change.toplevel),
|
||||
wm_change.client.old.x, wm_change.client.new_.x);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle screen changes
|
||||
// This HAS TO be called before refresh_windows, as handle_root_flags
|
||||
// could call configure_root, which will release images and mark them
|
||||
|
@ -1690,17 +1691,19 @@ static void handle_pending_updates(EV_P_ struct session *ps, double delta_t) {
|
|||
ps->pending_updates = false;
|
||||
log_trace("Exited critical section");
|
||||
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
wm_stack_foreach_safe(ps->wm, cursor, tmp) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
BUG_ON(w != NULL && w->tree_ref != cursor);
|
||||
// Window might be freed by this function, if it's destroyed and its
|
||||
// animation finished
|
||||
if (win_process_animation_and_state_change(ps, w, delta_t)) {
|
||||
if (w != NULL && win_process_animation_and_state_change(ps, w, delta_t)) {
|
||||
free(w->running_animation);
|
||||
w->running_animation = NULL;
|
||||
w->in_openclose = false;
|
||||
if (w->state == WSTATE_UNMAPPED) {
|
||||
unmap_win_finish(ps, w);
|
||||
} else if (w->state == WSTATE_DESTROYED) {
|
||||
destroy_win_finish(ps, &w->base);
|
||||
win_destroy_finish(ps, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1765,23 +1768,34 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
|||
//
|
||||
// TODO(yshui) I think maybe we don't need this anymore, since now we
|
||||
// immediate acquire pixmap right after `map_win_start`.
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
wm_stack_foreach_safe(ps->wm, cursor, tmp) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
free(w->running_animation);
|
||||
w->running_animation = NULL;
|
||||
if (w->state == WSTATE_DESTROYED) {
|
||||
destroy_win_finish(ps, &w->base);
|
||||
win_destroy_finish(ps, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ps->o.benchmark) {
|
||||
if (ps->o.benchmark_wid) {
|
||||
auto w = wm_find_managed(ps->wm, ps->o.benchmark_wid);
|
||||
if (!w) {
|
||||
auto w = wm_find(ps->wm, ps->o.benchmark_wid);
|
||||
if (w == NULL) {
|
||||
log_fatal("Couldn't find specified benchmark window.");
|
||||
exit(1);
|
||||
}
|
||||
add_damage_from_win(ps, w);
|
||||
w = wm_ref_toplevel_of(w);
|
||||
|
||||
auto win = wm_ref_deref(w);
|
||||
if (win != NULL) {
|
||||
add_damage_from_win(ps, win);
|
||||
} else {
|
||||
force_repaint(ps);
|
||||
}
|
||||
} else {
|
||||
force_repaint(ps);
|
||||
}
|
||||
|
@ -1792,7 +1806,7 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
|||
* screen should be redirected. */
|
||||
bool animation = false;
|
||||
bool was_redirected = ps->redirected;
|
||||
struct managed_win *bottom = NULL;
|
||||
struct win *bottom = NULL;
|
||||
if (!paint_preprocess(ps, &animation, &bottom)) {
|
||||
log_fatal("Pre-render preparation has failed, exiting...");
|
||||
exit(1);
|
||||
|
@ -2518,8 +2532,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
// think there still could be race condition that mandates discarding the events.
|
||||
x_discard_events(&ps->c);
|
||||
|
||||
xcb_query_tree_reply_t *query_tree_reply = xcb_query_tree_reply(
|
||||
ps->c.c, xcb_query_tree(ps->c.c, ps->c.screen_info->root), NULL);
|
||||
wm_complete_import(ps->wm, &ps->c, ps->atoms);
|
||||
|
||||
e = xcb_request_check(ps->c.c, xcb_ungrab_server_checked(ps->c.c));
|
||||
if (e) {
|
||||
|
@ -2530,22 +2543,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
|
||||
ps->server_grabbed = false;
|
||||
|
||||
if (query_tree_reply) {
|
||||
xcb_window_t *children;
|
||||
int nchildren;
|
||||
|
||||
children = xcb_query_tree_children(query_tree_reply);
|
||||
nchildren = xcb_query_tree_children_length(query_tree_reply);
|
||||
|
||||
for (int i = 0; i < nchildren; i++) {
|
||||
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, wm_stack_end(ps->wm), stack_neighbour) {
|
||||
log_debug("%#010x", w->id);
|
||||
wm_stack_foreach(ps->wm, i) {
|
||||
log_debug(" %#010x", wm_ref_win_id(i));
|
||||
}
|
||||
|
||||
ps->command_builder = command_builder_new();
|
||||
|
@ -2598,8 +2598,11 @@ static void session_destroy(session_t *ps) {
|
|||
}
|
||||
#endif
|
||||
|
||||
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
||||
free_win_res(ps, w);
|
||||
wm_stack_foreach(ps->wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w != NULL) {
|
||||
free_win_res(ps, w);
|
||||
}
|
||||
}
|
||||
|
||||
// Free blacklists
|
||||
|
@ -2706,7 +2709,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);
|
||||
wm_free(ps->wm);
|
||||
free_x_connection(&ps->c);
|
||||
}
|
||||
|
||||
|
|
23
src/render.c
23
src/render.c
|
@ -345,7 +345,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
|
|||
}
|
||||
|
||||
static inline void
|
||||
paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid, int hei,
|
||||
paint_region(session_t *ps, const struct win *w, int x, int y, int wid, int hei,
|
||||
double opacity, const region_t *reg_paint, xcb_render_picture_t pict) {
|
||||
const int dx = (w ? w->g.x : 0) + x;
|
||||
const int dy = (w ? w->g.y : 0) + y;
|
||||
|
@ -392,19 +392,19 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
|
|||
/**
|
||||
* Paint a window itself and dim it if asked.
|
||||
*/
|
||||
void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) {
|
||||
void paint_one(session_t *ps, struct win *w, const region_t *reg_paint) {
|
||||
// Fetch Pixmap
|
||||
if (!w->paint.pixmap) {
|
||||
w->paint.pixmap = x_new_id(&ps->c);
|
||||
set_ignore_cookie(&ps->c, xcb_composite_name_window_pixmap(
|
||||
ps->c.c, w->base.id, w->paint.pixmap));
|
||||
ps->c.c, win_id(w), w->paint.pixmap));
|
||||
}
|
||||
|
||||
xcb_drawable_t draw = w->paint.pixmap;
|
||||
if (!draw) {
|
||||
log_error("Failed to get pixmap from window %#010x (%s), window won't be "
|
||||
"visible",
|
||||
w->base.id, w->name);
|
||||
win_id(w), w->name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -424,12 +424,12 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
|||
// causing the jittering issue M4he reported in #7.
|
||||
if (!paint_bind_tex(ps, &w->paint, 0, 0, false, 0, w->a.visual,
|
||||
(!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) {
|
||||
log_error("Failed to bind texture for window %#010x.", w->base.id);
|
||||
log_error("Failed to bind texture for window %#010x.", win_id(w));
|
||||
}
|
||||
w->pixmap_damaged = false;
|
||||
|
||||
if (!paint_isvalid(ps, &w->paint)) {
|
||||
log_error("Window %#010x is missing painting data.", w->base.id);
|
||||
log_error("Window %#010x is missing painting data.", win_id(w));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -681,7 +681,7 @@ static void paint_root(session_t *ps, const region_t *reg_paint) {
|
|||
/**
|
||||
* Generate shadow <code>Picture</code> for a window.
|
||||
*/
|
||||
static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) {
|
||||
static bool win_build_shadow(session_t *ps, struct win *w, double opacity) {
|
||||
const int width = w->widthb;
|
||||
const int height = w->heightb;
|
||||
// log_trace("(): building shadow for %s %d %d", w->name, width, height);
|
||||
|
@ -761,13 +761,12 @@ shadow_picture_err:
|
|||
/**
|
||||
* Paint the shadow of a window.
|
||||
*/
|
||||
static inline void
|
||||
win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
|
||||
static inline void win_paint_shadow(session_t *ps, struct win *w, region_t *reg_paint) {
|
||||
// Bind shadow pixmap to GLX texture if needed
|
||||
paint_bind_tex(ps, &w->shadow_paint, 0, 0, false, 32, 0, false);
|
||||
|
||||
if (!paint_isvalid(ps, &w->shadow_paint)) {
|
||||
log_error("Window %#010x is missing shadow data.", w->base.id);
|
||||
log_error("Window %#010x is missing shadow data.", win_id(w));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -897,7 +896,7 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y
|
|||
* Blur the background of a window.
|
||||
*/
|
||||
static inline void
|
||||
win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t tgt_buffer,
|
||||
win_blur_background(session_t *ps, struct win *w, xcb_render_picture_t tgt_buffer,
|
||||
const region_t *reg_paint) {
|
||||
const int16_t x = w->g.x;
|
||||
const int16_t y = w->g.y;
|
||||
|
@ -1001,7 +1000,7 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
|
|||
/// paint all windows
|
||||
/// region = ??
|
||||
/// region_real = the damage region
|
||||
void paint_all(session_t *ps, struct managed_win *t) {
|
||||
void paint_all(session_t *ps, struct win *t) {
|
||||
if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) {
|
||||
if (ps->xsync_exists && !x_fence_sync(&ps->c, ps->sync_fence)) {
|
||||
log_error("x_fence_sync failed, xrender-sync-fence will be "
|
||||
|
|
|
@ -14,7 +14,7 @@ typedef struct _glx_texture glx_texture_t;
|
|||
typedef struct glx_prog_main glx_prog_main_t;
|
||||
typedef struct session session_t;
|
||||
|
||||
struct managed_win;
|
||||
struct win;
|
||||
|
||||
typedef struct paint {
|
||||
xcb_pixmap_t pixmap;
|
||||
|
@ -35,9 +35,9 @@ void render(session_t *ps, int x, int y, int dx, int dy, int w, int h, int fullw
|
|||
int fullh, double opacity, bool argb, bool neg, int cr,
|
||||
xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint,
|
||||
const glx_prog_main_t *pprogram, clip_t *clip);
|
||||
void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint);
|
||||
void paint_one(session_t *ps, struct win *w, const region_t *reg_paint);
|
||||
|
||||
void paint_all(session_t *ps, struct managed_win *const t);
|
||||
void paint_all(session_t *ps, struct win *const t);
|
||||
|
||||
void free_paint(session_t *ps, paint_t *ppaint);
|
||||
void free_root_tile(session_t *ps);
|
||||
|
|
|
@ -110,7 +110,7 @@ command_for_shadow(struct layer *layer, struct backend_command *cmd,
|
|||
layer->shadow.origin.x, layer->shadow.origin.y,
|
||||
(unsigned)shadow_size_scaled.width,
|
||||
(unsigned)shadow_size_scaled.height);
|
||||
log_trace("Calculate shadow for %#010x (%s)", w->base.id, w->name);
|
||||
log_trace("Calculate shadow for %#010x (%s)", win_id(w), w->name);
|
||||
log_region(TRACE, &cmd->target_mask);
|
||||
if (!wintype_options[w->window_type].full_shadow) {
|
||||
// We need to not draw under the window
|
||||
|
|
|
@ -8,14 +8,6 @@
|
|||
|
||||
#include "damage.h"
|
||||
|
||||
static inline bool attr_unused layer_key_eq(const struct layer_key *a,
|
||||
const struct layer_key *b) {
|
||||
if (!a->generation || !b->generation) {
|
||||
return false;
|
||||
}
|
||||
return a->window == b->window && a->generation == b->generation;
|
||||
}
|
||||
|
||||
/// Compare two layers that contain the same window, return if they are the "same". Same
|
||||
/// means these two layers are render in the same way at the same position, with the only
|
||||
/// possible differences being the contents inside the window.
|
||||
|
@ -164,7 +156,7 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age,
|
|||
auto layout = layout_manager_layout(lm, l);
|
||||
dynarr_foreach(layout->layers, layer) {
|
||||
log_trace("\t%#010x %dx%d+%dx%d (prev %d, next %d)",
|
||||
layer->key.window, layer->window.size.width,
|
||||
layer->key.x, layer->window.size.width,
|
||||
layer->window.size.height,
|
||||
layer->window.origin.x, layer->window.origin.y,
|
||||
layer->prev_rank, layer->next_rank);
|
||||
|
@ -265,9 +257,9 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age,
|
|||
break;
|
||||
}
|
||||
|
||||
assert(layer_key_eq(&past_layer->key, &curr_layer->key));
|
||||
log_trace("%#010x == %#010x %s", past_layer->key.window,
|
||||
curr_layer->key.window, curr_layer->win->name);
|
||||
assert(wm_treeid_eq(past_layer->key, curr_layer->key));
|
||||
log_trace("%#010x == %#010x %s", past_layer->key.x, curr_layer->key.x,
|
||||
curr_layer->win->name);
|
||||
|
||||
if (!layer_compare(past_layer, past_layer_cmd, curr_layer, curr_layer_cmd)) {
|
||||
region_union_render_layer(damage, curr_layer, curr_layer_cmd);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "layout.h"
|
||||
struct layer_index {
|
||||
UT_hash_handle hh;
|
||||
struct layer_key key;
|
||||
wm_treeid key;
|
||||
unsigned index;
|
||||
struct list_node free_list;
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ struct layout_manager {
|
|||
|
||||
/// Compute layout of a layer from a window. Returns false if the window is not
|
||||
/// visible / should not be rendered. `out_layer` is modified either way.
|
||||
static bool layer_from_window(struct layer *out_layer, struct managed_win *w, ivec2 size) {
|
||||
static bool layer_from_window(struct layer *out_layer, struct win *w, ivec2 size) {
|
||||
bool to_paint = false;
|
||||
if (!w->ever_damaged || w->paint_excluded) {
|
||||
goto out;
|
||||
|
@ -108,8 +108,7 @@ static bool layer_from_window(struct layer *out_layer, struct managed_win *w, iv
|
|||
out_layer->is_clipping = w->transparent_clipping;
|
||||
out_layer->next_rank = -1;
|
||||
out_layer->prev_rank = -1;
|
||||
out_layer->key =
|
||||
(struct layer_key){.window = w->base.id, .generation = w->base.generation};
|
||||
out_layer->key = wm_ref_treeid(w->tree_ref);
|
||||
out_layer->win = w;
|
||||
to_paint = true;
|
||||
|
||||
|
@ -181,20 +180,17 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm,
|
|||
auto layout = &lm->layouts[lm->current];
|
||||
command_builder_command_list_free(layout->commands);
|
||||
layout->root_image_generation = root_pixmap_generation;
|
||||
|
||||
unsigned nlayers = wm_stack_num_managed_windows(wm);
|
||||
dynarr_resize(layout->layers, nlayers, layer_init, layer_deinit);
|
||||
layout->size = size;
|
||||
|
||||
unsigned rank = 0;
|
||||
struct layer_index *index, *next_index;
|
||||
for (struct list_node *cursor = wm_stack_end(wm)->prev;
|
||||
cursor != wm_stack_end(wm); cursor = cursor->prev) {
|
||||
auto w = list_entry(cursor, struct win, stack_neighbour);
|
||||
if (!w->managed) {
|
||||
wm_stack_foreach_rev(wm, cursor) {
|
||||
auto w = wm_ref_deref(cursor);
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!layer_from_window(&layout->layers[rank], (struct managed_win *)w, size)) {
|
||||
dynarr_resize(layout->layers, rank + 1, layer_init, layer_deinit);
|
||||
if (!layer_from_window(&layout->layers[rank], (struct win *)w, size)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -205,9 +201,8 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm,
|
|||
layout->layers[rank].prev_rank = (int)index->index;
|
||||
}
|
||||
rank++;
|
||||
assert(rank <= nlayers);
|
||||
}
|
||||
dynarr_resize(layout->layers, rank, layer_init, layer_deinit);
|
||||
dynarr_truncate(layout->layers, rank, layer_deinit);
|
||||
|
||||
// Update indices. If a layer exist in both prev_layout and current layout,
|
||||
// we could update the index using next_rank; if a layer no longer exist in
|
||||
|
|
|
@ -9,26 +9,15 @@
|
|||
#include <picom/types.h>
|
||||
|
||||
#include "region.h"
|
||||
|
||||
struct layer_key {
|
||||
/// Window generation, (see `struct wm::generation` for explanation of what a
|
||||
/// generation is)
|
||||
uint64_t generation;
|
||||
/// Window ID
|
||||
xcb_window_t window;
|
||||
uint32_t pad; // explicit padding because this will be used as hash table
|
||||
// key
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct layer_key) == 16, "layer_key has implicit padding");
|
||||
#include "wm/wm.h"
|
||||
|
||||
/// A layer to be rendered in a render layout
|
||||
struct layer {
|
||||
/// Window that will be rendered in this layer
|
||||
struct layer_key key;
|
||||
wm_treeid key;
|
||||
/// The window, this is only valid for the current layout. Once
|
||||
/// a frame has passed, windows could have been freed.
|
||||
struct managed_win *win;
|
||||
struct win *win;
|
||||
/// Damaged region of this layer, in screen coordinates
|
||||
region_t damaged;
|
||||
/// Window rectangle in screen coordinates, before it's scaled.
|
||||
|
|
|
@ -182,7 +182,7 @@ renderer_set_root_size(struct renderer *r, struct backend_base *backend, ivec2 r
|
|||
}
|
||||
|
||||
static bool
|
||||
renderer_bind_mask(struct renderer *r, struct backend_base *backend, struct managed_win *w) {
|
||||
renderer_bind_mask(struct renderer *r, struct backend_base *backend, struct win *w) {
|
||||
ivec2 size = {.width = w->widthb, .height = w->heightb};
|
||||
bool succeeded = false;
|
||||
auto image = backend->ops.new_image(backend, BACKEND_IMAGE_FORMAT_MASK, size);
|
||||
|
@ -346,8 +346,8 @@ out:
|
|||
return shadow_image;
|
||||
}
|
||||
|
||||
static bool renderer_bind_shadow(struct renderer *r, struct backend_base *backend,
|
||||
struct managed_win *w) {
|
||||
static bool
|
||||
renderer_bind_shadow(struct renderer *r, struct backend_base *backend, struct win *w) {
|
||||
if (backend->ops.quirks(backend) & BACKEND_QUIRK_SLOW_BLUR) {
|
||||
xcb_pixmap_t shadow = XCB_NONE;
|
||||
xcb_render_picture_t pict = XCB_NONE;
|
||||
|
@ -398,7 +398,7 @@ static bool renderer_prepare_commands(struct renderer *r, struct backend_base *b
|
|||
assert(layer->number_of_commands > 0);
|
||||
layer_end = cmd + layer->number_of_commands;
|
||||
log_trace("Prepare commands for layer %#010x @ %#010x (%s)",
|
||||
layer->win->base.id, layer->win->client_win,
|
||||
win_id(layer->win), win_client_id(layer->win, false),
|
||||
layer->win->name);
|
||||
}
|
||||
|
||||
|
@ -522,8 +522,8 @@ bool renderer_render(struct renderer *r, struct backend_base *backend,
|
|||
layer += 1;
|
||||
layer_end += layer->number_of_commands;
|
||||
log_trace("Layer for window %#010x @ %#010x (%s)",
|
||||
layer->win->base.id, layer->win->client_win,
|
||||
layer->win->name);
|
||||
win_id(layer->win),
|
||||
win_client_id(layer->win, false), layer->win->name);
|
||||
}
|
||||
log_backend_command(TRACE, *i);
|
||||
}
|
||||
|
|
|
@ -324,6 +324,7 @@ void wm_tree_destroy_window(struct wm_tree *tree, struct wm_tree_node *node) {
|
|||
"malfunction.",
|
||||
node->id.x);
|
||||
list_foreach_safe(struct wm_tree_node, i, &node->children, siblings) {
|
||||
log_error(" Child window %#010x", i->id.x);
|
||||
wm_tree_destroy_window(tree, i);
|
||||
}
|
||||
}
|
||||
|
@ -341,7 +342,6 @@ void wm_tree_destroy_window(struct wm_tree *tree, struct wm_tree_node *node) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Move `node` to the top or the bottom of its parent's child window stack.
|
||||
void wm_tree_move_to_end(struct wm_tree *tree, struct wm_tree_node *node, bool to_bottom) {
|
||||
BUG_ON(node == NULL);
|
||||
BUG_ON(node->parent == NULL); // Trying to move the root window
|
||||
|
|
861
src/wm/win.c
861
src/wm/win.c
File diff suppressed because it is too large
Load diff
200
src/wm/win.h
200
src/wm/win.h
|
@ -20,20 +20,26 @@
|
|||
#include "transition/script.h"
|
||||
#include "utils/list.h"
|
||||
#include "utils/misc.h"
|
||||
#include "wm/wm.h"
|
||||
#include "x.h"
|
||||
#include "xcb/xproto.h"
|
||||
|
||||
struct backend_base;
|
||||
typedef struct session session_t;
|
||||
typedef struct _glx_texture glx_texture_t;
|
||||
struct wm_cursor;
|
||||
|
||||
#define win_stack_foreach_managed(w, win_stack) \
|
||||
list_foreach(struct managed_win, w, win_stack, \
|
||||
base.stack_neighbour) if ((w)->base.managed)
|
||||
#define wm_stack_foreach(wm, i) \
|
||||
for (struct wm_ref * (i) = wm_ref_topmost_child(wm_root_ref(wm)); (i); \
|
||||
(i) = wm_ref_below(i))
|
||||
#define wm_stack_foreach_rev(wm, i) \
|
||||
for (struct wm_ref * (i) = wm_ref_bottommost_child(wm_root_ref(wm)); (i); \
|
||||
(i) = wm_ref_above(i))
|
||||
|
||||
#define win_stack_foreach_managed_safe(w, win_stack) \
|
||||
list_foreach_safe(struct managed_win, w, win_stack, \
|
||||
base.stack_neighbour) if ((w)->base.managed)
|
||||
#define wm_stack_foreach_safe(wm, i, next_i) \
|
||||
for (struct wm_ref * (i) = wm_ref_topmost_child(wm_root_ref(wm)), \
|
||||
*(next_i) = (i) != NULL ? wm_ref_below(i) : NULL; \
|
||||
(i); (i) = (next_i), (next_i) = (i) != NULL ? wm_ref_below(i) : NULL)
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
// FIXME this type should be in opengl.h
|
||||
|
@ -73,25 +79,6 @@ struct window_stack_entry {
|
|||
* considered part of the window.
|
||||
*/
|
||||
|
||||
/// Structure representing a top-level managed window.
|
||||
struct win {
|
||||
UT_hash_handle hh;
|
||||
struct list_node stack_neighbour;
|
||||
/// ID of the top-level frame window.
|
||||
xcb_window_t id;
|
||||
/// Generation of the window.
|
||||
/// (see `struct wm::generation` for explanation of what a generation is)
|
||||
uint64_t generation;
|
||||
/// Whether the window is destroyed from Xorg's perspective
|
||||
bool destroyed : 1;
|
||||
/// True if we just received CreateNotify, and haven't queried X for any info
|
||||
/// about the window
|
||||
bool is_new : 1;
|
||||
/// True if this window is managed, i.e. this struct is actually a `managed_win`.
|
||||
/// Always false if `is_new` is true.
|
||||
bool managed : 1;
|
||||
};
|
||||
|
||||
struct win_geometry {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
|
@ -106,22 +93,20 @@ struct win_state_change {
|
|||
winstate_t state;
|
||||
};
|
||||
|
||||
struct managed_win {
|
||||
struct win base;
|
||||
struct win {
|
||||
/// Reference back to the position of this window inside the window tree.
|
||||
struct wm_ref *tree_ref;
|
||||
/// backend data attached to this window. Only available when
|
||||
/// `state` is not UNMAPPED
|
||||
image_handle win_image;
|
||||
image_handle shadow_image;
|
||||
image_handle mask_image;
|
||||
// TODO(yshui) only used by legacy backends, remove.
|
||||
/// Pointer to the next higher window to paint.
|
||||
struct managed_win *prev_trans;
|
||||
/// Number of windows above this window
|
||||
int stacking_rank;
|
||||
struct win *prev_trans;
|
||||
// TODO(yshui) rethink reg_ignore
|
||||
|
||||
// Core members
|
||||
/// The "mapped state" of this window, doesn't necessary
|
||||
/// match X mapped state, because of fading.
|
||||
winstate_t state;
|
||||
/// Window attributes.
|
||||
xcb_get_window_attributes_reply_t a;
|
||||
|
@ -184,14 +169,12 @@ struct managed_win {
|
|||
bool in_openclose;
|
||||
|
||||
// Client window related members
|
||||
/// ID of the top-level client window of the window.
|
||||
xcb_window_t client_win;
|
||||
/// Type of the window.
|
||||
wintype_t window_type;
|
||||
/// Leader window ID of the window.
|
||||
xcb_window_t leader;
|
||||
/// Cached topmost window ID of the window.
|
||||
xcb_window_t cache_leader;
|
||||
/// Cached topmost window ID of the leader window.
|
||||
struct wm_ref *cache_leader;
|
||||
|
||||
// Focus-related members
|
||||
/// Whether the window is to be considered focused.
|
||||
|
@ -357,47 +340,46 @@ static const struct script_output_info win_script_outputs[] = {
|
|||
/// section. Returns true if the window had an animation running and it has just finished,
|
||||
/// or if the window's states just changed and there is no animation defined for this
|
||||
/// state change.
|
||||
bool win_process_animation_and_state_change(struct session *ps, struct managed_win *w,
|
||||
double delta_t);
|
||||
double win_animatable_get(const struct managed_win *w, enum win_script_output output);
|
||||
void win_process_update_flags(session_t *ps, struct managed_win *w);
|
||||
void win_process_image_flags(session_t *ps, struct managed_win *w);
|
||||
bool win_process_animation_and_state_change(struct session *ps, struct win *w, double delta_t);
|
||||
double win_animatable_get(const struct win *w, enum win_script_output output);
|
||||
void win_process_update_flags(session_t *ps, struct win *w);
|
||||
void win_process_image_flags(session_t *ps, struct win *w);
|
||||
|
||||
/// Start the unmap of a window. We cannot unmap immediately since we might need to fade
|
||||
/// the window out.
|
||||
void unmap_win_start(struct managed_win *);
|
||||
void unmap_win_finish(session_t *ps, struct managed_win *w);
|
||||
void unmap_win_start(struct win *);
|
||||
void unmap_win_finish(session_t *ps, struct win *w);
|
||||
/// Start the destroying of a window. Windows cannot always be destroyed immediately
|
||||
/// because of fading and such.
|
||||
void destroy_win_start(session_t *ps, struct win *w);
|
||||
|
||||
void win_destroy_start(session_t *ps, struct win *w);
|
||||
void win_map_start(struct win *w);
|
||||
/// Release images bound with a window, set the *_NONE flags on the window. Only to be
|
||||
/// used when de-initializing the backend outside of win.c
|
||||
void win_release_images(struct backend_base *base, struct managed_win *w);
|
||||
winmode_t attr_pure win_calc_mode_raw(const struct managed_win *w);
|
||||
void win_release_images(struct backend_base *base, struct win *w);
|
||||
winmode_t attr_pure win_calc_mode_raw(const struct win *w);
|
||||
// TODO(yshui) `win_calc_mode` is only used by legacy backends
|
||||
winmode_t attr_pure win_calc_mode(const struct managed_win *w);
|
||||
void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val);
|
||||
void win_set_fade_force(struct managed_win *w, switch_t val);
|
||||
void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val);
|
||||
void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t val);
|
||||
winmode_t attr_pure win_calc_mode(const struct win *w);
|
||||
void win_set_shadow_force(session_t *ps, struct win *w, switch_t val);
|
||||
void win_set_fade_force(struct win *w, switch_t val);
|
||||
void win_set_focused_force(session_t *ps, struct win *w, switch_t val);
|
||||
void win_set_invert_color_force(session_t *ps, struct win *w, switch_t val);
|
||||
/**
|
||||
* Set real focused state of a window.
|
||||
*/
|
||||
void win_set_focused(session_t *ps, struct managed_win *w);
|
||||
void win_on_factor_change(session_t *ps, struct managed_win *w);
|
||||
void win_unmark_client(struct managed_win *w);
|
||||
void win_set_focused(session_t *ps, struct win *w);
|
||||
void win_on_factor_change(session_t *ps, struct win *w);
|
||||
void win_on_client_update(session_t *ps, struct win *w);
|
||||
|
||||
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
|
||||
bool attr_pure win_should_dim(session_t *ps, const struct win *w);
|
||||
|
||||
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw);
|
||||
void win_update_monitor(struct x_monitors *monitors, struct win *mw);
|
||||
|
||||
/// Recheck if a window is fullscreen
|
||||
void win_update_is_fullscreen(const session_t *ps, struct managed_win *w);
|
||||
void win_update_is_fullscreen(const session_t *ps, struct win *w);
|
||||
/**
|
||||
* Check if a window has BYPASS_COMPOSITOR property set
|
||||
*/
|
||||
bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w);
|
||||
bool win_is_bypassing_compositor(const session_t *ps, const struct win *w);
|
||||
/**
|
||||
* Get a rectangular region in global coordinates a window (and possibly
|
||||
* its shadow) occupies.
|
||||
|
@ -405,114 +387,112 @@ bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *
|
|||
* Note w->shadow and shadow geometry must be correct before calling this
|
||||
* function.
|
||||
*/
|
||||
void win_extents(const struct managed_win *w, region_t *res);
|
||||
region_t win_extents_by_val(const struct managed_win *w);
|
||||
void win_extents(const struct win *w, region_t *res);
|
||||
region_t win_extents_by_val(const struct win *w);
|
||||
/**
|
||||
* Add a window to damaged area.
|
||||
*
|
||||
* @param ps current session
|
||||
* @param w struct _win element representing the window
|
||||
*/
|
||||
void add_damage_from_win(session_t *ps, const struct managed_win *w);
|
||||
void add_damage_from_win(session_t *ps, const struct win *w);
|
||||
/**
|
||||
* Get a rectangular region a window occupies, excluding frame and shadow.
|
||||
*
|
||||
* Return region in global coordinates.
|
||||
*/
|
||||
void win_get_region_noframe_local(const struct managed_win *w, region_t *);
|
||||
void win_get_region_noframe_local_without_corners(const struct managed_win *w, region_t *);
|
||||
void win_get_region_noframe_local(const struct win *w, region_t *);
|
||||
void win_get_region_noframe_local_without_corners(const struct win *w, region_t *);
|
||||
|
||||
/// Get the region for the frame of the window
|
||||
void win_get_region_frame_local(const struct managed_win *w, region_t *res);
|
||||
void win_get_region_frame_local(const struct 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);
|
||||
region_t win_get_region_frame_local_by_val(const struct win *w);
|
||||
/// 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);
|
||||
struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor);
|
||||
|
||||
/**
|
||||
* Release a destroyed window that is no longer needed.
|
||||
*/
|
||||
void destroy_win_finish(session_t *ps, struct win *w);
|
||||
void win_destroy_finish(session_t *ps, struct win *w);
|
||||
|
||||
/**
|
||||
* Check if a window is focused, without using any focus rules or forced focus settings
|
||||
*/
|
||||
bool attr_pure win_is_focused_raw(const struct managed_win *w);
|
||||
bool attr_pure win_is_focused_raw(const struct win *w);
|
||||
|
||||
/// check if window has ARGB visual
|
||||
bool attr_pure win_has_alpha(const struct managed_win *w);
|
||||
bool attr_pure win_has_alpha(const struct win *w);
|
||||
|
||||
/// Whether it looks like a WM window. We consider a window WM window if
|
||||
/// it does not have a decedent with WM_STATE and it is not override-
|
||||
/// redirected itself.
|
||||
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 bool attr_pure win_is_wmwin(const struct win *w) {
|
||||
return wm_ref_client_of(w->tree_ref) == NULL && !w->a.override_redirect;
|
||||
}
|
||||
|
||||
static inline struct managed_win *win_as_managed(struct win *w) {
|
||||
BUG_ON(!w->managed);
|
||||
return (struct managed_win *)w;
|
||||
static inline xcb_window_t win_id(const struct win *w) {
|
||||
return wm_ref_win_id(w->tree_ref);
|
||||
}
|
||||
|
||||
static inline const char *win_get_name_if_managed(const struct win *w) {
|
||||
if (!w->managed) {
|
||||
return "(unmanaged)";
|
||||
/// Returns the client window of a window. If a client window does not exist, returns the
|
||||
/// window itself when `fallback_to_self` is true, otherwise returns XCB_NONE.
|
||||
static inline xcb_window_t win_client_id(const struct win *w, bool fallback_to_self) {
|
||||
auto client_win = wm_ref_client_of(w->tree_ref);
|
||||
if (client_win == NULL) {
|
||||
return fallback_to_self ? win_id(w) : XCB_NONE;
|
||||
}
|
||||
auto mw = (struct managed_win *)w;
|
||||
return mw->name;
|
||||
return wm_ref_win_id(client_win);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
bool attr_pure win_is_region_ignore_valid(session_t *ps, const struct win *w);
|
||||
|
||||
/// Whether a given window is mapped on the X server side
|
||||
bool win_is_mapped_in_x(const struct managed_win *w);
|
||||
bool win_is_mapped_in_x(const struct win *w);
|
||||
/// Set flags on a window. Some sanity checks are performed
|
||||
void win_set_flags(struct managed_win *w, uint64_t flags);
|
||||
void win_set_flags(struct win *w, uint64_t flags);
|
||||
/// Clear flags on a window. Some sanity checks are performed
|
||||
void win_clear_flags(struct managed_win *w, uint64_t flags);
|
||||
void win_clear_flags(struct win *w, uint64_t flags);
|
||||
/// Returns true if any of the flags in `flags` is set
|
||||
bool win_check_flags_any(struct managed_win *w, uint64_t flags);
|
||||
bool win_check_flags_any(struct win *w, uint64_t flags);
|
||||
/// Returns true if all of the flags in `flags` are set
|
||||
bool win_check_flags_all(struct managed_win *w, uint64_t flags);
|
||||
bool win_check_flags_all(struct 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);
|
||||
void win_set_properties_stale(struct win *w, const xcb_atom_t *prop, int nprops);
|
||||
|
||||
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);
|
||||
bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct win *w);
|
||||
/**
|
||||
* Retrieve frame extents from a window.
|
||||
*/
|
||||
void win_update_frame_extents(struct x_connection *c, struct atom *atoms,
|
||||
struct managed_win *w, xcb_window_t client,
|
||||
double frame_opacity);
|
||||
void win_update_frame_extents(struct x_connection *c, struct atom *atoms, struct win *w,
|
||||
xcb_window_t client, double frame_opacity);
|
||||
/**
|
||||
* Retrieve the <code>WM_CLASS</code> of a window and update its
|
||||
* <code>win</code> structure.
|
||||
*/
|
||||
bool win_update_class(struct x_connection *c, struct atom *atoms, struct managed_win *w);
|
||||
int win_update_role(struct x_connection *c, struct atom *atoms, struct managed_win *w);
|
||||
int win_update_name(struct x_connection *c, struct atom *atoms, struct managed_win *w);
|
||||
void win_on_win_size_change(struct managed_win *w, int shadow_offset_x,
|
||||
int shadow_offset_y, int shadow_radius);
|
||||
void win_update_bounding_shape(struct x_connection *c, struct managed_win *w,
|
||||
bool shape_exists, bool detect_rounded_corners);
|
||||
bool win_update_class(struct x_connection *c, struct atom *atoms, struct win *w);
|
||||
int win_update_role(struct x_connection *c, struct atom *atoms, struct win *w);
|
||||
int win_update_name(struct x_connection *c, struct atom *atoms, struct win *w);
|
||||
void win_on_win_size_change(struct win *w, int shadow_offset_x, int shadow_offset_y,
|
||||
int shadow_radius);
|
||||
void win_update_bounding_shape(struct x_connection *c, struct win *w, bool shape_exists,
|
||||
bool detect_rounded_corners);
|
||||
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
|
||||
struct managed_win *w);
|
||||
struct win *w);
|
||||
|
||||
static inline attr_unused void win_set_property_stale(struct managed_win *w, xcb_atom_t prop) {
|
||||
static inline attr_unused void win_set_property_stale(struct win *w, xcb_atom_t prop) {
|
||||
return win_set_properties_stale(w, (xcb_atom_t[]){prop}, 1);
|
||||
}
|
||||
|
||||
/// Free all resources in a struct win
|
||||
void free_win_res(session_t *ps, struct managed_win *w);
|
||||
void free_win_res(session_t *ps, struct win *w);
|
||||
|
||||
/// Remove the corners of window `w` from region `res`. `origin` is the top-left corner of
|
||||
/// `w` in `res`'s coordinate system.
|
||||
static inline void
|
||||
win_region_remove_corners(const struct managed_win *w, ivec2 origin, region_t *res) {
|
||||
win_region_remove_corners(const struct win *w, ivec2 origin, region_t *res) {
|
||||
static const int corner_index[][2] = {
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
|
@ -535,12 +515,11 @@ win_region_remove_corners(const struct managed_win *w, ivec2 origin, region_t *r
|
|||
}
|
||||
|
||||
/// Like `win_region_remove_corners`, but `origin` is (0, 0).
|
||||
static inline void
|
||||
win_region_remove_corners_local(const struct managed_win *w, region_t *res) {
|
||||
static inline void win_region_remove_corners_local(const struct win *w, region_t *res) {
|
||||
win_region_remove_corners(w, (ivec2){0, 0}, res);
|
||||
}
|
||||
|
||||
static inline region_t attr_unused win_get_bounding_shape_global_by_val(struct managed_win *w) {
|
||||
static inline region_t attr_unused win_get_bounding_shape_global_by_val(struct win *w) {
|
||||
region_t ret;
|
||||
pixman_region32_init(&ret);
|
||||
pixman_region32_copy(&ret, &w->bounding_shape);
|
||||
|
@ -548,8 +527,7 @@ static inline region_t attr_unused win_get_bounding_shape_global_by_val(struct m
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline region_t
|
||||
win_get_bounding_shape_global_without_corners_by_val(struct managed_win *w) {
|
||||
static inline region_t win_get_bounding_shape_global_without_corners_by_val(struct win *w) {
|
||||
region_t ret;
|
||||
pixman_region32_init(&ret);
|
||||
pixman_region32_copy(&ret, &w->bounding_shape);
|
||||
|
@ -562,7 +540,7 @@ win_get_bounding_shape_global_without_corners_by_val(struct managed_win *w) {
|
|||
* Calculate the extents of the frame of the given window based on EWMH
|
||||
* _NET_FRAME_EXTENTS and the X window border width.
|
||||
*/
|
||||
static inline margin_t attr_pure attr_unused win_calc_frame_extents(const struct managed_win *w) {
|
||||
static inline margin_t attr_pure attr_unused win_calc_frame_extents(const struct win *w) {
|
||||
margin_t result = w->frame_extents;
|
||||
result.top = max2(result.top, w->g.border_width);
|
||||
result.left = max2(result.left, w->g.border_width);
|
||||
|
@ -574,7 +552,7 @@ static inline margin_t attr_pure attr_unused win_calc_frame_extents(const struct
|
|||
/**
|
||||
* Check whether a window has WM frames.
|
||||
*/
|
||||
static inline bool attr_pure attr_unused win_has_frame(const struct managed_win *w) {
|
||||
static inline bool attr_pure attr_unused win_has_frame(const struct win *w) {
|
||||
return w->g.border_width || w->frame_extents.top || w->frame_extents.left ||
|
||||
w->frame_extents.right || w->frame_extents.bottom;
|
||||
}
|
||||
|
|
404
src/wm/wm.c
404
src/wm/wm.c
|
@ -9,7 +9,6 @@
|
|||
#include "log.h"
|
||||
#include "utils/dynarr.h"
|
||||
#include "utils/list.h"
|
||||
#include "utils/uthash_extra.h"
|
||||
#include "x.h"
|
||||
|
||||
#include "win.h"
|
||||
|
@ -17,32 +16,15 @@
|
|||
#include "wm_internal.h"
|
||||
|
||||
struct wm {
|
||||
/// Current window generation, start from 1. 0 is reserved for using as
|
||||
/// an invalid generation number.
|
||||
///
|
||||
/// Because X server recycles window IDs, `id` along
|
||||
/// is not enough to uniquely identify a window. This generation number is
|
||||
/// incremented every time a window is destroyed, so that if a window ID is
|
||||
/// reused, its generation number will be different from before.
|
||||
/// Unless, of course, if the generation number overflows, but since we are
|
||||
/// using a uint64_t here, that won't happen for a very long time. Still,
|
||||
/// it is recommended that you restart the compositor at least once before
|
||||
/// the Universe collapse back on itself.
|
||||
uint64_t generation;
|
||||
/// 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;
|
||||
struct 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;
|
||||
struct wm_tree_node *active_leader;
|
||||
struct wm_tree tree;
|
||||
|
||||
struct wm_tree_node *root;
|
||||
|
@ -55,14 +37,6 @@ struct wm {
|
|||
struct wm_tree_node **masked;
|
||||
};
|
||||
|
||||
unsigned int wm_get_window_count(struct wm *wm) {
|
||||
unsigned int count = 0;
|
||||
HASH_ITER2(wm->windows, w) {
|
||||
assert(!w->destroyed);
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
// TODO(yshui): this is a bit weird and I am not decided on it yet myself. Maybe we can
|
||||
// expose `wm_tree_node` directly. But maybe we want to bundle some additional data with
|
||||
// it. Anyway, this is probably easy to get rid of if we want to.
|
||||
|
@ -81,12 +55,35 @@ static inline const struct wm_tree_node *to_tree_node(const struct wm_ref *curso
|
|||
: NULL;
|
||||
}
|
||||
|
||||
static inline struct wm_tree_node *to_tree_node_mut(struct wm_ref *cursor) {
|
||||
return cursor != NULL ? list_entry(&cursor->inner, struct wm_tree_node, siblings)
|
||||
: NULL;
|
||||
}
|
||||
|
||||
xcb_window_t wm_ref_win_id(const struct wm_ref *cursor) {
|
||||
return to_tree_node(cursor)->id.x;
|
||||
}
|
||||
|
||||
struct managed_win *wm_active_win(struct wm *wm) {
|
||||
return wm->active_win;
|
||||
wm_treeid wm_ref_treeid(const struct wm_ref *cursor) {
|
||||
return to_tree_node(cursor)->id;
|
||||
}
|
||||
|
||||
struct win *wm_ref_deref(const struct wm_ref *cursor) {
|
||||
auto node = to_tree_node(cursor);
|
||||
if (node->parent == NULL) {
|
||||
log_error("Trying to dereference a root node. Expect malfunction.");
|
||||
return NULL;
|
||||
}
|
||||
if (node->parent->parent != NULL) {
|
||||
// Don't return the client window if this is not a toplevel node. This
|
||||
// saves us from needing to clear `->win` when a window is reparented.
|
||||
return NULL;
|
||||
}
|
||||
return node->win;
|
||||
}
|
||||
|
||||
void wm_ref_set(struct wm_ref *cursor, struct win *w) {
|
||||
to_tree_node_mut(cursor)->win = w;
|
||||
}
|
||||
|
||||
static ptrdiff_t wm_find_masked(struct wm *wm, xcb_window_t wid) {
|
||||
|
@ -98,315 +95,117 @@ static ptrdiff_t wm_find_masked(struct wm *wm, xcb_window_t wid) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
void wm_set_active_win(struct wm *wm, struct managed_win *w) {
|
||||
bool wm_is_wid_masked(struct wm *wm, xcb_window_t wid) {
|
||||
return wm_find_masked(wm, wid) != -1;
|
||||
}
|
||||
|
||||
struct win *wm_active_win(struct wm *wm) {
|
||||
return wm->active_win;
|
||||
}
|
||||
|
||||
void wm_set_active_win(struct wm *wm, struct win *w) {
|
||||
wm->active_win = w;
|
||||
}
|
||||
|
||||
xcb_window_t wm_active_leader(struct wm *wm) {
|
||||
return wm->active_leader;
|
||||
struct wm_ref *wm_active_leader(struct wm *wm) {
|
||||
return wm->active_leader != NULL ? (struct wm_ref *)&wm->active_leader->siblings : NULL;
|
||||
}
|
||||
|
||||
void wm_set_active_leader(struct wm *wm, xcb_window_t leader) {
|
||||
wm->active_leader = leader;
|
||||
void wm_set_active_leader(struct wm *wm, struct wm_ref *leader) {
|
||||
wm->active_leader = to_tree_node_mut(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;
|
||||
bool wm_ref_is_zombie(const struct wm_ref *cursor) {
|
||||
return to_tree_node(cursor)->is_zombie;
|
||||
}
|
||||
|
||||
// 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;
|
||||
struct wm_ref *wm_ref_below(const struct wm_ref *cursor) {
|
||||
return &to_tree_node(cursor)->parent->children != cursor->inner.next
|
||||
? (struct wm_ref *)cursor->inner.next
|
||||
: 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;
|
||||
struct wm_ref *wm_ref_above(const struct wm_ref *cursor) {
|
||||
return &to_tree_node(cursor)->parent->children != cursor->inner.prev
|
||||
? (struct wm_ref *)cursor->inner.prev
|
||||
: NULL;
|
||||
}
|
||||
|
||||
void wm_remove(struct wm *wm, struct win *w) {
|
||||
wm->generation++;
|
||||
HASH_DEL(wm->windows, w);
|
||||
struct wm_ref *wm_root_ref(const struct wm *wm) {
|
||||
return (struct wm_ref *)&wm->root->siblings;
|
||||
}
|
||||
|
||||
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;
|
||||
struct wm_ref *wm_ref_topmost_child(const struct wm_ref *cursor) {
|
||||
auto node = to_tree_node(cursor);
|
||||
return !list_is_empty(&node->children) ? (struct wm_ref *)node->children.next : NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
struct wm_ref *wm_ref_bottommost_child(const struct wm_ref *cursor) {
|
||||
auto node = to_tree_node(cursor);
|
||||
return !list_is_empty(&node->children) ? (struct wm_ref *)node->children.prev : NULL;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
new_w->generation = wm->generation;
|
||||
|
||||
HASH_ADD_INT(wm->windows, id, new_w);
|
||||
return new_w;
|
||||
struct wm_ref *wm_find(const struct wm *wm, xcb_window_t id) {
|
||||
auto node = wm_tree_find(&wm->tree, id);
|
||||
return node != NULL ? (struct wm_ref *)&node->siblings : NULL;
|
||||
}
|
||||
|
||||
struct win *wm_stack_add_top(struct wm *wm, xcb_window_t id) {
|
||||
return wm_stack_insert_after(wm, id, &wm->window_stack);
|
||||
struct wm_ref *wm_find_by_client(const struct wm *wm, xcb_window_t client) {
|
||||
auto node = wm_tree_find(&wm->tree, client);
|
||||
return node != NULL ? (struct wm_ref *)&wm_tree_find_toplevel_for(node)->siblings
|
||||
: NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
struct wm_ref *wm_ref_toplevel_of(struct wm_ref *cursor) {
|
||||
return (struct wm_ref *)&wm_tree_find_toplevel_for(to_tree_node_mut(cursor))->siblings;
|
||||
}
|
||||
|
||||
/// 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 wm_ref *wm_ref_client_of(struct wm_ref *cursor) {
|
||||
auto client = to_tree_node(cursor)->client_window;
|
||||
return client != NULL ? (struct wm_ref *)&client->siblings : NULL;
|
||||
}
|
||||
|
||||
struct list_node *wm_stack_end(struct wm *wm) {
|
||||
return &wm->window_stack;
|
||||
void wm_remove(struct wm *wm, struct wm_ref *w) {
|
||||
wm_tree_destroy_window(&wm->tree, (struct wm_tree_node *)w);
|
||||
}
|
||||
|
||||
struct wm_ref *wm_stack_end(struct wm *wm) {
|
||||
return (struct wm_ref *)&wm->root->children;
|
||||
}
|
||||
|
||||
/// 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_above(struct wm *wm, struct wm_ref *cursor, struct wm_ref *below) {
|
||||
wm_tree_move_to_above(&wm->tree, to_tree_node_mut(cursor), to_tree_node_mut(below));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
unsigned wm_stack_num_managed_windows(const struct wm *wm) {
|
||||
unsigned count = 0;
|
||||
list_foreach(struct win, w, &wm->window_stack, stack_neighbour) {
|
||||
if (w->managed) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
void wm_stack_move_to_end(struct wm *wm, struct wm_ref *cursor, bool to_bottom) {
|
||||
wm_tree_move_to_end(&wm->tree, to_tree_node_mut(cursor), to_bottom);
|
||||
}
|
||||
|
||||
struct wm *wm_new(void) {
|
||||
auto wm = ccalloc(1, struct wm);
|
||||
list_init_head(&wm->window_stack);
|
||||
wm->generation = 1;
|
||||
wm_tree_init(&wm->tree);
|
||||
wm->incompletes = dynarr_new(struct wm_tree_node *, 4);
|
||||
wm->masked = dynarr_new(struct wm_tree_node *, 8);
|
||||
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);
|
||||
}
|
||||
void wm_free(struct wm *wm) {
|
||||
// Free all `struct win`s associated with tree nodes, this leaves dangling
|
||||
// pointers, but we are freeing the tree nodes immediately after, so everything
|
||||
// is fine (TM).
|
||||
wm_stack_foreach_safe(wm, i, next) {
|
||||
auto w = wm_ref_deref(i);
|
||||
auto tree_node = to_tree_node_mut(i);
|
||||
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);
|
||||
if (tree_node->is_zombie) {
|
||||
// This mainly happens on `session_destroy`, e.g. when there's
|
||||
// ongoing animations.
|
||||
log_debug("Leftover zombie node for window %#010x", tree_node->id.x);
|
||||
wm_tree_reap_zombie(tree_node);
|
||||
}
|
||||
}
|
||||
wm_tree_clear(&wm->tree);
|
||||
dynarr_free_pod(wm->incompletes);
|
||||
|
@ -439,6 +238,10 @@ void wm_destroy(struct wm *wm, xcb_window_t wid) {
|
|||
wm_tree_destroy_window(&wm->tree, node);
|
||||
}
|
||||
|
||||
void wm_reap_zombie(struct wm_ref *zombie) {
|
||||
wm_tree_reap_zombie(to_tree_node_mut(zombie));
|
||||
}
|
||||
|
||||
void wm_reparent(struct wm *wm, xcb_window_t wid, xcb_window_t parent) {
|
||||
auto window = wm_tree_find(&wm->tree, wid);
|
||||
auto new_parent = wm_tree_find(&wm->tree, parent);
|
||||
|
@ -486,6 +289,10 @@ void wm_reparent(struct wm *wm, xcb_window_t wid, xcb_window_t parent) {
|
|||
wm_tree_reparent(&wm->tree, window, new_parent);
|
||||
}
|
||||
|
||||
void wm_set_has_wm_state(struct wm *wm, struct wm_ref *cursor, bool has_wm_state) {
|
||||
wm_tree_set_wm_state(&wm->tree, to_tree_node_mut(cursor), has_wm_state);
|
||||
}
|
||||
|
||||
void wm_import_incomplete(struct wm *wm, xcb_window_t wid, xcb_window_t parent) {
|
||||
auto masked = wm_find_masked(wm, wid);
|
||||
if (masked != -1) {
|
||||
|
@ -532,12 +339,9 @@ static void wm_complete_import_subtree(struct wm *wm, struct x_connection *c,
|
|||
wm_complete_import_single(wm, c, atoms, subroot);
|
||||
|
||||
for (auto curr = subroot; curr != NULL; curr = wm_tree_next(curr, subroot)) {
|
||||
if (!list_is_empty(&curr->children)) {
|
||||
log_error("Newly imported subtree root at %#010x already has "
|
||||
"children. "
|
||||
"Expect malfunction.",
|
||||
curr->id.x);
|
||||
}
|
||||
// Surprise! This newly imported window might already have children.
|
||||
// Although we haven't setup SubstructureNotify for it yet, it's still
|
||||
// possible for another window to be reparented to it.
|
||||
|
||||
xcb_query_tree_reply_t *tree = XCB_AWAIT(xcb_query_tree, c->c, curr->id.x);
|
||||
if (!tree) {
|
||||
|
|
81
src/wm/wm.h
81
src/wm/wm.h
|
@ -23,7 +23,7 @@
|
|||
#include "compiler.h"
|
||||
|
||||
struct wm;
|
||||
struct managed_win;
|
||||
struct win;
|
||||
struct list_node;
|
||||
struct x_connection;
|
||||
|
||||
|
@ -99,12 +99,12 @@ static inline bool wm_treeid_eq(wm_treeid a, wm_treeid b) {
|
|||
}
|
||||
|
||||
struct wm *wm_new(void);
|
||||
void wm_free(struct wm *wm, struct x_connection *c);
|
||||
void wm_free(struct wm *wm);
|
||||
|
||||
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);
|
||||
struct win *wm_active_win(struct wm *wm);
|
||||
void wm_set_active_win(struct wm *wm, struct win *w);
|
||||
struct wm_ref *wm_active_leader(struct wm *wm);
|
||||
void wm_set_active_leader(struct wm *wm, struct wm_ref *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
|
||||
|
@ -114,64 +114,45 @@ void wm_set_active_leader(struct wm *wm, xcb_window_t leader);
|
|||
// 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);
|
||||
struct wm_ref *attr_pure wm_find(const 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);
|
||||
void wm_remove(struct wm *wm, struct wm_ref *w);
|
||||
// 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);
|
||||
struct wm_ref *attr_pure wm_find_by_client(const struct wm *wm, xcb_window_t client);
|
||||
/// Find the toplevel of a window by going up the window tree.
|
||||
struct wm_ref *attr_pure wm_ref_toplevel_of(struct wm_ref *cursor);
|
||||
/// Return the client window of a window. Must be called with a cursor to a toplevel.
|
||||
/// Returns NULL if there is no client window.
|
||||
struct wm_ref *attr_pure wm_ref_client_of(struct wm_ref *cursor);
|
||||
/// Find the next window in the window stack. Returns NULL if `cursor` is the last window.
|
||||
struct wm_ref *attr_pure wm_ref_below(const struct wm_ref *cursor);
|
||||
struct wm_ref *attr_pure wm_ref_above(const struct wm_ref *cursor);
|
||||
struct wm_ref *attr_pure wm_root_ref(const struct wm *wm);
|
||||
|
||||
struct wm_ref *attr_pure wm_ref_topmost_child(const struct wm_ref *cursor);
|
||||
struct wm_ref *attr_pure wm_ref_bottommost_child(const struct wm_ref *cursor);
|
||||
|
||||
/// 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);
|
||||
}
|
||||
void wm_stack_move_to_above(struct wm *wm, struct wm_ref *cursor, struct wm_ref *below);
|
||||
/// 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_);
|
||||
unsigned attr_const wm_stack_num_managed_windows(const struct wm *wm);
|
||||
void wm_stack_move_to_end(struct wm *wm, struct wm_ref *cursor, bool to_bottom);
|
||||
|
||||
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);
|
||||
struct win *attr_pure wm_ref_deref(const struct wm_ref *cursor);
|
||||
xcb_window_t attr_pure wm_ref_win_id(const struct wm_ref *cursor);
|
||||
wm_treeid attr_pure wm_ref_treeid(const struct wm_ref *cursor);
|
||||
/// Assign a window to a cursor. The cursor must not already have a window assigned.
|
||||
void wm_ref_set(struct wm_ref *cursor, struct win *w);
|
||||
bool attr_pure wm_ref_is_zombie(const struct wm_ref *cursor);
|
||||
|
||||
/// Destroy a window. Children of this window should already have been destroyed. This
|
||||
/// will cause a `WM_TREE_CHANGE_TOPLEVEL_KILLED` event to be generated, and a zombie
|
||||
/// window to be placed where the window was.
|
||||
void wm_destroy(struct wm *wm, xcb_window_t wid);
|
||||
/// Remove a zombie window from the window tree.
|
||||
void wm_reap_zombie(struct wm_ref *zombie);
|
||||
void wm_reparent(struct wm *wm, xcb_window_t wid, xcb_window_t parent);
|
||||
void wm_set_has_wm_state(struct wm *wm, struct wm_ref *cursor, bool has_wm_state);
|
||||
|
||||
/// Create a tree node for `wid`, with `parent` as its parent. The parent must already
|
||||
/// be in the window tree. This function creates a placeholder tree node, without
|
||||
|
|
|
@ -13,6 +13,14 @@
|
|||
struct wm_tree {
|
||||
/// The generation of the wm tree. This number is incremented every time a new
|
||||
/// window is created.
|
||||
///
|
||||
/// Because X server recycles window IDs, X ID alone is not enough to uniquely
|
||||
/// identify a window. This generation number is incremented every time a window
|
||||
/// is created, so even if a window ID is reused, its generation number is
|
||||
/// guaranteed to be different from before. Unless, of course, the generation
|
||||
/// number overflows, but since we are using a uint64_t here, that won't happen
|
||||
/// for a very long time. Still, it is recommended that you restart the compositor
|
||||
/// at least once before the Universe collapse back on itself.
|
||||
uint64_t gen;
|
||||
/// wm tree nodes indexed by their X window ID.
|
||||
struct wm_tree_node *nodes;
|
||||
|
@ -68,8 +76,10 @@ struct wm_tree_change {
|
|||
/// Free all tree nodes and changes, without generating any change events. Used when
|
||||
/// shutting down.
|
||||
void wm_tree_clear(struct wm_tree *tree);
|
||||
struct wm_tree_node *wm_tree_find(const struct wm_tree *tree, xcb_window_t id);
|
||||
struct wm_tree_node *wm_tree_next(struct wm_tree_node *node, struct wm_tree_node *subroot);
|
||||
struct wm_tree_node *attr_pure wm_tree_find(const struct wm_tree *tree, xcb_window_t id);
|
||||
struct wm_tree_node *attr_pure wm_tree_find_toplevel_for(struct wm_tree_node *node);
|
||||
struct wm_tree_node *attr_pure wm_tree_next(struct wm_tree_node *node,
|
||||
struct wm_tree_node *subroot);
|
||||
/// Create a new window node in the tree, with X window ID `id`, and parent `parent`. If
|
||||
/// `parent` is NULL, the new node will be the root window. Only one root window is
|
||||
/// permitted, and the root window cannot be destroyed once created, until
|
||||
|
@ -78,13 +88,15 @@ struct wm_tree_node *wm_tree_next(struct wm_tree_node *node, struct wm_tree_node
|
|||
struct wm_tree_node *
|
||||
wm_tree_new_window(struct wm_tree *tree, xcb_window_t id, struct wm_tree_node *parent);
|
||||
void wm_tree_destroy_window(struct wm_tree *tree, struct wm_tree_node *node);
|
||||
struct wm_tree_node *wm_tree_find_toplevel_for(struct wm_tree_node *node);
|
||||
/// Detach the subtree rooted at `subroot` from `tree`. The subtree root is removed from
|
||||
/// its parent, and the disconnected tree nodes won't be able to be found via
|
||||
/// `wm_tree_find`. Relevant events will be generated.
|
||||
void wm_tree_detach(struct wm_tree *tree, struct wm_tree_node *subroot);
|
||||
void wm_tree_reparent(struct wm_tree *tree, struct wm_tree_node *node,
|
||||
struct wm_tree_node *new_parent);
|
||||
void wm_tree_move_to_above(struct wm_tree *tree, struct wm_tree_node *node,
|
||||
struct wm_tree_node *other);
|
||||
/// Move `node` to the top or the bottom of its parent's child window stack.
|
||||
void wm_tree_move_to_end(struct wm_tree *tree, struct wm_tree_node *node, bool to_bottom);
|
||||
struct wm_tree_change wm_tree_dequeue_change(struct wm_tree *tree);
|
||||
void wm_tree_reap_zombie(struct wm_tree_node *zombie);
|
||||
|
|
Loading…
Add table
Reference in a new issue