diff options
| author | allusive-dev <[email protected]> | 2023-11-15 15:13:38 +1100 |
|---|---|---|
| committer | allusive-dev <[email protected]> | 2023-11-15 15:13:38 +1100 |
| commit | f481ea09f9f69c96575662d7b67d290f380aee83 (patch) | |
| tree | 9155652619c26d1a028ddc63eb7a0c29094dd0d3 /src/picom.c | |
| parent | Update README.md (diff) | |
| download | compfy-f481ea09f9f69c96575662d7b67d290f380aee83.tar.xz compfy-f481ea09f9f69c96575662d7b67d290f380aee83.zip | |
merge with compfy again lol1.6.0
Diffstat (limited to 'src/picom.c')
| -rw-r--r-- | src/picom.c | 2832 |
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; -} |