aboutsummaryrefslogtreecommitdiff
path: root/src/picom.c
diff options
context:
space:
mode:
authorallusive-dev <[email protected]>2023-11-15 15:13:38 +1100
committerallusive-dev <[email protected]>2023-11-15 15:13:38 +1100
commitf481ea09f9f69c96575662d7b67d290f380aee83 (patch)
tree9155652619c26d1a028ddc63eb7a0c29094dd0d3 /src/picom.c
parentUpdate README.md (diff)
downloadcompfy-f481ea09f9f69c96575662d7b67d290f380aee83.tar.xz
compfy-f481ea09f9f69c96575662d7b67d290f380aee83.zip
merge with compfy again lol1.6.0
Diffstat (limited to 'src/picom.c')
-rw-r--r--src/picom.c2832
1 files changed, 0 insertions, 2832 deletions
diff --git a/src/picom.c b/src/picom.c
deleted file mode 100644
index a3ed614..0000000
--- a/src/picom.c
+++ /dev/null
@@ -1,2832 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Compton - a compositor for X11
- *
- * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
- *
- * Copyright (c) 2011-2013, Christopher Jeffrey
- * See LICENSE-mit for more information.
- *
- */
-
-#include <X11/Xlib-xcb.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/extensions/sync.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <xcb/composite.h>
-#include <xcb/damage.h>
-#include <xcb/glx.h>
-#include <xcb/present.h>
-#include <xcb/randr.h>
-#include <xcb/render.h>
-#include <xcb/sync.h>
-#include <xcb/xfixes.h>
-#include <xcb/xinerama.h>
-
-#include <ev.h>
-#include <test.h>
-
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "err.h"
-#include "kernel.h"
-#include "picom.h"
-#ifdef CONFIG_OPENGL
-#include "opengl.h"
-#endif
-#include "backend/backend.h"
-#include "c2.h"
-#include "config.h"
-#include "diagnostic.h"
-#include "log.h"
-#include "region.h"
-#include "render.h"
-#include "types.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-#ifdef CONFIG_DBUS
-#include "dbus.h"
-#endif
-#include "atom.h"
-#include "event.h"
-#include "file_watch.h"
-#include "list.h"
-#include "options.h"
-#include "uthash_extra.h"
-
-/// Get session_t pointer from a pointer to a member of session_t
-#define session_ptr(ptr, member) \
- ({ \
- const __typeof__(((session_t *)0)->member) *__mptr = (ptr); \
- (session_t *)((char *)__mptr - offsetof(session_t, member)); \
- })
-
-static bool must_use redirect_start(session_t *ps);
-
-static void unredirect(session_t *ps);
-
-// === Global constants ===
-
-/// Name strings for window types.
-const char *const WINTYPES[NUM_WINTYPES] = {
- "unknown", "desktop", "dock", "toolbar", "menu",
- "utility", "splash", "dialog", "normal", "dropdown_menu",
- "popup_menu", "tooltip", "notification", "combo", "dnd",
-};
-
-// clang-format off
-/// Names of backends.
-const char *const BACKEND_STRS[] = {[BKEND_XRENDER] = "xrender",
- [BKEND_GLX] = "glx",
- [BKEND_XR_GLX_HYBRID] = "xr_glx_hybrid",
- [BKEND_DUMMY] = "dummy",
- [BKEND_EGL] = "egl",
- NULL};
-// clang-format on
-
-// === Global variables ===
-
-/// Pointer to current session, as a global variable. Only used by
-/// xerror(), which could not have a pointer to current session passed in.
-/// XXX Limit what xerror can access by not having this pointer
-session_t *ps_g = NULL;
-
-void set_root_flags(session_t *ps, uint64_t flags) {
- log_debug("Setting root flags: %" PRIu64, flags);
- ps->root_flags |= flags;
- ps->pending_updates = true;
-}
-
-void quit(session_t *ps) {
- ps->quit = true;
- ev_break(ps->loop, EVBREAK_ALL);
-}
-
-/**
- * Free Xinerama screen info.
- *
- * XXX consider moving to x.c
- */
-static inline void free_xinerama_info(session_t *ps) {
- if (ps->xinerama_scr_regs) {
- for (int i = 0; i < ps->xinerama_nscrs; ++i)
- pixman_region32_fini(&ps->xinerama_scr_regs[i]);
- free(ps->xinerama_scr_regs);
- ps->xinerama_scr_regs = NULL;
- }
- ps->xinerama_nscrs = 0;
-}
-
-/**
- * Get current system clock in milliseconds.
- */
-static inline int64_t get_time_ms(void) {
- struct timespec tp;
- clock_gettime(CLOCK_MONOTONIC, &tp);
- return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000;
-}
-
-// XXX Move to x.c
-void cxinerama_upd_scrs(session_t *ps) {
- // XXX Consider deprecating Xinerama, switch to RandR when necessary
- free_xinerama_info(ps);
-
- if (!ps->o.xinerama_shadow_crop || !ps->xinerama_exists)
- return;
-
- xcb_xinerama_is_active_reply_t *active =
- xcb_xinerama_is_active_reply(ps->c, xcb_xinerama_is_active(ps->c), NULL);
- if (!active || !active->state) {
- free(active);
- return;
- }
- free(active);
-
- auto xinerama_scrs =
- xcb_xinerama_query_screens_reply(ps->c, xcb_xinerama_query_screens(ps->c), NULL);
- if (!xinerama_scrs) {
- return;
- }
-
- xcb_xinerama_screen_info_t *scrs =
- xcb_xinerama_query_screens_screen_info(xinerama_scrs);
- ps->xinerama_nscrs = xcb_xinerama_query_screens_screen_info_length(xinerama_scrs);
-
- ps->xinerama_scr_regs = ccalloc(ps->xinerama_nscrs, region_t);
- for (int i = 0; i < ps->xinerama_nscrs; ++i) {
- const xcb_xinerama_screen_info_t *const s = &scrs[i];
- pixman_region32_init_rect(&ps->xinerama_scr_regs[i], s->x_org, s->y_org,
- s->width, s->height);
- }
- free(xinerama_scrs);
-}
-
-/**
- * Find matched window.
- *
- * XXX move to win.c
- */
-static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t wid) {
- if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay)
- return NULL;
-
- auto w = find_managed_win(ps, wid);
- if (!w)
- w = find_toplevel(ps, wid);
- if (!w)
- w = find_managed_window_or_parent(ps, wid);
- return w;
-}
-
-void queue_redraw(session_t *ps) {
- // If --benchmark is used, redraw is always queued
- if (!ps->redraw_needed && !ps->o.benchmark) {
- ev_idle_start(ps->loop, &ps->draw_idle);
- }
- ps->redraw_needed = true;
-}
-
-/**
- * Get a region of the screen size.
- */
-static inline void get_screen_region(session_t *ps, region_t *res) {
- pixman_box32_t b = {.x1 = 0, .y1 = 0, .x2 = ps->root_width, .y2 = ps->root_height};
- pixman_region32_fini(res);
- pixman_region32_init_rects(res, &b, 1);
-}
-
-void add_damage(session_t *ps, const region_t *damage) {
- // Ignore damage when screen isn't redirected
- if (!ps->redirected) {
- return;
- }
-
- if (!damage) {
- return;
- }
- log_trace("Adding damage: ");
- dump_region(damage);
- pixman_region32_union(ps->damage, ps->damage, (region_t *)damage);
-}
-
-// === Fading ===
-
-/**
- * Get the time left before next fading point.
- *
- * In milliseconds.
- */
-static double fade_timeout(session_t *ps) {
- auto now = get_time_ms();
- if (ps->o.fade_delta + ps->fade_time < now)
- return 0;
-
- auto diff = ps->o.fade_delta + ps->fade_time - now;
-
- diff = clamp(diff, 0, ps->o.fade_delta * 2);
-
- return (double)diff / 1000.0;
-}
-
-/**
- * Run fading on a window.
- *
- * @param steps steps of fading
- * @return whether we are still in fading mode
- */
-static bool run_fade(session_t *ps, struct managed_win **_w, long steps) {
- auto w = *_w;
- if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
- // We are not fading
- assert(w->opacity_target == w->opacity);
- return false;
- }
-
- if (!win_should_fade(ps, w)) {
- log_debug("Window %#010x %s doesn't need fading", w->base.id, w->name);
- w->opacity = w->opacity_target;
- }
- if (w->opacity == w->opacity_target) {
- // We have reached target opacity.
- // We don't call win_check_fade_finished here because that could destroy
- // the window, but we still need the damage info from this window
- log_debug("Fading finished for window %#010x %s", w->base.id, w->name);
- return false;
- }
-
- if (steps) {
- log_trace("Window %#010x (%s) opacity was: %lf", w->base.id, w->name,
- w->opacity);
- if (w->opacity < w->opacity_target) {
- w->opacity = clamp(w->opacity + ps->o.fade_in_step * (double)steps,
- 0.0, w->opacity_target);
- } else {
- w->opacity = clamp(w->opacity - ps->o.fade_out_step * (double)steps,
- w->opacity_target, 1);
- }
- log_trace("... updated to: %lf", w->opacity);
- }
-
- // Note even if opacity == opacity_target here, we still want to run preprocess
- // one last time to finish state transition. So return true in that case too.
- return true;
-}
-
-// === Error handling ===
-
-void discard_ignore(session_t *ps, unsigned long sequence) {
- while (ps->ignore_head) {
- if (sequence > ps->ignore_head->sequence) {
- ignore_t *next = ps->ignore_head->next;
- free(ps->ignore_head);
- ps->ignore_head = next;
- if (!ps->ignore_head) {
- ps->ignore_tail = &ps->ignore_head;
- }
- } else {
- break;
- }
- }
-}
-
-static int should_ignore(session_t *ps, unsigned long sequence) {
- if (ps == NULL) {
- // Do not ignore errors until the session has been initialized
- return false;
- }
- discard_ignore(ps, sequence);
- return ps->ignore_head && ps->ignore_head->sequence == sequence;
-}
-
-// === Windows ===
-
-/**
- * Determine the event mask for a window.
- */
-uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode) {
- uint32_t evmask = 0;
- struct managed_win *w = NULL;
-
- // Check if it's a mapped frame window
- if (mode == WIN_EVMODE_FRAME ||
- ((w = find_managed_win(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
- evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
- if (!ps->o.use_ewmh_active_win) {
- evmask |= XCB_EVENT_MASK_FOCUS_CHANGE;
- }
- }
-
- // Check if it's a mapped client window
- if (mode == WIN_EVMODE_CLIENT ||
- ((w = find_toplevel(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
- evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
- }
-
- return evmask;
-}
-
-/**
- * Update current active window based on EWMH _NET_ACTIVE_WIN.
- *
- * Does not change anything if we fail to get the attribute or the window
- * returned could not be found.
- */
-void update_ewmh_active_win(session_t *ps) {
- // Search for the window
- xcb_window_t wid =
- wid_get_prop_window(ps->c, ps->root, ps->atoms->a_NET_ACTIVE_WINDOW);
- auto w = find_win_all(ps, wid);
-
- // Mark the window focused. No need to unfocus the previous one.
- if (w) {
- win_set_focused(ps, w);
- }
-}
-
-/**
- * Recheck currently focused window and set its <code>w->focused</code>
- * to true.
- *
- * @param ps current session
- * @return struct _win of currently focused window, NULL if not found
- */
-static void recheck_focus(session_t *ps) {
- // Use EWMH _NET_ACTIVE_WINDOW if enabled
- if (ps->o.use_ewmh_active_win) {
- update_ewmh_active_win(ps);
- return;
- }
-
- // Determine the currently focused window so we can apply appropriate
- // opacity on it
- xcb_window_t wid = XCB_NONE;
- xcb_get_input_focus_reply_t *reply =
- xcb_get_input_focus_reply(ps->c, xcb_get_input_focus(ps->c), NULL);
-
- if (reply) {
- wid = reply->focus;
- free(reply);
- }
-
- auto w = find_win_all(ps, wid);
-
- log_trace("%#010" PRIx32 " (%#010lx \"%s\") focused.", wid,
- (w ? w->base.id : XCB_NONE), (w ? w->name : NULL));
-
- // And we set the focus state here
- if (w) {
- win_set_focused(ps, w);
- return;
- }
-}
-
-/**
- * Rebuild cached <code>screen_reg</code>.
- */
-static void rebuild_screen_reg(session_t *ps) {
- get_screen_region(ps, &ps->screen_reg);
-}
-
-/**
- * Rebuild <code>shadow_exclude_reg</code>.
- */
-static void rebuild_shadow_exclude_reg(session_t *ps) {
- bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, &ps->shadow_exclude_reg);
- if (!ret)
- exit(1);
-}
-
-/// Free up all the images and deinit the backend
-static void destroy_backend(session_t *ps) {
- win_stack_foreach_managed_safe(w, &ps->window_stack) {
- // Wrapping up fading in progress
- if (win_skip_fading(ps, w)) {
- // `w` is freed by win_skip_fading
- continue;
- }
-
- if (ps->backend_data) {
- // Unmapped windows could still have shadow images, but not pixmap
- // images
- assert(!w->win_image || w->state != WSTATE_UNMAPPED);
- if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) &&
- w->state == WSTATE_MAPPED) {
- log_warn("Stale flags set for mapped window %#010x "
- "during backend destruction",
- w->base.id);
- assert(false);
- }
- // Unmapped windows can still have stale flags set, because their
- // stale flags aren't handled until they are mapped.
- win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
- win_release_images(ps->backend_data, w);
- }
- free_paint(ps, &w->paint);
- }
-
- HASH_ITER2(ps->shaders, shader) {
- if (shader->backend_shader != NULL) {
- ps->backend_data->ops->destroy_shader(ps->backend_data,
- shader->backend_shader);
- shader->backend_shader = NULL;
- }
- }
-
- if (ps->backend_data && ps->root_image) {
- ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
- ps->root_image = NULL;
- }
-
- if (ps->backend_data) {
- // deinit backend
- if (ps->backend_blur_context) {
- ps->backend_data->ops->destroy_blur_context(
- ps->backend_data, ps->backend_blur_context);
- ps->backend_blur_context = NULL;
- }
- if (ps->shadow_context) {
- ps->backend_data->ops->destroy_shadow_context(ps->backend_data,
- ps->shadow_context);
- ps->shadow_context = NULL;
- }
- ps->backend_data->ops->deinit(ps->backend_data);
- ps->backend_data = NULL;
- }
-}
-
-static bool initialize_blur(session_t *ps) {
- struct kernel_blur_args kargs;
- struct gaussian_blur_args gargs;
- struct box_blur_args bargs;
- struct dual_kawase_blur_args dkargs;
-
- void *args = NULL;
- switch (ps->o.blur_method) {
- case BLUR_METHOD_BOX:
- bargs.size = ps->o.blur_radius;
- args = (void *)&bargs;
- break;
- case BLUR_METHOD_KERNEL:
- kargs.kernel_count = ps->o.blur_kernel_count;
- kargs.kernels = ps->o.blur_kerns;
- args = (void *)&kargs;
- break;
- case BLUR_METHOD_GAUSSIAN:
- gargs.size = ps->o.blur_radius;
- gargs.deviation = ps->o.blur_deviation;
- args = (void *)&gargs;
- break;
- case BLUR_METHOD_DUAL_KAWASE:
- dkargs.size = ps->o.blur_radius;
- dkargs.strength = ps->o.blur_strength;
- args = (void *)&dkargs;
- break;
- default: return true;
- }
-
- ps->backend_blur_context = ps->backend_data->ops->create_blur_context(
- ps->backend_data, ps->o.blur_method, args);
- return ps->backend_blur_context != NULL;
-}
-
-/// Init the backend and bind all the window pixmap to backend images
-static bool initialize_backend(session_t *ps) {
- if (!ps->o.legacy_backends) {
- assert(!ps->backend_data);
- // Reinitialize win_data
- assert(backend_list[ps->o.backend]);
- ps->backend_data = backend_list[ps->o.backend]->init(ps);
- if (!ps->backend_data) {
- log_fatal("Failed to initialize backend, aborting...");
- quit(ps);
- return false;
- }
- ps->backend_data->ops = backend_list[ps->o.backend];
- ps->shadow_context = ps->backend_data->ops->create_shadow_context(
- ps->backend_data, ps->o.shadow_radius);
- if (!ps->shadow_context) {
- log_fatal("Failed to initialize shadow context, aborting...");
- goto err;
- }
-
- if (!initialize_blur(ps)) {
- log_fatal("Failed to prepare for background blur, aborting...");
- goto err;
- }
-
- // Create shaders
- HASH_ITER2(ps->shaders, shader) {
- assert(shader->backend_shader == NULL);
- shader->backend_shader = ps->backend_data->ops->create_shader(
- ps->backend_data, shader->source);
- if (shader->backend_shader == NULL) {
- log_warn("Failed to create shader for shader file %s, "
- "this shader will not be used",
- shader->key);
- } else {
- if (ps->backend_data->ops->get_shader_attributes) {
- shader->attributes =
- ps->backend_data->ops->get_shader_attributes(
- ps->backend_data, shader->backend_shader);
- } else {
- shader->attributes = 0;
- }
- log_debug("Shader %s has attributes %" PRIu64,
- shader->key, shader->attributes);
- }
- }
-
- // window_stack shouldn't include window that's
- // not in the hash table at this point. Since
- // there cannot be any fading windows.
- HASH_ITER2(ps->windows, _w) {
- if (!_w->managed) {
- continue;
- }
- auto w = (struct managed_win *)_w;
- assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
- // We need to reacquire image
- log_debug("Marking window %#010x (%s) for update after "
- "redirection",
- w->base.id, w->name);
- win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
- ps->pending_updates = true;
- }
- }
-
- // The old backends binds pixmap lazily, nothing to do here
- return true;
-err:
- if (ps->shadow_context) {
- ps->backend_data->ops->destroy_shadow_context(ps->backend_data,
- ps->shadow_context);
- ps->shadow_context = NULL;
- }
- ps->backend_data->ops->deinit(ps->backend_data);
- ps->backend_data = NULL;
- quit(ps);
- return false;
-}
-
-/// Handle configure event of the root window
-static void configure_root(session_t *ps) {
- auto r = XCB_AWAIT(xcb_get_geometry, ps->c, ps->root);
- if (!r) {
- log_fatal("Failed to fetch root geometry");
- abort();
- }
-
- log_info("Root configuration changed, new geometry: %dx%d", r->width, r->height);
- bool has_root_change = false;
- if (ps->redirected) {
- // On root window changes
- if (!ps->o.legacy_backends) {
- assert(ps->backend_data);
- has_root_change = ps->backend_data->ops->root_change != NULL;
- } else {
- // Old backend can handle root change
- has_root_change = true;
- }
-
- if (!has_root_change) {
- // deinit/reinit backend and free up resources if the backend
- // cannot handle root change
- destroy_backend(ps);
- }
- free_paint(ps, &ps->tgt_buffer);
- }
-
- ps->root_width = r->width;
- ps->root_height = r->height;
-
- // auto prop = x_get_prop(ps->c, ps->root, ps->atoms->a_NET_CURRENT_DESKTOP,
- // 1L, XCB_ATOM_CARDINAL, 32);
-
- // ps->root_desktop_switch_direction = 0;
- // if (prop.nitems) {
- // ps->root_desktop_num = (int)*prop.c32;
- // }
-
- rebuild_screen_reg(ps);
- rebuild_shadow_exclude_reg(ps);
-
- // Invalidate reg_ignore from the top
- auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
- if (top_w) {
- rc_region_unref(&top_w->reg_ignore);
- top_w->reg_ignore_valid = false;
- }
-
- if (ps->redirected) {
- for (int i = 0; i < ps->ndamage; i++) {
- pixman_region32_clear(&ps->damage_ring[i]);
- }
- ps->damage = ps->damage_ring + ps->ndamage - 1;
-#ifdef CONFIG_OPENGL
- // GLX root change callback
- if (BKEND_GLX == ps->o.backend && ps->o.legacy_backends) {
- glx_on_root_change(ps);
- }
-#endif
- if (has_root_change) {
- if (ps->backend_data != NULL) {
- ps->backend_data->ops->root_change(ps->backend_data, ps);
- }
- // Old backend's root_change is not a specific function
- } else {
- if (!initialize_backend(ps)) {
- log_fatal("Failed to re-initialize backend after root "
- "change, aborting...");
- ps->quit = true;
- /* TODO(yshui) only event handlers should request
- * ev_break, otherwise it's too hard to keep track of what
- * can break the event loop */
- ev_break(ps->loop, EVBREAK_ALL);
- return;
- }
-
- // Re-acquire the root pixmap.
- root_damaged(ps);
- }
- force_repaint(ps);
- }
- return;
-}
-
-static void handle_root_flags(session_t *ps) {
- if ((ps->root_flags & ROOT_FLAGS_SCREEN_CHANGE) != 0) {
- if (ps->o.xinerama_shadow_crop) {
- cxinerama_upd_scrs(ps);
- }
- ps->root_flags &= ~(uint64_t)ROOT_FLAGS_SCREEN_CHANGE;
- }
-
- if ((ps->root_flags & ROOT_FLAGS_CONFIGURED) != 0) {
- configure_root(ps);
- ps->root_flags &= ~(uint64_t)ROOT_FLAGS_CONFIGURED;
- }
-}
-
-static struct managed_win *
-paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
- // 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;
- *fade_running = false;
- *animation_running = false;
- auto now = get_time_ms();
-
- // Fading step calculation
- long steps = 0L;
- if (ps->fade_time) {
- assert(now >= ps->fade_time);
- steps = (now - ps->fade_time) / ps->o.fade_delta;
- } else {
- // Reset fade_time if unset
- ps->fade_time = now;
- steps = 0L;
- }
- ps->fade_time += steps * ps->o.fade_delta;
-
- double animation_delta = 0;
- if (ps->o.animations) {
- if (!ps->animation_time)
- ps->animation_time = now;
-
- animation_delta = (double)(now - ps->animation_time) /
- (ps->o.animation_delta*100);
-
- if (ps->o.animation_force_steps)
- animation_delta = min2(animation_delta, ps->o.animation_delta/1000);
- }
-
- // First, let's process fading, and animated shaders
- // TODO(yshui) check if a window is fully obscured, and if we don't need to
- // process fading or animation for it.
- win_stack_foreach_managed_safe(w, &ps->window_stack) {
- const winmode_t mode_old = w->mode;
- const bool was_painted = w->to_paint;
- const double opacity_old = w->opacity;
-
- // IMPORTANT: These window animation steps must happen before any other
- // [pre]processing. This is because it changes the window's geometry.
- if (ps->o.animations &&
- !isnan(w->animation_progress) && w->animation_progress != 1.0 &&
- ps->o.wintype_option[w->window_type].animation != 0 &&
- win_is_mapped_in_x(w))
- {
- double neg_displacement_x =
- w->animation_dest_center_x - w->animation_center_x;
- double neg_displacement_y =
- w->animation_dest_center_y - w->animation_center_y;
- double neg_displacement_w = w->animation_dest_w - w->animation_w;
- double neg_displacement_h = w->animation_dest_h - w->animation_h;
- double acceleration_x =
- (ps->o.animation_stiffness * neg_displacement_x -
- ps->o.animation_dampening * w->animation_velocity_x) /
- ps->o.animation_window_mass;
- double acceleration_y =
- (ps->o.animation_stiffness * neg_displacement_y -
- ps->o.animation_dampening * w->animation_velocity_y) /
- ps->o.animation_window_mass;
- double acceleration_w =
- (ps->o.animation_stiffness * neg_displacement_w -
- ps->o.animation_dampening * w->animation_velocity_w) /
- ps->o.animation_window_mass;
- double acceleration_h =
- (ps->o.animation_stiffness * neg_displacement_h -
- ps->o.animation_dampening * w->animation_velocity_h) /
- ps->o.animation_window_mass;
- w->animation_velocity_x += acceleration_x * animation_delta;
- w->animation_velocity_y += acceleration_y * animation_delta;
- w->animation_velocity_w += acceleration_w * animation_delta;
- w->animation_velocity_h += acceleration_h * animation_delta;
-
- // Animate window geometry
- double new_animation_x =
- w->animation_center_x + w->animation_velocity_x * animation_delta;
- double new_animation_y =
- w->animation_center_y + w->animation_velocity_y * animation_delta;
- double new_animation_w =
- w->animation_w + w->animation_velocity_w * animation_delta;
- double new_animation_h =
- w->animation_h + w->animation_velocity_h * animation_delta;
-
- // Negative new width/height causes segfault and it can happen
- // when clamping disabled and shading a window
- if (new_animation_h < 0)
- new_animation_h = 0;
-
- if (new_animation_w < 0)
- new_animation_w = 0;
-
- if (ps->o.animation_clamping) {
- w->animation_center_x = clamp(
- new_animation_x,
- min2(w->animation_center_x, w->animation_dest_center_x),
- max2(w->animation_center_x, w->animation_dest_center_x));
- w->animation_center_y = clamp(
- new_animation_y,
- min2(w->animation_center_y, w->animation_dest_center_y),
- max2(w->animation_center_y, w->animation_dest_center_y));
- w->animation_w =
- clamp(new_animation_w,
- min2(w->animation_w, w->animation_dest_w),
- max2(w->animation_w, w->animation_dest_w));
- w->animation_h =
- clamp(new_animation_h,
- min2(w->animation_h, w->animation_dest_h),
- max2(w->animation_h, w->animation_dest_h));
- } else {
- w->animation_center_x = new_animation_x;
- w->animation_center_y = new_animation_y;
- w->animation_w = new_animation_w;
- w->animation_h = new_animation_h;
- }
-
- // Now we are done doing the math; we just need to submit our
- // changes (if there are any).
-
- struct win_geometry old_g = w->g;
- double old_animation_progress = w->animation_progress;
- new_animation_x = round(w->animation_center_x - w->animation_w * 0.5);
- new_animation_y = round(w->animation_center_y - w->animation_h * 0.5);
- new_animation_w = round(w->animation_w);
- new_animation_h = round(w->animation_h);
-
- bool position_changed =
- new_animation_x != old_g.x || new_animation_y != old_g.y;
- bool size_changed =
- new_animation_w != old_g.width || new_animation_h != old_g.height;
- bool geometry_changed = position_changed || size_changed;
-
- // Mark past window region with damage
- if (was_painted && geometry_changed)
- add_damage_from_win(ps, w);
-
- double x_dist = w->animation_dest_center_x - w->animation_center_x;
- double y_dist = w->animation_dest_center_y - w->animation_center_y;
- double w_dist = w->animation_dest_w - w->animation_w;
- double h_dist = w->animation_dest_h - w->animation_h;
- w->animation_progress =
- 1.0 - w->animation_inv_og_distance *
- sqrt(x_dist * x_dist + y_dist * y_dist +
- w_dist * w_dist + h_dist * h_dist);
-
- // When clamping disabled we don't want the overlayed image to
- // fade in again because process is moving to negative value
- if (w->animation_progress < old_animation_progress)
- w->animation_progress = old_animation_progress;
-
- w->g.x = (int16_t)new_animation_x;
- w->g.y = (int16_t)new_animation_y;
- w->g.width = (uint16_t)new_animation_w;
- w->g.height = (uint16_t)new_animation_h;
-
- // Submit window size change
- if (size_changed) {
- win_on_win_size_change(ps, w);
-
- // if (ps->o.support_for_wm == WM_SUPPORT_AWESOME) {
- // win_update_bounding_shape(ps, w);
- // } else if (ps->o.support_for_wm == WM_SUPPORT_HERB) {
- // win_update_bounding_shape(ps, w);
- // } else {
- // pixman_region32_clear(&w->bounding_shape);
- // pixman_region32_fini(&w->bounding_shape);
- // pixman_region32_init_rect(&w->bounding_shape, 0, 0,
- // (uint)w->widthb, (uint)w->heightb);
- // }
-
- if (ps->o.support_for_wm == WM_SUPPORT_LEGACY) {
- pixman_region32_clear(&w->bounding_shape);
- pixman_region32_fini(&w->bounding_shape);
- pixman_region32_init_rect(&w->bounding_shape, 0, 0,
- (uint)w->widthb, (uint)w->heightb);
- } else {
- win_update_bounding_shape(ps, w);
- }
-
- if (w->state != WSTATE_DESTROYING)
- win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
-
- win_process_image_flags(ps, w);
- }
- // Mark new window region with damage
- if (was_painted && geometry_changed) {
- add_damage_from_win(ps, w);
- w->reg_ignore_valid = false;
- }
-
- // We can't check for 1 here as sometimes 1 = 0.999999999999999
- // in case of floating numbers
- if (w->animation_progress >= 0.999999999) {
- w->animation_progress = 1;
- w->animation_velocity_x = 0.0;
- w->animation_velocity_y = 0.0;
- w->animation_velocity_w = 0.0;
- w->animation_velocity_h = 0.0;
- }
-
- // if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
- // steps = 0;
- // double new_opacity = clamp(
- // w->opacity_target_old-w->animation_progress,
- // w->opacity_target, 1);
-
- // if (new_opacity < w->opacity)
- // w->opacity = new_opacity;
-
- // } else if (w->state == WSTATE_MAPPING) {
- // steps = 0;
- // double new_opacity = clamp(
- // w->animation_progress,
- // 0.0, w->opacity_target);
-
- // if (new_opacity > w->opacity)
- // w->opacity = new_opacity;
- // }
-
- *animation_running = true;
- }
-
- if (win_should_dim(ps, w) != w->dim) {
- w->dim = win_should_dim(ps, w);
- add_damage_from_win(ps, w);
- }
-
- if (w->fg_shader && (w->fg_shader->attributes & SHADER_ATTRIBUTE_ANIMATED)) {
- add_damage_from_win(ps, w);
- *animation_running = true;
- }
-
- if (w->opacity != w->opacity_target) {
- // Run fading
- if (run_fade(ps, &w, steps)) {
- *fade_running = true;
- }
-
- // Add window to damaged area if its opacity changes
- // If was_painted == false, and to_paint is also false, we don't care
- // If was_painted == false, but to_paint is true, damage will be added in
- // the loop below
- if (was_painted && w->opacity != opacity_old) {
- add_damage_from_win(ps, w);
- }
-
- if (win_check_fade_finished(ps, w)) {
- // the window has been destroyed because fading finished
- continue;
- }
-
- if (win_has_frame(w)) {
- w->frame_opacity = ps->o.frame_opacity;
- } else {
- w->frame_opacity = 1.0;
- }
-
- // Update window mode
- w->mode = win_calc_mode(w);
-
- // Destroy all reg_ignore above when frame opaque state changes on
- // SOLID mode
- if (was_painted && w->mode != mode_old) {
- w->reg_ignore_valid = false;
- }
- }
- }
-
- if (*animation_running)
- ps->animation_time = now;
-
- // Opacity will not change, from now on.
- rc_region_t *last_reg_ignore = rc_region_new();
-
- bool unredir_possible = false;
- // Track whether it's the highest window to paint
- bool is_highest = true;
- bool reg_ignore_valid = true;
- win_stack_foreach_managed(w, &ps->window_stack) {
- __label__ skip_window;
- bool to_paint = true;
- // w->to_paint remembers whether this window is painted last time
- const bool was_painted = w->to_paint;
-
- // Destroy reg_ignore if some window above us invalidated it
- if (!reg_ignore_valid) {
- rc_region_unref(&w->reg_ignore);
- }
-
- // log_trace("%d %d %s", w->a.map_state, w->ever_damaged, 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
- // excluded
- if (w->state == WSTATE_UNMAPPED ||
- unlikely(w->base.id == ps->debug_window ||
- w->client_win == ps->debug_window)) {
-
- if (!*fade_running || w->opacity == w->opacity_target)
- to_paint = false;
-
- } else if (!w->ever_damaged && w->state != WSTATE_UNMAPPING &&
- w->state != WSTATE_DESTROYING) {
- // Unmapping clears w->ever_damaged, but the fact that the window
- // is fading out means it must have been damaged when it was still
- // mapped (because unmap_win_start will skip fading if it wasn't),
- // so we still need to paint it.
- log_trace("Window %#010x (%s) will not be painted because it has "
- "not received any damages",
- w->base.id, w->name);
- to_paint = false;
- } else if (unlikely(w->g.x + w->g.width < 1 || w->g.y + w->g.height < 1 ||
- w->g.x >= ps->root_width || w->g.y >= ps->root_height)) {
- log_trace("Window %#010x (%s) will not be painted because it is "
- "positioned outside of the screen",
- w->base.id, w->name);
- to_paint = false;
- } else if (unlikely((double)w->opacity * MAX_ALPHA < 1 && !w->blur_background)) {
- /* TODO(yshui) for consistency, even a window has 0 opacity, we
- * still probably need to blur its background, so to_paint
- * shouldn't be false for them. */
- log_trace("Window %#010x (%s) will not be painted because it has "
- "0 opacity",
- w->base.id, w->name);
- to_paint = false;
- } else if (w->paint_excluded) {
- log_trace("Window %#010x (%s) will not be painted because it is "
- "excluded from painting",
- w->base.id, w->name);
- to_paint = false;
- } else if (unlikely((w->flags & WIN_FLAGS_IMAGE_ERROR) != 0)) {
- log_trace("Window %#010x (%s) will not be painted because it has "
- "image errors",
- w->base.id, w->name);
- to_paint = false;
- }
- // log_trace("%s %d %d %d", w->name, to_paint, w->opacity,
- // w->paint_excluded);
-
- // Add window to damaged area if its painting status changes
- // or opacity changes
- if (to_paint != was_painted) {
- w->reg_ignore_valid = false;
- add_damage_from_win(ps, w);
- }
-
- // to_paint will never change after this point
- if (!to_paint) {
- goto skip_window;
- }
-
- log_trace("Window %#010x (%s) will be painted", w->base.id, w->name);
-
- // Calculate shadow opacity
- w->shadow_opacity = ps->o.shadow_opacity * w->opacity * ps->o.frame_opacity;
-
- // Generate ignore region for painting to reduce GPU load
- if (!w->reg_ignore) {
- w->reg_ignore = rc_region_ref(last_reg_ignore);
- }
-
- // If the window is solid, or we enabled clipping for transparent windows,
- // we add the window region to the ignored region
- // Otherwise last_reg_ignore shouldn't change
- if ((w->mode != WMODE_TRANS && !ps->o.force_win_blend) ||
- (ps->o.transparent_clipping && !w->transparent_clipping_excluded)) {
- // w->mode == WMODE_SOLID or WMODE_FRAME_TRANS
- region_t *tmp = rc_region_new();
- if (w->mode == WMODE_SOLID) {
- *tmp =
- win_get_bounding_shape_global_without_corners_by_val(w);
- } else {
- // w->mode == WMODE_FRAME_TRANS
- win_get_region_noframe_local_without_corners(w, tmp);
- pixman_region32_intersect(tmp, tmp, &w->bounding_shape);
- pixman_region32_translate(tmp, w->g.x, w->g.y);
- }
-
- pixman_region32_union(tmp, tmp, last_reg_ignore);
- rc_region_unref(&last_reg_ignore);
- last_reg_ignore = tmp;
- }
-
- // (Un)redirect screen
- // We could definitely unredirect the screen when there's no window to
- // paint, but this is typically unnecessary, may cause flickering when
- // fading is enabled, and could create inconsistency when the wallpaper
- // is not correctly set.
- if (ps->o.unredir_if_possible && is_highest) {
- if (w->mode == WMODE_SOLID && !ps->o.force_win_blend &&
- win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) {
- unredir_possible = true;
- }
- }
-
- // Unredirect screen if some window is requesting compositor bypass, even
- // if that window is not on the top.
- if (ps->o.unredir_if_possible && win_is_bypassing_compositor(ps, w) &&
- !w->unredir_if_possible_excluded) {
- // Here we deviate from EWMH a bit. EWMH says we must not
- // unredirect the screen if the window requesting bypassing would
- // look different after unredirecting. Instead we always follow
- // the request.
- unredir_possible = true;
- }
-
- 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,
- // this window should not cause the screen to become redirected
- if (!(ps->o.wintype_option[w->window_type].redir_ignore && !ps->redirected)) {
- is_highest = false;
- }
-
- skip_window:
- reg_ignore_valid = reg_ignore_valid && w->reg_ignore_valid;
- w->reg_ignore_valid = true;
-
- // Avoid setting w->to_paint if w is freed
- if (w) {
- w->to_paint = to_paint;
- }
- }
-
- rc_region_unref(&last_reg_ignore);
-
- // If possible, unredirect all windows and stop painting
- if (ps->o.redirected_force != UNSET) {
- unredir_possible = !ps->o.redirected_force;
- } else if (ps->o.unredir_if_possible && is_highest && !ps->redirected) {
- // If there's no window to paint, and the screen isn't redirected,
- // don't redirect it.
- unredir_possible = true;
- }
- if (unredir_possible) {
- if (ps->redirected) {
- if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) {
- unredirect(ps);
- } else if (!ev_is_active(&ps->unredir_timer)) {
- ev_timer_set(
- &ps->unredir_timer,
- (double)ps->o.unredir_if_possible_delay / 1000.0, 0);
- ev_timer_start(ps->loop, &ps->unredir_timer);
- }
- }
- } else {
- ev_timer_stop(ps->loop, &ps->unredir_timer);
- if (!ps->redirected) {
- if (!redirect_start(ps)) {
- return NULL;
- }
- }
- }
-
- return bottom;
-}
-
-void root_damaged(session_t *ps) {
- if (ps->root_tile_paint.pixmap) {
- free_root_tile(ps);
- }
-
- if (!ps->redirected) {
- return;
- }
-
- if (ps->backend_data) {
- if (ps->root_image) {
- ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
- }
- auto pixmap = x_get_root_back_pixmap(ps->c, ps->root, ps->atoms);
- if (pixmap != XCB_NONE) {
- ps->root_image = ps->backend_data->ops->bind_pixmap(
- ps->backend_data, pixmap, x_get_visual_info(ps->c, ps->vis), false);
- if (ps->root_image) {
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
- ps->root_image, (int[]){ps->root_width, ps->root_height});
- } else {
- log_error("Failed to bind root back pixmap");
- }
- }
- }
-
- // Mark screen damaged
- force_repaint(ps);
-}
-
-/**
- * Xlib error handler function.
- */
-static int xerror(Display attr_unused *dpy, XErrorEvent *ev) {
- if (!should_ignore(ps_g, ev->serial)) {
- x_print_error(ev->serial, ev->request_code, ev->minor_code, ev->error_code);
- }
- return 0;
-}
-
-/**
- * XCB error handler function.
- */
-void ev_xcb_error(session_t *ps, xcb_generic_error_t *err) {
- if (!should_ignore(ps, err->sequence)) {
- x_print_error(err->sequence, err->major_code, err->minor_code, err->error_code);
- }
-}
-
-/**
- * Force a full-screen repaint.
- */
-void force_repaint(session_t *ps) {
- assert(pixman_region32_not_empty(&ps->screen_reg));
- queue_redraw(ps);
- add_damage(ps, &ps->screen_reg);
-}
-
-#ifdef CONFIG_DBUS
-/** @name DBus hooks
- */
-///@{
-
-/**
- * Set no_fading_openclose option.
- *
- * Don't affect fading already in progress
- */
-void opts_set_no_fading_openclose(session_t *ps, bool newval) {
- ps->o.no_fading_openclose = newval;
-}
-
-//!@}
-#endif
-
-/**
- * Setup window properties, then register us with the compositor selection (_NET_WM_CM_S)
- *
- * @return 0 if success, 1 if compositor already running, -1 if error.
- */
-static int register_cm(session_t *ps) {
- assert(!ps->reg_win);
-
- ps->reg_win = x_new_id(ps->c);
- auto e = xcb_request_check(
- ps->c, xcb_create_window_checked(ps->c, XCB_COPY_FROM_PARENT, ps->reg_win, ps->root,
- 0, 0, 1, 1, 0, XCB_NONE, ps->vis, 0, NULL));
-
- if (e) {
- log_fatal("Failed to create window.");
- free(e);
- return -1;
- }
-
- const xcb_atom_t prop_atoms[] = {
- ps->atoms->aWM_NAME,
- ps->atoms->a_NET_WM_NAME,
- ps->atoms->aWM_ICON_NAME,
- };
-
- const bool prop_is_utf8[] = {false, true, false};
-
- // Set names and classes
- for (size_t i = 0; i < ARR_SIZE(prop_atoms); i++) {
- e = xcb_request_check(
- ps->c, xcb_change_property_checked(
- ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win, prop_atoms[i],
- prop_is_utf8[i] ? ps->atoms->aUTF8_STRING : XCB_ATOM_STRING,
- 8, strlen("picom"), "picom"));
- if (e) {
- log_error_x_error(e, "Failed to set window property %d",
- prop_atoms[i]);
- free(e);
- }
- }
-
- const char picom_class[] = "picom\0picom";
- e = xcb_request_check(
- ps->c, xcb_change_property_checked(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
- ps->atoms->aWM_CLASS, XCB_ATOM_STRING, 8,
- ARR_SIZE(picom_class), picom_class));
- if (e) {
- log_error_x_error(e, "Failed to set the WM_CLASS property");
- free(e);
- }
-
- // Set WM_CLIENT_MACHINE. As per EWMH, because we set _NET_WM_PID, we must also
- // set WM_CLIENT_MACHINE.
- {
- const auto hostname_max = (unsigned long)sysconf(_SC_HOST_NAME_MAX);
- char *hostname = malloc(hostname_max);
-
- if (gethostname(hostname, hostname_max) == 0) {
- e = xcb_request_check(
- ps->c, xcb_change_property_checked(
- ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
- ps->atoms->aWM_CLIENT_MACHINE, XCB_ATOM_STRING, 8,
- (uint32_t)strlen(hostname), hostname));
- if (e) {
- log_error_x_error(e, "Failed to set the WM_CLIENT_MACHINE"
- " property");
- free(e);
- }
- } else {
- log_error_errno("Failed to get hostname");
- }
-
- free(hostname);
- }
-
- // Set _NET_WM_PID
- {
- auto pid = getpid();
- xcb_change_property(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
- ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
- }
-
- // Set COMPTON_VERSION
- e = xcb_request_check(
- ps->c, xcb_change_property_checked(
- ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
- get_atom(ps->atoms, "COMPTON_VERSION"), XCB_ATOM_STRING, 8,
- (uint32_t)strlen(PICOM_VERSION), PICOM_VERSION));
- if (e) {
- log_error_x_error(e, "Failed to set COMPTON_VERSION.");
- free(e);
- }
-
- // Acquire X Selection _NET_WM_CM_S?
- if (!ps->o.no_x_selection) {
- const char register_prop[] = "_NET_WM_CM_S";
- xcb_atom_t atom;
-
- char *buf = NULL;
- if (asprintf(&buf, "%s%d", register_prop, ps->scr) < 0) {
- log_fatal("Failed to allocate memory");
- return -1;
- }
- atom = get_atom(ps->atoms, buf);
- free(buf);
-
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(
- ps->c, xcb_get_selection_owner(ps->c, atom), NULL);
-
- if (reply && reply->owner != XCB_NONE) {
- // Another compositor already running
- free(reply);
- return 1;
- }
- free(reply);
- xcb_set_selection_owner(ps->c, ps->reg_win, atom, 0);
- }
-
- return 0;
-}
-
-/**
- * Write PID to a file.
- */
-static inline bool write_pid(session_t *ps) {
- if (!ps->o.write_pid_path) {
- return true;
- }
-
- FILE *f = fopen(ps->o.write_pid_path, "w");
- if (unlikely(!f)) {
- log_error("Failed to write PID to \"%s\".", ps->o.write_pid_path);
- return false;
- }
-
- fprintf(f, "%ld\n", (long)getpid());
- fclose(f);
-
- return true;
-}
-
-/**
- * Initialize X composite overlay window.
- */
-static bool init_overlay(session_t *ps) {
- xcb_composite_get_overlay_window_reply_t *reply =
- xcb_composite_get_overlay_window_reply(
- ps->c, xcb_composite_get_overlay_window(ps->c, ps->root), NULL);
- if (reply) {
- ps->overlay = reply->overlay_win;
- free(reply);
- } else {
- ps->overlay = XCB_NONE;
- }
- if (ps->overlay != XCB_NONE) {
- // Set window region of the overlay window, code stolen from
- // compiz-0.8.8
- if (!XCB_AWAIT_VOID(xcb_shape_mask, ps->c, XCB_SHAPE_SO_SET,
- XCB_SHAPE_SK_BOUNDING, ps->overlay, 0, 0, 0)) {
- log_fatal("Failed to set the bounding shape of overlay, giving "
- "up.");
- return false;
- }
- if (!XCB_AWAIT_VOID(xcb_shape_rectangles, ps->c, XCB_SHAPE_SO_SET,
- XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED,
- ps->overlay, 0, 0, 0, NULL)) {
- log_fatal("Failed to set the input shape of overlay, giving up.");
- return false;
- }
-
- // Listen to Expose events on the overlay
- xcb_change_window_attributes(ps->c, ps->overlay, XCB_CW_EVENT_MASK,
- (const uint32_t[]){XCB_EVENT_MASK_EXPOSURE});
-
- // Retrieve DamageNotify on root window if we are painting on an
- // overlay
- // root_damage = XDamageCreate(ps->dpy, root, XDamageReportNonEmpty);
-
- // Unmap the overlay, we will map it when needed in redirect_start
- XCB_AWAIT_VOID(xcb_unmap_window, ps->c, ps->overlay);
- } else {
- log_error("Cannot get X Composite overlay window. Falling "
- "back to painting on root window.");
- }
- log_debug("overlay = %#010x", ps->overlay);
-
- return true;
-}
-
-static bool init_debug_window(session_t *ps) {
- xcb_colormap_t colormap = x_new_id(ps->c);
- ps->debug_window = x_new_id(ps->c);
-
- auto err = xcb_request_check(
- ps->c, xcb_create_colormap_checked(ps->c, XCB_COLORMAP_ALLOC_NONE, colormap,
- ps->root, ps->vis));
- if (err) {
- goto err_out;
- }
-
- err = xcb_request_check(
- ps->c, xcb_create_window_checked(ps->c, (uint8_t)ps->depth, ps->debug_window,
- ps->root, 0, 0, to_u16_checked(ps->root_width),
- to_u16_checked(ps->root_height), 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT, ps->vis,
- XCB_CW_COLORMAP, (uint32_t[]){colormap, 0}));
- if (err) {
- goto err_out;
- }
-
- err = xcb_request_check(ps->c, xcb_map_window(ps->c, ps->debug_window));
- if (err) {
- goto err_out;
- }
- return true;
-
-err_out:
- free(err);
- return false;
-}
-
-xcb_window_t session_get_target_window(session_t *ps) {
- if (ps->o.debug_mode) {
- return ps->debug_window;
- }
- return ps->overlay != XCB_NONE ? ps->overlay : ps->root;
-}
-
-uint8_t session_redirection_mode(session_t *ps) {
- if (ps->o.debug_mode) {
- // If the backend is not rendering to the screen, we don't need to
- // take over the screen.
- assert(!ps->o.legacy_backends);
- return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
- }
- if (!ps->o.legacy_backends && !backend_list[ps->o.backend]->present) {
- // if the backend doesn't render anything, we don't need to take over the
- // screen.
- return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
- }
- return XCB_COMPOSITE_REDIRECT_MANUAL;
-}
-
-/**
- * Redirect all windows.
- *
- * @return whether the operation succeeded or not
- */
-static bool redirect_start(session_t *ps) {
- assert(!ps->redirected);
- log_debug("Redirecting the screen.");
-
- // Map overlay window. Done firstly according to this:
- // https://bugzilla.gnome.org/show_bug.cgi?id=597014
- if (ps->overlay != XCB_NONE) {
- xcb_map_window(ps->c, ps->overlay);
- }
-
- bool success = XCB_AWAIT_VOID(xcb_composite_redirect_subwindows, ps->c, ps->root,
- session_redirection_mode(ps));
- if (!success) {
- log_fatal("Another composite manager is already running "
- "(and does not handle _NET_WM_CM_Sn correctly)");
- return false;
- }
-
- x_sync(ps->c);
-
- if (!initialize_backend(ps)) {
- return false;
- }
-
- if (!ps->o.legacy_backends) {
- assert(ps->backend_data);
- ps->ndamage = ps->backend_data->ops->max_buffer_age;
- } else {
- ps->ndamage = maximum_buffer_age(ps);
- }
- ps->damage_ring = ccalloc(ps->ndamage, region_t);
- ps->damage = ps->damage_ring + ps->ndamage - 1;
-
- for (int i = 0; i < ps->ndamage; i++) {
- pixman_region32_init(&ps->damage_ring[i]);
- }
-
- // Must call XSync() here
- x_sync(ps->c);
-
- ps->redirected = true;
- ps->first_frame = true;
-
- // Re-detect driver since we now have a backend
- ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root);
- apply_driver_workarounds(ps, ps->drivers);
-
- root_damaged(ps);
-
- // Repaint the whole screen
- force_repaint(ps);
- log_debug("Screen redirected.");
- return true;
-}
-
-/**
- * Unredirect all windows.
- */
-static void unredirect(session_t *ps) {
- assert(ps->redirected);
- log_debug("Unredirecting the screen.");
-
- destroy_backend(ps);
-
- xcb_composite_unredirect_subwindows(ps->c, ps->root, session_redirection_mode(ps));
- // Unmap overlay window
- if (ps->overlay != XCB_NONE) {
- xcb_unmap_window(ps->c, ps->overlay);
- }
-
- // Free the damage ring
- for (int i = 0; i < ps->ndamage; ++i) {
- pixman_region32_fini(&ps->damage_ring[i]);
- }
- ps->ndamage = 0;
- free(ps->damage_ring);
- ps->damage_ring = ps->damage = NULL;
-
- // Must call XSync() here
- x_sync(ps->c);
-
- ps->redirected = false;
- log_debug("Screen unredirected.");
-}
-
-// Handle queued events before we go to sleep
-static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents attr_unused) {
- session_t *ps = session_ptr(w, event_check);
- xcb_generic_event_t *ev;
- while ((ev = xcb_poll_for_queued_event(ps->c))) {
- ev_handle(ps, ev);
- free(ev);
- };
- // Flush because if we go into sleep when there is still
- // requests in the outgoing buffer, they will not be sent
- // for an indefinite amount of time.
- // Use XFlush here too, we might still use some Xlib functions
- // because OpenGL.
- XFlush(ps->dpy);
- xcb_flush(ps->c);
- int err = xcb_connection_has_error(ps->c);
- if (err) {
- log_fatal("X11 server connection broke (error %d)", err);
- exit(1);
- }
-}
-
-static void handle_new_windows(session_t *ps) {
- list_foreach_safe(struct win, w, &ps->window_stack, stack_neighbour) {
- if (w->is_new) {
- auto new_w = fill_win(ps, w);
- if (!new_w->managed) {
- continue;
- }
- 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;
- }
- }
- }
-}
-
-static void refresh_windows(session_t *ps) {
- win_stack_foreach_managed(w, &ps->window_stack) {
- win_process_update_flags(ps, w);
- }
-}
-
-static void refresh_images(session_t *ps) {
- win_stack_foreach_managed(w, &ps->window_stack) {
- win_process_image_flags(ps, w);
- }
-}
-
-/**
- * Unredirection timeout callback.
- */
-static void tmout_unredir_callback(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
- session_t *ps = session_ptr(w, unredir_timer);
- ps->tmout_unredir_hit = true;
- queue_redraw(ps);
-}
-
-static void fade_timer_callback(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
- session_t *ps = session_ptr(w, fade_timer);
- queue_redraw(ps);
-}
-
-static void animation_timer_callback(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
- session_t *ps = session_ptr(w, animation_timer);
- queue_redraw(ps);
-}
-
-static void handle_pending_updates(EV_P_ struct session *ps) {
- if (ps->pending_updates) {
- log_debug("Delayed handling of events, entering critical section");
- auto e = xcb_request_check(ps->c, xcb_grab_server_checked(ps->c));
- if (e) {
- log_fatal_x_error(e, "failed to grab x server");
- return quit(ps);
- }
-
- ps->server_grabbed = true;
-
- // Catching up with X server
- handle_queued_x_events(EV_A_ & ps->event_check, 0);
-
- // Call fill_win on new windows
- handle_new_windows(ps);
-
- // 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
- // stale.
- handle_root_flags(ps);
-
- // Process window flags (window mapping)
- refresh_windows(ps);
-
- {
- auto r = xcb_get_input_focus_reply(
- ps->c, xcb_get_input_focus(ps->c), NULL);
- if (!ps->active_win || (r && r->focus != ps->active_win->base.id)) {
- recheck_focus(ps);
- }
- free(r);
- }
-
- // Process window flags (stale images)
- refresh_images(ps);
-
- e = xcb_request_check(ps->c, xcb_ungrab_server_checked(ps->c));
- if (e) {
- log_fatal_x_error(e, "failed to ungrab x server");
- return quit(ps);
- }
-
- ps->server_grabbed = false;
- ps->pending_updates = false;
- log_debug("Exited critical section");
- }
-}
-
-static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
- handle_pending_updates(EV_A_ ps);
-
- if (ps->first_frame) {
- // If we are still rendering the first frame, if some of the windows are
- // unmapped/destroyed during the above handle_pending_updates() call, they
- // won't have pixmap before we rendered it, causing us to crash.
- // But we will only render them if they are in fading. So we just skip
- // fading for all windows here.
- //
- // Using foreach_safe here since skipping fading can cause window to be
- // freed if it's destroyed.
- win_stack_foreach_managed_safe(w, &ps->window_stack) {
- auto _ attr_unused = win_skip_fading(ps, w);
- }
- }
-
- if (ps->o.benchmark) {
- if (ps->o.benchmark_wid) {
- auto w = find_managed_win(ps, ps->o.benchmark_wid);
- if (!w) {
- log_fatal("Couldn't find specified benchmark window.");
- exit(1);
- }
- add_damage_from_win(ps, w);
- } else {
- force_repaint(ps);
- }
- }
-
- /* TODO(yshui) Have a stripped down version of paint_preprocess that is used when
- * screen is not redirected. its sole purpose should be to decide whether the
- * screen should be redirected. */
- bool fade_running = false;
- bool animation_running = false;
- bool was_redirected = ps->redirected;
- auto bottom = paint_preprocess(ps, &fade_running, &animation_running);
- ps->tmout_unredir_hit = false;
-
- if (!was_redirected && ps->redirected) {
- // paint_preprocess redirected the screen, which might change the state of
- // some of the windows (e.g. the window image might become stale).
- // so we rerun _draw_callback to make sure the rendering decision we make
- // is up-to-date, and all the new flags got handled.
- //
- // TODO(yshui) This is not ideal, we should try to avoid setting window
- // flags in paint_preprocess.
- log_debug("Re-run _draw_callback");
- return draw_callback_impl(EV_A_ ps, revents);
- }
-
- // Start/stop fade timer depends on whether window are fading
- if (!fade_running && ev_is_active(&ps->fade_timer)) {
- ev_timer_stop(EV_A_ & ps->fade_timer);
- } else if (fade_running && !ev_is_active(&ps->fade_timer)) {
- ev_timer_set(&ps->fade_timer, fade_timeout(ps), 0);
- ev_timer_start(EV_A_ & ps->fade_timer);
- }
-
- // Start/stop animation timer depends on whether windows are animating
- if (!animation_running && ev_is_active(&ps->animation_timer)) {
- ev_timer_stop(EV_A_ & ps->animation_timer);
- } else if (animation_running && !ev_is_active(&ps->animation_timer)) {
- ev_timer_set(&ps->animation_timer, 0, 0);
- ev_timer_start(EV_A_ & ps->animation_timer);
- }
-
- // If the screen is unredirected, free all_damage to stop painting
- if (ps->redirected && ps->o.stoppaint_force != ON) {
- static int paint = 0;
-
- log_trace("Render start, frame %d", paint);
- if (!ps->o.legacy_backends) {
- paint_all_new(ps, bottom, false);
- } else {
- paint_all(ps, bottom, false);
- }
- log_trace("Render end");
-
- ps->first_frame = false;
- paint++;
- if (ps->o.benchmark && paint >= ps->o.benchmark) {
- exit(0);
- }
- }
-
- if (!fade_running) {
- ps->fade_time = 0L;
- }
- if (!animation_running) {
- ps->animation_time = 0L;
- // ps->root_desktop_switch_direction = 0;
- }
-
- // TODO(yshui) Investigate how big the X critical section needs to be. There are
- // suggestions that rendering should be in the critical section as well.
-
- ps->redraw_needed = false;
-}
-
-static void draw_callback(EV_P_ ev_idle *w, int revents) {
- session_t *ps = session_ptr(w, draw_idle);
-
- draw_callback_impl(EV_A_ ps, revents);
-
- // Don't do painting non-stop unless we are in benchmark mode, or if
- // draw_callback_impl thinks we should continue painting.
- if (!ps->o.benchmark && !ps->redraw_needed) {
- ev_idle_stop(EV_A_ & ps->draw_idle);
- }
-}
-
-static void x_event_callback(EV_P attr_unused, ev_io *w, int revents attr_unused) {
- session_t *ps = (session_t *)w;
- xcb_generic_event_t *ev = xcb_poll_for_event(ps->c);
- if (ev) {
- ev_handle(ps, ev);
- free(ev);
- }
-}
-
-/**
- * Turn on the program reset flag.
- *
- * This will result in the compostior resetting itself after next paint.
- */
-static void reset_enable(EV_P_ ev_signal *w attr_unused, int revents attr_unused) {
- log_info("picom is resetting...");
- ev_break(EV_A_ EVBREAK_ALL);
-}
-
-static void exit_enable(EV_P attr_unused, ev_signal *w, int revents attr_unused) {
- session_t *ps = session_ptr(w, int_signal);
- log_info("picom is quitting...");
- quit(ps);
-}
-
-static void config_file_change_cb(void *_ps) {
- auto ps = (struct session *)_ps;
- reset_enable(ps->loop, NULL, 0);
-}
-
-static bool load_shader_source(session_t *ps, const char *path) {
- if (!path) {
- // Using the default shader.
- return false;
- }
-
- log_info("Loading shader source from %s", path);
-
- struct shader_info *shader = NULL;
- HASH_FIND_STR(ps->shaders, path, shader);
- if (shader) {
- log_debug("Shader already loaded, reusing");
- return false;
- }
-
- shader = ccalloc(1, struct shader_info);
- shader->key = strdup(path);
- HASH_ADD_KEYPTR(hh, ps->shaders, shader->key, strlen(shader->key), shader);
-
- FILE *f = fopen(path, "r");
- if (!f) {
- log_error("Failed to open custom shader file: %s", path);
- goto err;
- }
- struct stat statbuf;
- if (fstat(fileno(f), &statbuf) < 0) {
- log_error("Failed to access custom shader file: %s", path);
- goto err;
- }
-
- auto num_bytes = (size_t)statbuf.st_size;
- shader->source = ccalloc(num_bytes + 1, char);
- auto read_bytes = fread(shader->source, sizeof(char), num_bytes, f);
- if (read_bytes < num_bytes || ferror(f)) {
- // This is a difficult to hit error case, review thoroughly.
- log_error("Failed to read custom shader at %s. (read %lu bytes, expected "
- "%lu bytes)",
- path, read_bytes, num_bytes);
- goto err;
- }
- return false;
-err:
- HASH_DEL(ps->shaders, shader);
- if (f) {
- fclose(f);
- }
- free(shader->source);
- free(shader->key);
- free(shader);
- return true;
-}
-
-static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data) {
- return load_shader_source(data, c2_list_get_data(cond));
-}
-
-/**
- * Initialize a session.
- *
- * @param argc number of commandline arguments
- * @param argv commandline arguments
- * @param dpy the X Display
- * @param config_file the path to the config file
- * @param all_xerros whether we should report all X errors
- * @param fork whether we will fork after initialization
- */
-static session_t *session_init(int argc, char **argv, Display *dpy,
- const char *config_file, bool all_xerrors, bool fork) {
- static const session_t s_def = {
- .backend_data = NULL,
- .dpy = NULL,
- .scr = 0,
- .c = NULL,
- .vis = 0,
- .depth = 0,
- .root = XCB_NONE,
- .root_height = 0,
- .root_width = 0,
- // .root_damage = XCB_NONE,
- .overlay = XCB_NONE,
- .root_tile_fill = false,
- .root_tile_paint = PAINT_INIT,
- .tgt_picture = XCB_NONE,
- .tgt_buffer = PAINT_INIT,
- .reg_win = XCB_NONE,
-#ifdef CONFIG_OPENGL
- .glx_prog_win = GLX_PROG_MAIN_INIT,
-#endif
- .redirected = false,
- .alpha_picts = NULL,
- .fade_time = 0L,
- .animation_time = 0L,
- .ignore_head = NULL,
- .ignore_tail = NULL,
- .quit = false,
-
- .expose_rects = NULL,
- .size_expose = 0,
- .n_expose = 0,
-
- .windows = NULL,
- .active_win = NULL,
- .active_leader = XCB_NONE,
-
- .black_picture = XCB_NONE,
- .cshadow_picture = XCB_NONE,
- .white_picture = XCB_NONE,
- .shadow_context = NULL,
-
-#ifdef CONFIG_VSYNC_DRM
- .drm_fd = -1,
-#endif
-
- .xfixes_event = 0,
- .xfixes_error = 0,
- .damage_event = 0,
- .damage_error = 0,
- .render_event = 0,
- .render_error = 0,
- .composite_event = 0,
- .composite_error = 0,
- .composite_opcode = 0,
- .shape_exists = false,
- .shape_event = 0,
- .shape_error = 0,
- .randr_exists = 0,
- .randr_event = 0,
- .randr_error = 0,
- .glx_exists = false,
- .glx_event = 0,
- .glx_error = 0,
- .xrfilter_convolution_exists = false,
-
- .atoms_wintypes = {0},
- .track_atom_lst = NULL,
-
-#ifdef CONFIG_DBUS
- .dbus_data = NULL,
-#endif
- };
-
- auto stderr_logger = stderr_logger_new();
- if (stderr_logger) {
- // stderr logger might fail to create if we are already
- // daemonized.
- log_add_target_tls(stderr_logger);
- }
-
- // Allocate a session and copy default values into it
- session_t *ps = cmalloc(session_t);
- *ps = s_def;
- list_init_head(&ps->window_stack);
- ps->loop = EV_DEFAULT;
- pixman_region32_init(&ps->screen_reg);
-
- ps->ignore_tail = &ps->ignore_head;
-
- ps->o.show_all_xerrors = all_xerrors;
-
- // Use the same Display across reset, primarily for resource leak checking
- ps->dpy = dpy;
- ps->c = XGetXCBConnection(ps->dpy);
-
- const xcb_query_extension_reply_t *ext_info;
-
- ps->previous_xerror_handler = XSetErrorHandler(xerror);
-
- ps->scr = DefaultScreen(ps->dpy);
-
- auto screen = x_screen_of_display(ps->c, ps->scr);
- ps->vis = screen->root_visual;
- ps->depth = screen->root_depth;
- ps->root = screen->root;
- ps->root_width = screen->width_in_pixels;
- ps->root_height = screen->height_in_pixels;
-
- // Start listening to events on root earlier to catch all possible
- // root geometry changes
- auto e = xcb_request_check(
- ps->c, xcb_change_window_attributes_checked(
- ps->c, ps->root, XCB_CW_EVENT_MASK,
- (const uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
- XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
- XCB_EVENT_MASK_PROPERTY_CHANGE}));
- if (e) {
- log_error_x_error(e, "Failed to setup root window event mask");
- }
-
- xcb_prefetch_extension_data(ps->c, &xcb_render_id);
- xcb_prefetch_extension_data(ps->c, &xcb_composite_id);
- xcb_prefetch_extension_data(ps->c, &xcb_damage_id);
- xcb_prefetch_extension_data(ps->c, &xcb_shape_id);
- xcb_prefetch_extension_data(ps->c, &xcb_xfixes_id);
- xcb_prefetch_extension_data(ps->c, &xcb_randr_id);
- xcb_prefetch_extension_data(ps->c, &xcb_xinerama_id);
- xcb_prefetch_extension_data(ps->c, &xcb_present_id);
- xcb_prefetch_extension_data(ps->c, &xcb_sync_id);
- xcb_prefetch_extension_data(ps->c, &xcb_glx_id);
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_render_id);
- if (!ext_info || !ext_info->present) {
- log_fatal("No render extension");
- exit(1);
- }
- ps->render_event = ext_info->first_event;
- ps->render_error = ext_info->first_error;
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_composite_id);
- if (!ext_info || !ext_info->present) {
- log_fatal("No composite extension");
- exit(1);
- }
- ps->composite_opcode = ext_info->major_opcode;
- ps->composite_event = ext_info->first_event;
- ps->composite_error = ext_info->first_error;
-
- {
- xcb_composite_query_version_reply_t *reply = xcb_composite_query_version_reply(
- ps->c,
- xcb_composite_query_version(ps->c, XCB_COMPOSITE_MAJOR_VERSION,
- XCB_COMPOSITE_MINOR_VERSION),
- NULL);
-
- if (!reply || (reply->major_version == 0 && reply->minor_version < 2)) {
- log_fatal("Your X server doesn't have Composite >= 0.2 support, "
- "we cannot proceed.");
- exit(1);
- }
- free(reply);
- }
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_damage_id);
- if (!ext_info || !ext_info->present) {
- log_fatal("No damage extension");
- exit(1);
- }
- ps->damage_event = ext_info->first_event;
- ps->damage_error = ext_info->first_error;
- xcb_discard_reply(ps->c, xcb_damage_query_version(ps->c, XCB_DAMAGE_MAJOR_VERSION,
- XCB_DAMAGE_MINOR_VERSION)
- .sequence);
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_xfixes_id);
- if (!ext_info || !ext_info->present) {
- log_fatal("No XFixes extension");
- exit(1);
- }
- ps->xfixes_event = ext_info->first_event;
- ps->xfixes_error = ext_info->first_error;
- xcb_discard_reply(ps->c, xcb_xfixes_query_version(ps->c, XCB_XFIXES_MAJOR_VERSION,
- XCB_XFIXES_MINOR_VERSION)
- .sequence);
-
- ps->damaged_region = x_new_id(ps->c);
- if (!XCB_AWAIT_VOID(xcb_xfixes_create_region, ps->c, ps->damaged_region, 0, NULL)) {
- log_fatal("Failed to create a XFixes region");
- goto err;
- }
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_glx_id);
- if (ext_info && ext_info->present) {
- ps->glx_exists = true;
- ps->glx_error = ext_info->first_error;
- ps->glx_event = ext_info->first_event;
- }
-
- // Parse configuration file
- win_option_mask_t winopt_mask[NUM_WINTYPES] = {{0}};
- bool shadow_enabled = false, fading_enable = false, hasneg = false;
- char *config_file_to_free = NULL;
- config_file = config_file_to_free = parse_config(
- &ps->o, config_file, &shadow_enabled, &fading_enable, &hasneg, winopt_mask);
-
- if (IS_ERR(config_file_to_free)) {
- return NULL;
- }
-
- // Parse all of the rest command line options
- if (!get_cfg(&ps->o, argc, argv, shadow_enabled, fading_enable, hasneg, winopt_mask)) {
- log_fatal("Failed to get configuration, usually mean you have specified "
- "invalid options.");
- return NULL;
- }
-
- if (ps->o.window_shader_fg) {
- log_debug("Default window shader: \"%s\"", ps->o.window_shader_fg);
- }
-
- if (ps->o.logpath) {
- auto l = file_logger_new(ps->o.logpath);
- if (l) {
- log_info("Switching to log file: %s", ps->o.logpath);
- if (stderr_logger) {
- log_remove_target_tls(stderr_logger);
- stderr_logger = NULL;
- }
- log_add_target_tls(l);
- stderr_logger = NULL;
- } else {
- log_error("Failed to setup log file %s, I will keep using stderr",
- ps->o.logpath);
- }
- }
-
- if (strstr(argv[0], "compton")) {
- log_warn("This compositor has been renamed to \"picom\", the \"compton\" "
- "binary will not be installed in the future.");
- }
-
- ps->atoms = init_atoms(ps->c);
- ps->atoms_wintypes[WINTYPE_UNKNOWN] = 0;
-#define SET_WM_TYPE_ATOM(x) \
- ps->atoms_wintypes[WINTYPE_##x] = ps->atoms->a_NET_WM_WINDOW_TYPE_##x
- SET_WM_TYPE_ATOM(DESKTOP);
- SET_WM_TYPE_ATOM(DOCK);
- SET_WM_TYPE_ATOM(TOOLBAR);
- SET_WM_TYPE_ATOM(MENU);
- SET_WM_TYPE_ATOM(UTILITY);
- SET_WM_TYPE_ATOM(SPLASH);
- SET_WM_TYPE_ATOM(DIALOG);
- SET_WM_TYPE_ATOM(NORMAL);
- SET_WM_TYPE_ATOM(DROPDOWN_MENU);
- SET_WM_TYPE_ATOM(POPUP_MENU);
- SET_WM_TYPE_ATOM(TOOLTIP);
- SET_WM_TYPE_ATOM(NOTIFICATION);
- SET_WM_TYPE_ATOM(COMBO);
- SET_WM_TYPE_ATOM(DND);
-#undef SET_WM_TYPE_ATOM
-
- // Get needed atoms for c2 condition lists
- if (!(c2_list_postprocess(ps, ps->o.unredir_if_possible_blacklist) &&
- c2_list_postprocess(ps, ps->o.paint_blacklist) &&
- c2_list_postprocess(ps, ps->o.shadow_blacklist) &&
- c2_list_postprocess(ps, ps->o.shadow_clip_list) &&
- c2_list_postprocess(ps, ps->o.fade_blacklist) &&
- c2_list_postprocess(ps, ps->o.blur_background_blacklist) &&
- c2_list_postprocess(ps, ps->o.invert_color_list) &&
- c2_list_postprocess(ps, ps->o.window_shader_fg_rules) &&
- c2_list_postprocess(ps, ps->o.opacity_rules) &&
- c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
- c2_list_postprocess(ps, ps->o.corner_rules) &&
- c2_list_postprocess(ps, ps->o.blur_rules) &&
- c2_list_postprocess(ps, ps->o.animation_open_blacklist) &&
- c2_list_postprocess(ps, ps->o.animation_unmap_blacklist) &&
- c2_list_postprocess(ps, ps->o.active_opacity_blacklist) &&
- c2_list_postprocess(ps, ps->o.inactive_opacity_blacklist) &&
- c2_list_postprocess(ps, ps->o.focus_blacklist))) {
- log_error("Post-processing of conditionals failed, some of your rules "
- "might not work");
- }
-
- // Load shader source file specified in the shader rules
- if (c2_list_foreach(ps->o.window_shader_fg_rules, load_shader_source_for_condition, ps)) {
- log_error("Failed to load shader source file for some of the window "
- "shader rules");
- }
- if (load_shader_source(ps, ps->o.window_shader_fg)) {
- log_error("Failed to load window shader source file");
- }
-
- if (log_get_level_tls() <= LOG_LEVEL_DEBUG) {
- HASH_ITER2(ps->shaders, shader) {
- log_debug("Shader %s:", shader->key);
- log_debug("%s", shader->source);
- }
- }
-
- if (ps->o.legacy_backends) {
- ps->shadow_context =
- (void *)gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
- sum_kernel_preprocess((conv *)ps->shadow_context);
- }
-
- rebuild_shadow_exclude_reg(ps);
-
- // Query X Shape
- ext_info = xcb_get_extension_data(ps->c, &xcb_shape_id);
- if (ext_info && ext_info->present) {
- ps->shape_event = ext_info->first_event;
- ps->shape_error = ext_info->first_error;
- ps->shape_exists = true;
- }
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_randr_id);
- if (ext_info && ext_info->present) {
- ps->randr_exists = true;
- ps->randr_event = ext_info->first_event;
- ps->randr_error = ext_info->first_error;
- }
-
- ext_info = xcb_get_extension_data(ps->c, &xcb_present_id);
- if (ext_info && ext_info->present) {
- auto r = xcb_present_query_version_reply(
- ps->c,
- xcb_present_query_version(ps->c, XCB_PRESENT_MAJOR_VERSION,
- XCB_PRESENT_MINOR_VERSION),
- NULL);
- if (r) {
- ps->present_exists = true;
- free(r);
- }
- }
-
- // Query X Sync
- ext_info = xcb_get_extension_data(ps->c, &xcb_sync_id);
- if (ext_info && ext_info->present) {
- ps->xsync_error = ext_info->first_error;
- ps->xsync_event = ext_info->first_event;
- // Need X Sync 3.1 for fences
- auto r = xcb_sync_initialize_reply(
- ps->c,
- xcb_sync_initialize(ps->c, XCB_SYNC_MAJOR_VERSION, XCB_SYNC_MINOR_VERSION),
- NULL);
- if (r && (r->major_version > 3 ||
- (r->major_version == 3 && r->minor_version >= 1))) {
- ps->xsync_exists = true;
- free(r);
- }
- }
-
- ps->sync_fence = XCB_NONE;
- if (ps->xsync_exists) {
- ps->sync_fence = x_new_id(ps->c);
- e = xcb_request_check(
- ps->c, xcb_sync_create_fence(ps->c, ps->root, ps->sync_fence, 0));
- if (e) {
- if (ps->o.xrender_sync_fence) {
- log_error_x_error(e, "Failed to create a XSync fence. "
- "xrender-sync-fence will be "
- "disabled");
- ps->o.xrender_sync_fence = false;
- }
- ps->sync_fence = XCB_NONE;
- free(e);
- }
- } else if (ps->o.xrender_sync_fence) {
- log_error("XSync extension not found. No XSync fence sync is "
- "possible. (xrender-sync-fence can't be enabled)");
- ps->o.xrender_sync_fence = false;
- }
-
- // Query X RandR
- if (ps->o.xinerama_shadow_crop) {
- if (!ps->randr_exists) {
- log_fatal("No XRandR extension. xinerama-shadow-crop cannot be "
- "enabled.");
- goto err;
- }
- }
-
- // Query X Xinerama extension
- if (ps->o.xinerama_shadow_crop) {
- ext_info = xcb_get_extension_data(ps->c, &xcb_xinerama_id);
- ps->xinerama_exists = ext_info && ext_info->present;
- }
-
- rebuild_screen_reg(ps);
-
- bool compositor_running = false;
- if (session_redirection_mode(ps) == XCB_COMPOSITE_REDIRECT_MANUAL) {
- // We are running in the manual redirection mode, meaning we are running
- // as a proper compositor. So we need to register us as a compositor, etc.
-
- // We are also here when --diagnostics is set. We want to be here because
- // that gives us more diagnostic information.
-
- // Create registration window
- int ret = register_cm(ps);
- if (ret == -1) {
- exit(1);
- }
-
- compositor_running = ret == 1;
- if (compositor_running) {
- // Don't take the overlay when there is another compositor
- // running, so we don't disrupt it.
-
- // If we are printing diagnostic, we will continue a bit further
- // to get more diagnostic information, otherwise we will exit.
- if (!ps->o.print_diagnostics) {
- log_fatal("Another composite manager is already running");
- exit(1);
- }
- } else {
- if (!init_overlay(ps)) {
- goto err;
- }
- }
- } else {
- // We are here if we don't really function as a compositor, so we are not
- // taking over the screen, and we don't need to register as a compositor
-
- // If we are in debug mode, we need to create a window for rendering if
- // the backend supports presenting.
-
- // The old backends doesn't have a automatic redirection mode
- log_info("The compositor is started in automatic redirection mode.");
- assert(!ps->o.legacy_backends);
-
- if (backend_list[ps->o.backend]->present) {
- // If the backend has `present`, we couldn't be in automatic
- // redirection mode unless we are in debug mode.
- assert(ps->o.debug_mode);
- if (!init_debug_window(ps)) {
- goto err;
- }
- }
- }
-
- ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root);
- apply_driver_workarounds(ps, ps->drivers);
-
- // Initialize filters, must be preceded by OpenGL context creation
- if (ps->o.legacy_backends && !init_render(ps)) {
- log_fatal("Failed to initialize the backend");
- exit(1);
- }
-
- if (ps->o.print_diagnostics) {
- print_diagnostics(ps, config_file, compositor_running);
- free(config_file_to_free);
- exit(0);
- }
-
- ps->file_watch_handle = file_watch_init(ps->loop);
- if (ps->file_watch_handle && config_file) {
- file_watch_add(ps->file_watch_handle, config_file, config_file_change_cb, ps);
- }
-
- free(config_file_to_free);
-
- if (bkend_use_glx(ps) && ps->o.legacy_backends) {
- auto gl_logger = gl_string_marker_logger_new();
- if (gl_logger) {
- log_info("Enabling gl string marker");
- log_add_target_tls(gl_logger);
- }
- }
-
- if (!ps->o.legacy_backends) {
- if (ps->o.monitor_repaint && !backend_list[ps->o.backend]->fill) {
- log_warn("--monitor-repaint is not supported by the backend, "
- "disabling");
- ps->o.monitor_repaint = false;
- }
- }
-
- // Monitor screen changes if vsync_sw is enabled and we are using
- // an auto-detected refresh rate, or when Xinerama features are enabled
- if (ps->randr_exists && ps->o.xinerama_shadow_crop) {
- xcb_randr_select_input(ps->c, ps->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
- }
-
- cxinerama_upd_scrs(ps);
-
- {
- xcb_render_create_picture_value_list_t pa = {
- .subwindowmode = IncludeInferiors,
- };
-
- ps->root_picture = x_create_picture_with_visual_and_pixmap(
- ps->c, ps->vis, ps->root, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
- if (ps->overlay != XCB_NONE) {
- ps->tgt_picture = x_create_picture_with_visual_and_pixmap(
- ps->c, ps->vis, ps->overlay, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
- } else
- ps->tgt_picture = ps->root_picture;
- }
-
- ev_io_init(&ps->xiow, x_event_callback, ConnectionNumber(ps->dpy), EV_READ);
- ev_io_start(ps->loop, &ps->xiow);
- ev_init(&ps->unredir_timer, tmout_unredir_callback);
- ev_idle_init(&ps->draw_idle, draw_callback);
-
- ev_init(&ps->fade_timer, fade_timer_callback);
- ev_init(&ps->animation_timer, animation_timer_callback);
-
- // Set up SIGUSR1 signal handler to reset program
- ev_signal_init(&ps->usr1_signal, reset_enable, SIGUSR1);
- ev_signal_init(&ps->int_signal, exit_enable, SIGINT);
- ev_signal_start(ps->loop, &ps->usr1_signal);
- ev_signal_start(ps->loop, &ps->int_signal);
-
- // xcb can read multiple events from the socket when a request with reply is
- // made.
- //
- // Use an ev_prepare to make sure we cannot accidentally forget to handle them
- // before we go to sleep.
- //
- // If we don't drain the queue before goes to sleep (i.e. blocking on socket
- // input), we will be sleeping with events available in queue. Which might
- // cause us to block indefinitely because arrival of new events could be
- // dependent on processing of existing events (e.g. if we don't process damage
- // event and do damage subtract, new damage event won't be generated).
- //
- // So we make use of a ev_prepare handle, which is called right before libev
- // goes into sleep, to handle all the queued X events.
- ev_prepare_init(&ps->event_check, handle_queued_x_events);
- // Make sure nothing can cause xcb to read from the X socket after events are
- // handled and before we going to sleep.
- ev_set_priority(&ps->event_check, EV_MINPRI);
- ev_prepare_start(ps->loop, &ps->event_check);
-
- // Initialize DBus. We need to do this early, because add_win might call dbus
- // functions
- if (ps->o.dbus) {
-#ifdef CONFIG_DBUS
- cdbus_init(ps, DisplayString(ps->dpy));
- if (!ps->dbus_data) {
- ps->o.dbus = false;
- }
-#else
- log_fatal("DBus support not compiled in!");
- exit(1);
-#endif
- }
-
- e = xcb_request_check(ps->c, xcb_grab_server_checked(ps->c));
- if (e) {
- log_fatal_x_error(e, "Failed to grab X server");
- free(e);
- goto err;
- }
-
- ps->server_grabbed = true;
-
- // We are going to pull latest information from X server now, events sent by X
- // earlier is irrelavant at this point.
- // A better solution is probably grabbing the server from the very start. But I
- // 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, xcb_query_tree(ps->c, ps->root), NULL);
-
- e = xcb_request_check(ps->c, xcb_ungrab_server(ps->c));
- if (e) {
- log_fatal_x_error(e, "Failed to ungrab server");
- free(e);
- goto err;
- }
-
- 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++) {
- add_win_above(ps, children[i], i ? children[i - 1] : XCB_NONE);
- }
- free(query_tree_reply);
- }
-
- log_debug("Initial stack:");
- list_foreach(struct win, w, &ps->window_stack, stack_neighbour) {
- log_debug("%#010x", w->id);
- }
-
- ps->pending_updates = true;
-
- write_pid(ps);
-
- if (fork && stderr_logger) {
- // Remove the stderr logger if we will fork
- log_remove_target_tls(stderr_logger);
- }
- return ps;
-err:
- free(ps);
- return NULL;
-}
-
-/**
- * Destroy a session.
- *
- * Does not close the X connection or free the <code>session_t</code>
- * structure, though.
- *
- * @param ps session to destroy
- */
-static void session_destroy(session_t *ps) {
- if (ps->redirected) {
- unredirect(ps);
- }
-
-#ifdef CONFIG_OPENGL
- free(ps->argb_fbconfig);
- ps->argb_fbconfig = NULL;
-#endif
-
- file_watch_destroy(ps->loop, ps->file_watch_handle);
- ps->file_watch_handle = NULL;
-
- // Stop listening to events on root window
- xcb_change_window_attributes(ps->c, ps->root, XCB_CW_EVENT_MASK,
- (const uint32_t[]){0});
-
-#ifdef CONFIG_DBUS
- // Kill DBus connection
- if (ps->o.dbus) {
- assert(ps->dbus_data);
- cdbus_destroy(ps);
- }
-#endif
-
- // Free window linked list
-
- list_foreach_safe(struct win, w, &ps->window_stack, stack_neighbour) {
- if (!w->destroyed) {
- win_ev_stop(ps, w);
- HASH_DEL(ps->windows, w);
- }
-
- if (w->managed) {
- auto mw = (struct managed_win *)w;
- free_win_res(ps, mw);
- }
- free(w);
- }
- list_init_head(&ps->window_stack);
-
- // Free blacklists
- c2_list_free(&ps->o.shadow_blacklist, NULL);
- c2_list_free(&ps->o.shadow_clip_list, NULL);
- c2_list_free(&ps->o.fade_blacklist, NULL);
- c2_list_free(&ps->o.focus_blacklist, NULL);
- c2_list_free(&ps->o.invert_color_list, NULL);
- c2_list_free(&ps->o.blur_background_blacklist, NULL);
- c2_list_free(&ps->o.opacity_rules, NULL);
- c2_list_free(&ps->o.paint_blacklist, NULL);
- c2_list_free(&ps->o.unredir_if_possible_blacklist, NULL);
- c2_list_free(&ps->o.rounded_corners_blacklist, NULL);
- c2_list_free(&ps->o.corner_rules, NULL);
- c2_list_free(&ps->o.blur_rules, NULL);
- c2_list_free(&ps->o.animation_open_blacklist, NULL);
- c2_list_free(&ps->o.animation_unmap_blacklist, NULL);
- c2_list_free(&ps->o.active_opacity_blacklist, NULL);
- c2_list_free(&ps->o.inactive_opacity_blacklist, NULL);
- c2_list_free(&ps->o.window_shader_fg_rules, free);
-
- // Free tracked atom list
- {
- latom_t *next = NULL;
- for (latom_t *this = ps->track_atom_lst; this; this = next) {
- next = this->next;
- free(this);
- }
-
- ps->track_atom_lst = NULL;
- }
-
- // Free ignore linked list
- {
- ignore_t *next = NULL;
- for (ignore_t *ign = ps->ignore_head; ign; ign = next) {
- next = ign->next;
-
- free(ign);
- }
-
- // Reset head and tail
- ps->ignore_head = NULL;
- ps->ignore_tail = &ps->ignore_head;
- }
-
- // Free tgt_{buffer,picture} and root_picture
- if (ps->tgt_buffer.pict == ps->tgt_picture)
- ps->tgt_buffer.pict = XCB_NONE;
-
- if (ps->tgt_picture == ps->root_picture)
- ps->tgt_picture = XCB_NONE;
- else
- free_picture(ps->c, &ps->tgt_picture);
-
- free_picture(ps->c, &ps->root_picture);
- free_paint(ps, &ps->tgt_buffer);
-
- pixman_region32_fini(&ps->screen_reg);
- free(ps->expose_rects);
-
- free(ps->o.write_pid_path);
- free(ps->o.logpath);
- for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
- free(ps->o.blur_kerns[i]);
- }
- free(ps->o.blur_kerns);
- free(ps->o.glx_fshader_win_str);
- free_xinerama_info(ps);
-
- // Release custom window shaders
- free(ps->o.window_shader_fg);
- struct shader_info *shader, *tmp;
- HASH_ITER(hh, ps->shaders, shader, tmp) {
- HASH_DEL(ps->shaders, shader);
- assert(shader->backend_shader == NULL);
- free(shader->source);
- free(shader->key);
- free(shader);
- }
-
-#ifdef CONFIG_VSYNC_DRM
- // Close file opened for DRM VSync
- if (ps->drm_fd >= 0) {
- close(ps->drm_fd);
- ps->drm_fd = -1;
- }
-#endif
-
- // Release overlay window
- if (ps->overlay) {
- xcb_composite_release_overlay_window(ps->c, ps->overlay);
- ps->overlay = XCB_NONE;
- }
-
- if (ps->sync_fence != XCB_NONE) {
- xcb_sync_destroy_fence(ps->c, ps->sync_fence);
- ps->sync_fence = XCB_NONE;
- }
-
- // Free reg_win
- if (ps->reg_win != XCB_NONE) {
- xcb_destroy_window(ps->c, ps->reg_win);
- ps->reg_win = XCB_NONE;
- }
-
- if (ps->debug_window != XCB_NONE) {
- xcb_destroy_window(ps->c, ps->debug_window);
- ps->debug_window = XCB_NONE;
- }
-
- if (ps->damaged_region != XCB_NONE) {
- xcb_xfixes_destroy_region(ps->c, ps->damaged_region);
- ps->damaged_region = XCB_NONE;
- }
-
- if (!ps->o.legacy_backends) {
- // backend is deinitialized in unredirect()
- assert(ps->backend_data == NULL);
- } else {
- deinit_render(ps);
- }
-
-#if CONFIG_OPENGL
- if (glx_has_context(ps)) {
- // GLX context created, but not for rendering
- glx_destroy(ps);
- }
-#endif
-
- // Flush all events
- x_sync(ps->c);
- ev_io_stop(ps->loop, &ps->xiow);
- if (ps->o.legacy_backends) {
- free_conv((conv *)ps->shadow_context);
- }
- destroy_atoms(ps->atoms);
-
-#ifdef DEBUG_XRC
- // Report about resource leakage
- xrc_report_xid();
-#endif
-
- XSetErrorHandler(ps->previous_xerror_handler);
-
- // Stop libev event handlers
- ev_timer_stop(ps->loop, &ps->unredir_timer);
- ev_timer_stop(ps->loop, &ps->fade_timer);
- ev_timer_stop(ps->loop, &ps->animation_timer);
- ev_idle_stop(ps->loop, &ps->draw_idle);
- ev_prepare_stop(ps->loop, &ps->event_check);
- ev_signal_stop(ps->loop, &ps->usr1_signal);
- ev_signal_stop(ps->loop, &ps->int_signal);
-}
-
-/**
- * Do the actual work.
- *
- * @param ps current session
- */
-static void session_run(session_t *ps) {
- // In benchmark mode, we want draw_idle handler to always be active
- if (ps->o.benchmark) {
- ev_idle_start(ps->loop, &ps->draw_idle);
- } else {
- // Let's draw our first frame!
- queue_redraw(ps);
- }
- ev_run(ps->loop, 0);
-}
-
-/**
- * The function that everybody knows.
- */
-int main(int argc, char **argv) {
- // Set locale so window names with special characters are interpreted
- // correctly
- setlocale(LC_ALL, "");
-
- // Initialize logging system for early logging
- log_init_tls();
-
- {
- auto stderr_logger = stderr_logger_new();
- if (stderr_logger) {
- log_add_target_tls(stderr_logger);
- }
- }
-
- int exit_code;
- char *config_file = NULL;
- bool all_xerrors = false, need_fork = false;
- if (get_early_config(argc, argv, &config_file, &all_xerrors, &need_fork, &exit_code)) {
- return exit_code;
- }
-
- int pfds[2];
- if (need_fork) {
- if (pipe2(pfds, O_CLOEXEC)) {
- perror("pipe2");
- return 1;
- }
- auto pid = fork();
- if (pid < 0) {
- perror("fork");
- return 1;
- }
- if (pid > 0) {
- // We are the parent
- close(pfds[1]);
- // We wait for the child to tell us it has finished initialization
- // by sending us something via the pipe.
- int tmp;
- if (read(pfds[0], &tmp, sizeof tmp) <= 0) {
- // Failed to read, the child has most likely died
- // We can probably waitpid() here.
- return 1;
- } else {
- // We are done
- return 0;
- }
- }
- // We are the child
- close(pfds[0]);
- }
-
- // Main loop
- bool quit = false;
- int ret_code = 0;
- char *pid_file = NULL;
-
- do {
- Display *dpy = XOpenDisplay(NULL);
- if (!dpy) {
- log_fatal("Can't open display.");
- ret_code = 1;
- break;
- }
- XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
-
- // Reinit logging system so we don't get leftovers from previous sessions
- // or early logging.
- log_deinit_tls();
- log_init_tls();
-
- ps_g = session_init(argc, argv, dpy, config_file, all_xerrors, need_fork);
- if (!ps_g) {
- log_fatal("Failed to create new session.");
- ret_code = 1;
- break;
- }
- if (need_fork) {
- // Finishing up daemonization
- // Close files
- if (fclose(stdout) || fclose(stderr) || fclose(stdin)) {
- log_fatal("Failed to close standard input/output");
- ret_code = 1;
- break;
- }
- // Make us the session and process group leader so we don't get
- // killed when our parent die.
- setsid();
- // Notify the parent that we are done. This might cause the parent
- // to quit, so only do this after setsid()
- int tmp = 1;
- write(pfds[1], &tmp, sizeof tmp);
- close(pfds[1]);
- // We only do this once
- need_fork = false;
- }
- session_run(ps_g);
- quit = ps_g->quit;
- if (quit && ps_g->o.write_pid_path) {
- pid_file = strdup(ps_g->o.write_pid_path);
- }
- session_destroy(ps_g);
- free(ps_g);
- ps_g = NULL;
- if (dpy) {
- XCloseDisplay(dpy);
- }
- } while (!quit);
-
- free(config_file);
- if (pid_file) {
- log_trace("remove pid file %s", pid_file);
- unlink(pid_file);
- free(pid_file);
- }
-
- log_deinit_tls();
-
- return ret_code;
-}