aboutsummaryrefslogtreecommitdiff
path: root/src/event.c
diff options
context:
space:
mode:
authorallusive-dev <[email protected]>2023-09-19 17:47:33 +1000
committerallusive-dev <[email protected]>2023-09-19 17:47:33 +1000
commita93aba600b1c5d019b680b9f4ff3fa85d5d43a60 (patch)
tree77f8152222655657472a70e0bfa413a0495dd555 /src/event.c
parentreset (diff)
downloadcompfy-a93aba600b1c5d019b680b9f4ff3fa85d5d43a60.tar.xz
compfy-a93aba600b1c5d019b680b9f4ff3fa85d5d43a60.zip
Fixed broken files/code and other errors
Diffstat (limited to 'src/event.c')
-rw-r--r--src/event.c757
1 files changed, 757 insertions, 0 deletions
diff --git a/src/event.c b/src/event.c
new file mode 100644
index 0000000..5e4017f
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,757 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) 2019, Yuxuan Shui <[email protected]>
+
+#include <stdio.h>
+
+#include <X11/Xlibint.h>
+#include <X11/extensions/sync.h>
+#include <xcb/damage.h>
+#include <xcb/randr.h>
+
+#include "atom.h"
+#include "common.h"
+#include "compiler.h"
+#include "config.h"
+#include "event.h"
+#include "log.h"
+#include "picom.h"
+#include "region.h"
+#include "utils.h"
+#include "win.h"
+#include "x.h"
+
+/// Event handling with X is complicated. Handling events with other events possibly
+/// in-flight is no good. Because your internal state won't be up to date. Also, querying
+/// the server while events are in-flight is not good. Because events later in the queue
+/// might container information you are querying. Thus those events will cause you to do
+/// unnecessary updates even when you already have the latest information (remember, you
+/// made the query when those events were already in the queue. so the reply you got is
+/// more up-to-date than the events). Also, handling events when other client are making
+/// concurrent requests is not good. Because the server states are changing without you
+/// knowning them. This is super racy, and can cause lots of potential problems.
+///
+/// All of above mandates we do these things:
+/// 1. Grab server when handling events
+/// 2. Make sure the event queue is empty before we make any query to the server
+///
+/// Notice (2) has a dependency circle. To handle events, you sometimes need to make
+/// queries. But to make queries you have to first handle events.
+///
+/// To break that circle, we split all event handling into top and bottom halves. The
+/// bottom half will just look at the event itself, update as much state as they can
+/// without making queries, then queue up necessary works need to be done by the top half.
+/// The top half will do all the other necessary updates. Before entering the top half, we
+/// grab the server and make sure the event queue is empty.
+///
+/// When top half finished, we enter the render stage, where no server state should be
+/// queried. All rendering should be done with our internal knowledge of the server state.
+///
+
+// TODO(yshui) the things described above
+
+/**
+ * Get a window's name from window ID.
+ */
+static inline const char *ev_window_name(session_t *ps, xcb_window_t wid) {
+ char *name = "";
+ if (wid) {
+ name = "(Failed to get title)";
+ if (ps->root == wid) {
+ name = "(Root window)";
+ } else if (ps->overlay == wid) {
+ name = "(Overlay)";
+ } else {
+ auto w = find_managed_win(ps, wid);
+ if (!w) {
+ w = find_toplevel(ps, wid);
+ }
+
+ if (w && w->name) {
+ name = w->name;
+ }
+ }
+ }
+ return name;
+}
+
+static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_t *ev) {
+ switch (ev->response_type) {
+ case FocusIn:
+ case FocusOut: return ((xcb_focus_in_event_t *)ev)->event;
+ case CreateNotify: return ((xcb_create_notify_event_t *)ev)->window;
+ case ConfigureNotify: return ((xcb_configure_notify_event_t *)ev)->window;
+ case DestroyNotify: return ((xcb_destroy_notify_event_t *)ev)->window;
+ case MapNotify: return ((xcb_map_notify_event_t *)ev)->window;
+ case UnmapNotify: return ((xcb_unmap_notify_event_t *)ev)->window;
+ case ReparentNotify: return ((xcb_reparent_notify_event_t *)ev)->window;
+ case CirculateNotify: return ((xcb_circulate_notify_event_t *)ev)->window;
+ case Expose: return ((xcb_expose_event_t *)ev)->window;
+ case PropertyNotify: return ((xcb_property_notify_event_t *)ev)->window;
+ case ClientMessage: return ((xcb_client_message_event_t *)ev)->window;
+ default:
+ if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
+ return ((xcb_damage_notify_event_t *)ev)->drawable;
+ }
+
+ if (ps->shape_exists && ev->response_type == ps->shape_event) {
+ return ((xcb_shape_notify_event_t *)ev)->affected_window;
+ }
+
+ return 0;
+ }
+}
+
+#define CASESTRRET(s) \
+ case s: return #s;
+
+static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) {
+ static char buf[128];
+ switch (ev->response_type & 0x7f) {
+ CASESTRRET(FocusIn);
+ CASESTRRET(FocusOut);
+ CASESTRRET(CreateNotify);
+ CASESTRRET(ConfigureNotify);
+ CASESTRRET(DestroyNotify);
+ CASESTRRET(MapNotify);
+ CASESTRRET(UnmapNotify);
+ CASESTRRET(ReparentNotify);
+ CASESTRRET(CirculateNotify);
+ CASESTRRET(Expose);
+ CASESTRRET(PropertyNotify);
+ CASESTRRET(ClientMessage);
+ }
+
+ if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type)
+ return "Damage";
+
+ if (ps->shape_exists && ev->response_type == ps->shape_event)
+ return "ShapeNotify";
+
+ if (ps->xsync_exists) {
+ int o = ev->response_type - ps->xsync_event;
+ switch (o) {
+ CASESTRRET(XSyncCounterNotify);
+ CASESTRRET(XSyncAlarmNotify);
+ }
+ }
+
+ sprintf(buf, "Event %d", ev->response_type);
+
+ return buf;
+}
+
+static inline const char *attr_pure ev_focus_mode_name(xcb_focus_in_event_t *ev) {
+ switch (ev->mode) {
+ CASESTRRET(NotifyNormal);
+ CASESTRRET(NotifyWhileGrabbed);
+ CASESTRRET(NotifyGrab);
+ CASESTRRET(NotifyUngrab);
+ }
+
+ return "Unknown";
+}
+
+static inline const char *attr_pure ev_focus_detail_name(xcb_focus_in_event_t *ev) {
+ switch (ev->detail) {
+ CASESTRRET(NotifyAncestor);
+ CASESTRRET(NotifyVirtual);
+ CASESTRRET(NotifyInferior);
+ CASESTRRET(NotifyNonlinear);
+ CASESTRRET(NotifyNonlinearVirtual);
+ CASESTRRET(NotifyPointer);
+ CASESTRRET(NotifyPointerRoot);
+ CASESTRRET(NotifyDetailNone);
+ }
+
+ return "Unknown";
+}
+
+#undef CASESTRRET
+
+static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) {
+ log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
+ ev_focus_detail_name(ev));
+ ps->pending_updates = true;
+}
+
+static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
+ log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
+ ev_focus_detail_name(ev));
+ ps->pending_updates = true;
+}
+
+static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
+ if (ev->parent == ps->root) {
+ add_win_top(ps, ev->window);
+ }
+}
+
+/// Handle configure event of a regular window
+static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
+ auto w = find_win(ps, ce->window);
+
+ if (!w) {
+ return;
+ }
+
+ if (!w->managed) {
+ restack_above(ps, w, ce->above_sibling);
+ return;
+ }
+
+ auto mw = (struct managed_win *)w;
+
+ restack_above(ps, w, ce->above_sibling);
+
+ // We check against pending_g here, because there might have been multiple
+ // configure notifies in this cycle, or the window could receive multiple updates
+ // 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;
+ if (position_changed || size_changed) {
+ // Queue pending updates
+ win_set_flags(mw, 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);
+ }
+
+ 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);
+ }
+
+ // Recalculate which screen this window is on
+ win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, mw);
+ }
+
+ // 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;
+}
+
+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 }",
+ ev->event, ev->window, ev->above_sibling, ev->override_redirect);
+ if (ev->window == ps->root) {
+ set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
+ } else {
+ configure_win(ps, ev);
+ }
+}
+
+static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
+ auto w = find_win(ps, ev->window);
+ auto mw = find_toplevel(ps, ev->window);
+ if (mw && mw->client_win == mw->base.id) {
+ // We only want _real_ frame window
+ assert(&mw->base == w);
+ mw = NULL;
+ }
+ assert(w == NULL || mw == NULL);
+
+ if (w != NULL) {
+ auto _ attr_unused = destroy_win_start(ps, w);
+ } else if (mw != NULL) {
+ win_unmark_client(ps, mw);
+ win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
+ ps->pending_updates = true;
+ } else {
+ 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, xcb_unmap_window(ps->c, ps->overlay));
+ if (e) {
+ log_error("Failed to unmap the overlay window");
+ free(e);
+ }
+ // We don't track the overlay window, so we can return
+ return;
+ }
+
+ auto w = find_managed_win(ps, ev->window);
+ if (!w) {
+ return;
+ }
+
+ win_set_flags(w, WIN_FLAGS_MAPPED);
+
+ // FocusIn/Out may be ignored when the window is unmapped, so we must
+ // recheck focus here
+ ps->pending_updates = true; // to update focus
+}
+
+static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) {
+ auto w = find_managed_win(ps, ev->window);
+ if (w) {
+ unmap_win_start(ps, w);
+ }
+}
+
+static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
+ log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
+ ev->window, ev->parent, ev->override_redirect);
+ auto w_top = find_toplevel(ps, ev->window);
+ if (w_top) {
+ win_unmark_client(ps, w_top);
+ win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
+ ps->pending_updates = true;
+ }
+
+ if (ev->parent == ps->root) {
+ // X will generate reparent notifiy even if the parent didn't actually
+ // change (i.e. reparent again to current parent). So we check if that's
+ // the case
+ auto w = find_win(ps, ev->window);
+ if (w) {
+ // This window has already been reparented to root before,
+ // so we don't need to create a new window for it, we just need to
+ // move it to the top
+ restack_top(ps, w);
+ } else {
+ add_win_top(ps, ev->window);
+ }
+ } else {
+ // otherwise, find and destroy the window first
+ {
+ auto w = find_win(ps, ev->window);
+ if (w) {
+ auto ret = destroy_win_start(ps, w);
+ if (!ret && w->managed) {
+ auto mw = (struct managed_win *)w;
+ CHECK(win_skip_fading(ps, mw));
+ }
+ }
+ }
+
+ // Reset event mask in case something wrong happens
+ xcb_change_window_attributes(
+ ps->c, ev->window, XCB_CW_EVENT_MASK,
+ (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
+
+ if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
+ log_debug("Window %#010x doesn't have WM_STATE property, it is "
+ "probably not a client window. But we will listen for "
+ "property change in case it gains one.",
+ ev->window);
+ xcb_change_window_attributes(
+ ps->c, ev->window, XCB_CW_EVENT_MASK,
+ (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
+ XCB_EVENT_MASK_PROPERTY_CHANGE});
+ } else {
+ auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
+ if (w_real_top && w_real_top->state != WSTATE_UNMAPPED &&
+ w_real_top->state != WSTATE_UNMAPPING) {
+ log_debug("Mark window %#010x (%s) as having a stale "
+ "client",
+ w_real_top->base.id, w_real_top->name);
+ win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
+ ps->pending_updates = true;
+ } else {
+ if (!w_real_top)
+ log_debug("parent %#010x not found", ev->parent);
+ else {
+ // Window is not currently mapped, unmark its
+ // client to trigger a client recheck when it is
+ // mapped later.
+ win_unmark_client(ps, w_real_top);
+ log_debug("parent %#010x (%s) is in state %d",
+ w_real_top->base.id, w_real_top->name,
+ w_real_top->state);
+ }
+ }
+ }
+ }
+}
+
+static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
+ auto w = find_win(ps, ev->window);
+
+ if (!w)
+ return;
+
+ if (ev->place == PlaceOnTop) {
+ restack_top(ps, w);
+ } else {
+ restack_bottom(ps, w);
+ }
+}
+
+static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) {
+ region_t region;
+ pixman_region32_init_rects(&region, rects, nrects);
+ add_damage(ps, &region);
+ pixman_region32_fini(&region);
+}
+
+static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
+ if (ev->window == ps->root || (ps->overlay && ev->window == ps->overlay)) {
+ int more = ev->count + 1;
+ if (ps->n_expose == ps->size_expose) {
+ if (ps->expose_rects) {
+ ps->expose_rects =
+ crealloc(ps->expose_rects, ps->size_expose + more);
+ ps->size_expose += more;
+ } else {
+ ps->expose_rects = ccalloc(more, rect_t);
+ ps->size_expose = more;
+ }
+ }
+
+ ps->expose_rects[ps->n_expose].x1 = ev->x;
+ ps->expose_rects[ps->n_expose].y1 = ev->y;
+ ps->expose_rects[ps->n_expose].x2 = ev->x + ev->width;
+ ps->expose_rects[ps->n_expose].y2 = ev->y + ev->height;
+ ps->n_expose++;
+
+ if (ev->count == 0) {
+ expose_root(ps, ps->expose_rects, ps->n_expose);
+ ps->n_expose = 0;
+ }
+ }
+}
+
+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
+ xcb_get_atom_name_reply_t *reply =
+ xcb_get_atom_name_reply(ps->c, xcb_get_atom_name(ps->c, ev->atom), NULL);
+ const char *name = "?";
+ int name_len = 1;
+ if (reply) {
+ name = xcb_get_atom_name_name(reply);
+ name_len = xcb_get_atom_name_name_length(reply);
+ }
+
+ log_debug("{ atom = %.*s }", name_len, name);
+ free(reply);
+ }
+
+ if (ps->root == ev->window) {
+ // If desktop number property changes
+ if (ev->atom == ps->atoms->a_NET_CURRENT_DESKTOP) {
+ auto prop = x_get_prop(ps->c, ps->root, ps->atoms->a_NET_CURRENT_DESKTOP,
+ 1L, XCB_ATOM_CARDINAL, 32);
+
+ if (prop.nitems) {
+ ps->root_desktop_switch_direction = ((int)*prop.c32) - ps->root_desktop_num;
+ ps->root_desktop_num = (int)*prop.c32;
+ }
+ }
+
+ if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
+ // to update focus
+ ps->pending_updates = true;
+ } else {
+ // Destroy the root "image" if the wallpaper probably changed
+ if (x_is_root_back_pixmap_atom(ps->atoms, ev->atom)) {
+ root_damaged(ps);
+ }
+ }
+
+ // Unconcerned about any other proprties on root window
+ return;
+ }
+
+ ps->pending_updates = true;
+ // If WM_STATE changes
+ if (ev->atom == ps->atoms->aWM_STATE) {
+ // Check whether it could be a client window
+ if (!find_toplevel(ps, ev->window)) {
+ // Reset event mask anyway
+ xcb_change_window_attributes(ps->c, ev->window, XCB_CW_EVENT_MASK,
+ (const uint32_t[]){determine_evmask(
+ ps, ev->window, WIN_EVMODE_UNKNOWN)});
+
+ auto w_top = find_managed_window_or_parent(ps, ev->window);
+ // ev->window might have not been managed yet, in that case w_top
+ // would be NULL.
+ if (w_top) {
+ win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
+ }
+ }
+ return;
+ }
+
+ // If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
+ // there are always some stupid applications. (#144)
+ if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) {
+ struct managed_win *w = NULL;
+ if ((w = find_toplevel(ps, ev->window))) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
+ // Unnecessay until we remove the queue_redraw in ev_handle
+ queue_redraw(ps);
+ }
+
+ // If _NET_WM_OPACITY changes
+ if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY) {
+ auto w = find_managed_win(ps, ev->window) ?: find_toplevel(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // If frame extents property changes
+ if (ev->atom == ps->atoms->a_NET_FRAME_EXTENTS) {
+ auto w = find_toplevel(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // If name changes
+ if (ps->atoms->aWM_NAME == ev->atom || ps->atoms->a_NET_WM_NAME == ev->atom) {
+ auto w = find_toplevel(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // If class changes
+ if (ps->atoms->aWM_CLASS == ev->atom) {
+ auto w = find_toplevel(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // If role changes
+ if (ps->atoms->aWM_WINDOW_ROLE == ev->atom) {
+ auto w = find_toplevel(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // If _COMPTON_SHADOW changes
+ if (ps->atoms->a_COMPTON_SHADOW == ev->atom) {
+ auto w = find_managed_win(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // If a leader property changes
+ if ((ps->o.detect_transient && ps->atoms->aWM_TRANSIENT_FOR == ev->atom) ||
+ (ps->o.detect_client_leader && ps->atoms->aWM_CLIENT_LEADER == ev->atom)) {
+ auto w = find_toplevel(ps, ev->window);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
+ }
+
+ // Check for other atoms we are tracking
+ for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
+ if (platom->atom == ev->atom) {
+ auto w = find_managed_win(ps, ev->window);
+ if (!w) {
+ w = find_toplevel(ps, ev->window);
+ }
+ if (w) {
+ // 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);
+ }
+ break;
+ }
+ }
+}
+
+static inline void repair_win(session_t *ps, struct managed_win *w) {
+ // Only mapped window can receive damages
+ assert(win_is_mapped_in_x(w));
+
+ region_t parts;
+ pixman_region32_init(&parts);
+
+ if (!w->ever_damaged) {
+ win_extents(w, &parts);
+ set_ignore_cookie(
+ ps, xcb_damage_subtract(ps->c, w->damage, XCB_NONE, XCB_NONE));
+ } else {
+ set_ignore_cookie(
+ ps, xcb_damage_subtract(ps->c, w->damage, XCB_NONE, ps->damaged_region));
+ x_fetch_region(ps->c, ps->damaged_region, &parts);
+ pixman_region32_translate(&parts, w->g.x + w->g.border_width,
+ w->g.y + w->g.border_width);
+ }
+
+ log_trace("Mark window %#010x (%s) as having received damage", w->base.id, w->name);
+ w->ever_damaged = true;
+ w->pixmap_damaged = true;
+
+ // Why care about damage when screen is unredirected?
+ // We will force full-screen repaint on redirection.
+ if (!ps->redirected) {
+ pixman_region32_fini(&parts);
+ return;
+ }
+
+ // Remove the part in the damage area that could be ignored
+ if (w->reg_ignore && win_is_region_ignore_valid(ps, w)) {
+ pixman_region32_subtract(&parts, &parts, w->reg_ignore);
+ }
+
+ add_damage(ps, &parts);
+ pixman_region32_fini(&parts);
+}
+
+static inline void ev_damage_notify(session_t *ps, xcb_damage_notify_event_t *de) {
+ /*
+ if (ps->root == de->drawable) {
+ root_damaged();
+ return;
+ } */
+
+ auto w = find_managed_win(ps, de->drawable);
+
+ if (!w) {
+ return;
+ }
+
+ repair_win(ps, w);
+}
+
+static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev) {
+ auto w = find_managed_win(ps, ev->affected_window);
+ if (!w || w->a.map_state == XCB_MAP_STATE_UNMAPPED) {
+ return;
+ }
+
+ /*
+ * Empty bounding_shape may indicated an
+ * unmapped/destroyed window, in which case
+ * seemingly BadRegion errors would be triggered
+ * if we attempt to rebuild border_size
+ */
+ // Mark the old bounding shape as damaged
+ if (!win_check_flags_any(w, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
+ region_t tmp = win_get_bounding_shape_global_by_val(w);
+ add_damage(ps, &tmp);
+ pixman_region32_fini(&tmp);
+ }
+ w->reg_ignore_valid = false;
+
+ win_set_flags(w, WIN_FLAGS_SIZE_STALE);
+ ps->pending_updates = true;
+}
+
+static inline void
+ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) {
+ // The only selection we own is the _NET_WM_CM_Sn selection.
+ // If we lose that one, we should exit.
+ log_fatal("Another composite manager started and took the _NET_WM_CM_Sn "
+ "selection.");
+ quit(ps);
+}
+
+void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
+ if ((ev->response_type & 0x7f) != KeymapNotify) {
+ discard_ignore(ps, ev->full_sequence);
+ }
+
+ xcb_window_t wid = ev_window(ps, ev);
+ if (ev->response_type != ps->damage_event + XCB_DAMAGE_NOTIFY) {
+ log_debug("event %10.10s serial %#010x window %#010x \"%s\"",
+ ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid));
+ } else {
+ log_trace("event %10.10s serial %#010x window %#010x \"%s\"",
+ ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid));
+ }
+
+ // Check if a custom XEvent constructor was registered in xlib for this event
+ // type, and call it discarding the constructed XEvent if any. XESetWireToEvent
+ // might be used by libraries to intercept messages from the X server e.g. the
+ // OpenGL lib waiting for DRI2 events.
+
+ // XXX This exists to workaround compton issue #33, #34, #47
+ // For even more details, see:
+ // https://bugs.freedesktop.org/show_bug.cgi?id=35945
+ // https://lists.freedesktop.org/archives/xcb/2011-November/007337.html
+ auto proc = XESetWireToEvent(ps->dpy, ev->response_type, 0);
+ if (proc) {
+ XESetWireToEvent(ps->dpy, ev->response_type, proc);
+ XEvent dummy;
+
+ // Stop Xlib from complaining about lost sequence numbers.
+ // proc might also just be Xlib internal event processing functions, and
+ // because they probably won't see all X replies, they will complain about
+ // missing sequence numbers.
+ //
+ // We only need the low 16 bits
+ ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->dpy) & 0xffff);
+ proc(ps->dpy, &dummy, (xEvent *)ev);
+ }
+
+ // XXX redraw needs to be more fine grained
+ queue_redraw(ps);
+
+ switch (ev->response_type) {
+ case FocusIn: ev_focus_in(ps, (xcb_focus_in_event_t *)ev); break;
+ case FocusOut: ev_focus_out(ps, (xcb_focus_out_event_t *)ev); break;
+ case CreateNotify: ev_create_notify(ps, (xcb_create_notify_event_t *)ev); break;
+ case ConfigureNotify:
+ ev_configure_notify(ps, (xcb_configure_notify_event_t *)ev);
+ break;
+ case DestroyNotify:
+ ev_destroy_notify(ps, (xcb_destroy_notify_event_t *)ev);
+ break;
+ case MapNotify: ev_map_notify(ps, (xcb_map_notify_event_t *)ev); break;
+ case UnmapNotify: ev_unmap_notify(ps, (xcb_unmap_notify_event_t *)ev); break;
+ case ReparentNotify:
+ ev_reparent_notify(ps, (xcb_reparent_notify_event_t *)ev);
+ break;
+ case CirculateNotify:
+ ev_circulate_notify(ps, (xcb_circulate_notify_event_t *)ev);
+ break;
+ case Expose: ev_expose(ps, (xcb_expose_event_t *)ev); break;
+ case PropertyNotify:
+ ev_property_notify(ps, (xcb_property_notify_event_t *)ev);
+ break;
+ case SelectionClear:
+ ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev);
+ break;
+ case 0: ev_xcb_error(ps, (xcb_generic_error_t *)ev); break;
+ default:
+ if (ps->shape_exists && ev->response_type == ps->shape_event) {
+ ev_shape_notify(ps, (xcb_shape_notify_event_t *)ev);
+ break;
+ }
+ if (ps->randr_exists &&
+ ev->response_type == (ps->randr_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
+ set_root_flags(ps, ROOT_FLAGS_SCREEN_CHANGE);
+ break;
+ }
+ if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
+ ev_damage_notify(ps, (xcb_damage_notify_event_t *)ev);
+ break;
+ }
+ }
+}