aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/atom.c37
-rw-r--r--src/atom.h68
-rw-r--r--src/backend/backend.c494
-rw-r--r--src/backend/backend.h290
-rw-r--r--src/backend/backend_common.c480
-rw-r--r--src/backend/backend_common.h78
-rw-r--r--src/backend/driver.c82
-rw-r--r--src/backend/driver.h62
-rw-r--r--src/backend/dummy/dummy.c174
-rw-r--r--src/backend/gl/gl_common.c1922
-rw-r--r--src/backend/gl/gl_common.h220
-rw-r--r--src/backend/gl/glx.c648
-rw-r--r--src/backend/gl/glx.h77
-rw-r--r--src/backend/meson.build7
-rw-r--r--src/backend/xrender/xrender.c779
-rw-r--r--src/c2.c1674
-rw-r--r--src/c2.h26
-rw-r--r--src/cache.c95
-rw-r--r--src/cache.h32
-rw-r--r--src/common.h541
-rw-r--r--src/compiler.h124
-rw-r--r--src/config.c650
-rw-r--r--src/config.h380
-rw-r--r--src/config_libconfig.c784
-rw-r--r--src/dbus.c1340
-rw-r--r--src/dbus.h54
-rw-r--r--src/diagnostic.c53
-rw-r--r--src/diagnostic.h9
-rw-r--r--src/err.h37
-rw-r--r--src/event.c757
-rw-r--r--src/event.h8
-rw-r--r--src/file_watch.c188
-rw-r--r--src/file_watch.h10
-rw-r--r--src/kernel.c160
-rw-r--r--src/kernel.h39
-rw-r--r--src/list.h108
-rw-r--r--src/log.c376
-rw-r--r--src/log.h96
-rw-r--r--src/meson.build97
-rw-r--r--src/meta.h75
-rw-r--r--src/opengl.c1514
-rw-r--r--src/opengl.h246
-rw-r--r--src/options.c1128
-rw-r--r--src/options.h37
-rw-r--r--src/picom.c2790
-rw-r--r--src/picom.h117
-rw-r--r--src/picom.modulemap214
-rw-r--r--src/region.h100
-rw-r--r--src/render.c1500
-rw-r--r--src/render.h50
-rw-r--r--src/string_utils.c129
-rw-r--r--src/string_utils.h54
-rw-r--r--src/types.h32
-rw-r--r--src/uthash_extra.h7
-rw-r--r--src/utils.c51
-rw-r--r--src/utils.h294
-rw-r--r--src/vsync.c204
-rw-r--r--src/vsync.h7
-rw-r--r--src/win.c3116
-rw-r--r--src/win.h531
-rw-r--r--src/win_defs.h102
-rw-r--r--src/x.c748
-rw-r--r--src/x.h293
-rw-r--r--src/xrescheck.c65
-rw-r--r--src/xrescheck.h62
65 files changed, 0 insertions, 26522 deletions
diff --git a/src/atom.c b/src/atom.c
deleted file mode 100644
index 0272dc8..0000000
--- a/src/atom.c
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <string.h>
-#include <xcb/xcb.h>
-
-#include "atom.h"
-#include "common.h"
-#include "utils.h"
-#include "log.h"
-
-static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
- xcb_connection_t *c = ud;
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
- c, xcb_intern_atom(c, 0, to_u16_checked(strlen(atom_name)), atom_name), NULL);
-
- xcb_atom_t atom = XCB_NONE;
- if (reply) {
- log_debug("Atom %s is %d", atom_name, reply->atom);
- atom = reply->atom;
- free(reply);
- } else {
- log_error("Failed to intern atoms");
- *err = 1;
- }
- return (void *)(intptr_t)atom;
-}
-
-/**
- * Create a new atom structure and fetch all predefined atoms
- */
-struct atom *init_atoms(xcb_connection_t *c) {
- auto atoms = ccalloc(1, struct atom);
- atoms->c = new_cache((void *)c, atom_getter, NULL);
-#define ATOM_GET(x) atoms->a##x = (xcb_atom_t)(intptr_t)cache_get(atoms->c, #x, NULL)
- LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
- LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
-#undef ATOM_GET
- return atoms;
-}
diff --git a/src/atom.h b/src/atom.h
deleted file mode 100644
index baf3360..0000000
--- a/src/atom.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-#include <stdlib.h>
-
-#include <xcb/xcb.h>
-
-#include "meta.h"
-#include "cache.h"
-
-// clang-format off
-// Splitted into 2 lists because of the limitation of our macros
-#define ATOM_LIST1 \
- _NET_WM_WINDOW_OPACITY, \
- _NET_FRAME_EXTENTS, \
- WM_STATE, \
- _NET_WM_NAME, \
- _NET_WM_PID, \
- WM_NAME, \
- WM_CLASS, \
- WM_ICON_NAME, \
- WM_TRANSIENT_FOR, \
- WM_WINDOW_ROLE, \
- WM_CLIENT_LEADER, \
- WM_CLIENT_MACHINE, \
- _NET_ACTIVE_WINDOW, \
- _COMPTON_SHADOW, \
- _NET_WM_WINDOW_TYPE, \
- _NET_CURRENT_DESKTOP
-
-#define ATOM_LIST2 \
- _NET_WM_WINDOW_TYPE_DESKTOP, \
- _NET_WM_WINDOW_TYPE_DOCK, \
- _NET_WM_WINDOW_TYPE_TOOLBAR, \
- _NET_WM_WINDOW_TYPE_MENU, \
- _NET_WM_WINDOW_TYPE_UTILITY, \
- _NET_WM_WINDOW_TYPE_SPLASH, \
- _NET_WM_WINDOW_TYPE_DIALOG, \
- _NET_WM_WINDOW_TYPE_NORMAL, \
- _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, \
- _NET_WM_WINDOW_TYPE_POPUP_MENU, \
- _NET_WM_WINDOW_TYPE_TOOLTIP, \
- _NET_WM_WINDOW_TYPE_NOTIFICATION, \
- _NET_WM_WINDOW_TYPE_COMBO, \
- _NET_WM_WINDOW_TYPE_DND, \
- _NET_WM_STATE, \
- _NET_WM_STATE_FULLSCREEN, \
- _NET_WM_BYPASS_COMPOSITOR, \
- UTF8_STRING, \
- C_STRING
-// clang-format on
-
-#define ATOM_DEF(x) xcb_atom_t a##x
-
-struct atom {
- struct cache *c;
- LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1);
- LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2);
-};
-
-struct atom *init_atoms(xcb_connection_t *);
-
-static inline xcb_atom_t get_atom(struct atom *a, const char *key) {
- return (xcb_atom_t)(intptr_t)cache_get(a->c, key, NULL);
-}
-
-static inline void destroy_atoms(struct atom *a) {
- cache_free(a->c);
- free(a);
-}
diff --git a/src/backend/backend.c b/src/backend/backend.c
deleted file mode 100644
index b0e562a..0000000
--- a/src/backend/backend.c
+++ /dev/null
@@ -1,494 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#include <xcb/sync.h>
-#include <xcb/xcb.h>
-
-#include "backend/backend.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h"
-#include "region.h"
-#include "types.h"
-#include "win.h"
-#include "x.h"
-
-extern struct backend_operations xrender_ops, dummy_ops;
-#ifdef CONFIG_OPENGL
-extern struct backend_operations glx_ops;
-#endif
-
-struct backend_operations *backend_list[NUM_BKEND] = {
- [BKEND_XRENDER] = &xrender_ops,
- [BKEND_DUMMY] = &dummy_ops,
-#ifdef CONFIG_OPENGL
- [BKEND_GLX] = &glx_ops,
-#endif
-};
-
-/**
- * @param all_damage if true ignore damage and repaint the whole screen
- */
-region_t get_damage(session_t *ps, bool all_damage) {
- region_t region;
- auto buffer_age_fn = ps->backend_data->ops->buffer_age;
- int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data) : -1;
-
- if (all_damage) {
- buffer_age = -1;
- }
-
- pixman_region32_init(&region);
- if (buffer_age == -1 || buffer_age > ps->ndamage) {
- pixman_region32_copy(&region, &ps->screen_reg);
- } else {
- for (int i = 0; i < buffer_age; i++) {
- auto curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage;
- log_trace("damage index: %d, damage ring offset: %ld", i, curr);
- dump_region(&ps->damage_ring[curr]);
- pixman_region32_union(&region, &region, &ps->damage_ring[curr]);
- }
- pixman_region32_intersect(&region, &region, &ps->screen_reg);
- }
- return region;
-}
-
-static void process_window_for_painting(session_t *ps, struct managed_win* w, void* win_image,
- double additional_alpha,
- region_t* reg_bound, region_t* reg_visible,
- region_t* reg_paint, region_t* reg_paint_in_bound) {
- // For window image processing, we don't have to limit the process
- // region to damage for correctness. (see <damager-note> for
- // details)
-
- // The visible region, in window local coordinates Although we
- // don't limit process region to damage, we provide that info in
- // reg_visible as a hint. Since window image data outside of the
- // damage region won't be painted onto target
- region_t reg_visible_local;
- {
- // The bounding shape, in window local coordinates
- region_t reg_bound_local;
- pixman_region32_init(&reg_bound_local);
- pixman_region32_copy(&reg_bound_local, reg_bound);
- pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
-
- pixman_region32_init(&reg_visible_local);
- pixman_region32_intersect(&reg_visible_local,
- reg_visible, reg_paint);
- pixman_region32_translate(&reg_visible_local, -w->g.x,
- -w->g.y);
- // Data outside of the bounding shape won't be visible,
- // but it is not necessary to limit the image operations
- // to the bounding shape yet. So pass that as the visible
- // region, not the clip region.
- pixman_region32_intersect(
- &reg_visible_local, &reg_visible_local, &reg_bound_local);
- pixman_region32_fini(&reg_bound_local);
- }
-
- auto new_img = ps->backend_data->ops->clone_image(
- ps->backend_data, win_image, &reg_visible_local);
- auto reg_frame = win_get_region_frame_local_by_val(w);
- double alpha = additional_alpha*w->opacity;
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_OPACITY, new_img, &alpha);
- ps->backend_data->ops->image_op(
- ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
- &reg_visible_local, (double[]){w->frame_opacity});
- pixman_region32_fini(&reg_frame);
- ps->backend_data->ops->compose(ps->backend_data, new_img,
- w->g.x, w->g.y,
- w->g.x + w->widthb, w->g.y + w->heightb,
- reg_paint_in_bound, reg_visible);
- ps->backend_data->ops->release_image(ps->backend_data, new_img);
- pixman_region32_fini(&reg_visible_local);
-}
-
-/// paint all windows
-void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
- if (ps->o.xrender_sync_fence) {
- if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
- log_error("x_fence_sync failed, xrender-sync-fence will be "
- "disabled from now on.");
- xcb_sync_destroy_fence(ps->c, ps->sync_fence);
- ps->sync_fence = XCB_NONE;
- ps->o.xrender_sync_fence = false;
- ps->xsync_exists = false;
- }
- }
- // All painting will be limited to the damage, if _some_ of
- // the paints bleed out of the damage region, it will destroy
- // part of the image we want to reuse
- region_t reg_damage;
- if (!ignore_damage) {
- reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage);
- } else {
- pixman_region32_init(&reg_damage);
- pixman_region32_copy(&reg_damage, &ps->screen_reg);
- }
-
- if (!pixman_region32_not_empty(&reg_damage)) {
- pixman_region32_fini(&reg_damage);
- return;
- }
-
-#ifdef DEBUG_REPAINT
- static struct timespec last_paint = {0};
-#endif
-
- // <damage-note>
- // If use_damage is enabled, we MUST make sure only the damaged regions of the
- // screen are ever touched by the compositor. The reason is that at the beginning
- // of each render, we clear the damaged regions with the wallpaper, and nothing
- // else. If later during the render we changed anything outside the damaged
- // region, that won't be cleared by the next render, and will thus accumulate.
- // (e.g. if shadow is drawn outside the damaged region, it will become thicker and
- // thicker over time.)
-
- /// The adjusted damaged regions
- region_t reg_paint;
- assert(ps->o.blur_method != BLUR_METHOD_INVALID);
- if (ps->o.blur_method != BLUR_METHOD_NONE && ps->backend_data->ops->get_blur_size) {
- int blur_width, blur_height;
- ps->backend_data->ops->get_blur_size(ps->backend_blur_context,
- &blur_width, &blur_height);
-
- // The region of screen a given window influences will be smeared
- // out by blur. With more windows on top of the given window, the
- // influences region will be smeared out more.
- //
- // Also, blurring requires data slightly outside the area that needs
- // to be blurred. The more semi-transparent windows are stacked on top
- // of each other, the larger the area will be.
- //
- // Instead of accurately calculate how much bigger the damage
- // region will be because of blur, we assume the worst case here.
- // That is, the damaged window is at the bottom of the stack, and
- // all other windows have semi-transparent background
- int resize_factor = 1;
- if (t) {
- resize_factor = t->stacking_rank;
- }
- resize_region_in_place(&reg_damage, blur_width * resize_factor,
- blur_height * resize_factor);
- reg_paint = resize_region(&reg_damage, blur_width * resize_factor,
- blur_height * resize_factor);
- pixman_region32_intersect(&reg_paint, &reg_paint, &ps->screen_reg);
- pixman_region32_intersect(&reg_damage, &reg_damage, &ps->screen_reg);
- } else {
- pixman_region32_init(&reg_paint);
- pixman_region32_copy(&reg_paint, &reg_damage);
- }
-
- // A hint to backend, the region that will be visible on screen
- // backend can optimize based on this info
- region_t reg_visible;
- pixman_region32_init(&reg_visible);
- pixman_region32_copy(&reg_visible, &ps->screen_reg);
- if (t && !ps->o.transparent_clipping) {
- // Calculate the region upon which the root window (wallpaper) is to be
- // painted based on the ignore region of the lowest window, if available
- //
- // NOTE If transparent_clipping is enabled, transparent windows are
- // included in the reg_ignore, but we still want to have the wallpaper
- // beneath them, so we don't use reg_ignore for wallpaper in that case.
- pixman_region32_subtract(&reg_visible, &reg_visible, t->reg_ignore);
- }
-
- // Region on screen we don't want any shadows on
- region_t reg_shadow_clip;
- pixman_region32_init(&reg_shadow_clip);
-
- if (ps->backend_data->ops->prepare) {
- ps->backend_data->ops->prepare(ps->backend_data, &reg_paint);
- }
-
- if (ps->root_image) {
- ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
- 0, 0, ps->root_width, ps->root_height,
- &reg_paint, &reg_visible);
- } else {
- ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
- &reg_paint);
- }
-
- // Windows are sorted from bottom to top
- // Each window has a reg_ignore, which is the region obscured by all the windows
- // on top of that window. This is used to reduce the number of pixels painted.
- //
- // Whether this is beneficial is to be determined XXX
- for (auto w = t; w; w = w->prev_trans) {
- pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore);
- assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR));
- assert(!(w->flags & WIN_FLAGS_PIXMAP_STALE));
- assert(!(w->flags & WIN_FLAGS_PIXMAP_NONE));
-
- // The bounding shape of the window, in global/target coordinates
- // reminder: bounding shape contains the WM frame
- auto reg_bound = win_get_bounding_shape_global_by_val(w);
-
- // The clip region for the current window, in global/target coordinates
- // reg_paint_in_bound \in reg_paint
- region_t reg_paint_in_bound;
- pixman_region32_init(&reg_paint_in_bound);
- pixman_region32_intersect(&reg_paint_in_bound, &reg_bound, &reg_paint);
- if (ps->o.transparent_clipping) {
- // <transparent-clipping-note>
- // If transparent_clipping is enabled, we need to be SURE that
- // things are not drawn inside reg_ignore, because otherwise they
- // will appear underneath transparent windows.
- // So here we have make sure reg_paint_in_bound \in reg_visible
- // There are a few other places below where this is needed as
- // well.
- pixman_region32_intersect(&reg_paint_in_bound,
- &reg_paint_in_bound, &reg_visible);
- }
-
- // Blur window background
- /* TODO(yshui) since the backend might change the content of the window
- * (e.g. with shaders), we should consult the backend whether the window
- * is transparent or not. for now we will just rely on the force_win_blend
- * option */
- auto real_win_mode = w->mode;
-
- if (w->blur_background &&
- (ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
- (ps->o.blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) {
- // Minimize the region we try to blur, if the window
- // itself is not opaque, only the frame is.
-
- double blur_opacity = 1;
- if (w->opacity < (1.0 / MAX_ALPHA)) {
- // Hide blur for fully transparent windows.
- blur_opacity = 0;
- } else if (w->state == WSTATE_MAPPING) {
- // Gradually increase the blur intensity during
- // fading in.
- assert(w->opacity <= w->opacity_target);
- blur_opacity = w->opacity / w->opacity_target;
- } else if (w->state == WSTATE_UNMAPPING ||
- w->state == WSTATE_DESTROYING) {
- // Gradually decrease the blur intensity during
- // fading out.
- assert(w->opacity <= w->opacity_target_old);
- blur_opacity = w->opacity / w->opacity_target_old;
- } else if (w->state == WSTATE_FADING) {
- if (w->opacity < w->opacity_target &&
- w->opacity_target_old < (1.0 / MAX_ALPHA)) {
- // Gradually increase the blur intensity during
- // fading in.
- assert(w->opacity <= w->opacity_target);
- blur_opacity = w->opacity / w->opacity_target;
- } else if (w->opacity > w->opacity_target &&
- w->opacity_target < (1.0 / MAX_ALPHA)) {
- // Gradually decrease the blur intensity during
- // fading out.
- assert(w->opacity <= w->opacity_target_old);
- blur_opacity = w->opacity / w->opacity_target_old;
- }
- }
- assert(blur_opacity >= 0 && blur_opacity <= 1);
-
- if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
- // We need to blur the bounding shape of the window
- // (reg_paint_in_bound = reg_bound \cap reg_paint)
- ps->backend_data->ops->blur(
- ps->backend_data, blur_opacity, ps->backend_blur_context,
- &reg_paint_in_bound, &reg_visible);
- } else {
- // Window itself is solid, we only need to blur the frame
- // region
-
- // Readability assertions
- assert(ps->o.blur_background_frame);
- assert(real_win_mode == WMODE_FRAME_TRANS);
-
- auto reg_blur = win_get_region_frame_local_by_val(w);
- pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
- // make sure reg_blur \in reg_paint
- pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint);
- if (ps->o.transparent_clipping) {
- // ref: <transparent-clipping-note>
- pixman_region32_intersect(&reg_blur, &reg_blur,
- &reg_visible);
- }
- ps->backend_data->ops->blur(ps->backend_data, blur_opacity,
- ps->backend_blur_context,
- &reg_blur, &reg_visible);
- pixman_region32_fini(&reg_blur);
- }
- }
-
- // Draw shadow on target
- if (w->shadow) {
- assert(!(w->flags & WIN_FLAGS_SHADOW_NONE));
- // Clip region for the shadow
- // reg_shadow \in reg_paint
- auto reg_shadow = win_extents_by_val(w);
- pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_paint);
- if (!ps->o.wintype_option[w->window_type].full_shadow) {
- pixman_region32_subtract(&reg_shadow, &reg_shadow, &reg_bound);
- }
-
- // Mask out the region we don't want shadow on
- if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
- pixman_region32_subtract(&reg_shadow, &reg_shadow,
- &ps->shadow_exclude_reg);
- }
- if (pixman_region32_not_empty(&reg_shadow_clip)) {
- pixman_region32_subtract(&reg_shadow, &reg_shadow,
- &reg_shadow_clip);
- }
-
- if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
- w->xinerama_scr < ps->xinerama_nscrs) {
- // There can be a window where number of screens is
- // updated, but the screen number attached to the windows
- // have not.
- //
- // Window screen number will be updated eventually, so
- // here we just check to make sure we don't access out of
- // bounds.
- pixman_region32_intersect(
- &reg_shadow, &reg_shadow,
- &ps->xinerama_scr_regs[w->xinerama_scr]);
- }
-
- if (ps->o.transparent_clipping) {
- // ref: <transparent-clipping-note>
- pixman_region32_intersect(&reg_shadow, &reg_shadow,
- &reg_visible);
- }
-
- assert(w->shadow_image);
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image,
- &w->opacity);
- ps->backend_data->ops->compose(
- ps->backend_data, w->shadow_image,
- w->g.x + w->shadow_dx, w->g.y + w->shadow_dy,
- w->g.x + w->shadow_dx + w->shadow_width,
- w->g.y + w->shadow_dy + w->shadow_height,
- &reg_shadow, &reg_visible);
- pixman_region32_fini(&reg_shadow);
- }
-
- // Update image properties
- {
- double dim_opacity = 0.0;
- if (w->dim) {
- dim_opacity = ps->o.inactive_dim;
- if (!ps->o.inactive_dim_fixed) {
- dim_opacity *= w->opacity;
- }
- }
-
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image,
- &ps->o.max_brightness);
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_INVERTED, w->win_image,
- &w->invert_color);
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, w->win_image,
- &dim_opacity);
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, &w->opacity);
- }
-
- if (w->opacity * MAX_ALPHA < 1) {
- // We don't need to paint the window body itself if it's
- // completely transparent.
- goto skip;
- }
-
- if (w->clip_shadow_above) {
- // Add window bounds to shadow-clip region
- pixman_region32_union(&reg_shadow_clip, &reg_shadow_clip, &reg_bound);
- } else {
- // Remove overlapping window bounds from shadow-clip region
- pixman_region32_subtract(&reg_shadow_clip, &reg_shadow_clip, &reg_bound);
- }
-
- // Draw window on target
- bool is_animating = 0 <= w->animation_progress && w->animation_progress < 1.0;
- if (w->frame_opacity == 1 && !is_animating) {
- ps->backend_data->ops->compose(ps->backend_data, w->win_image,
- w->g.x, w->g.y,
- w->g.x + w->widthb, w->g.y + w->heightb,
- &reg_paint_in_bound, &reg_visible);
- } else {
- if (is_animating && w->old_win_image) {
- assert(w->old_win_image);
-
- bool resizing =
- w->g.width != w->pending_g.width ||
- w->g.height != w->pending_g.height;
-
- // Only animate opacity here if we are resizing
- // a transparent window
- process_window_for_painting(ps, w, w->win_image,
- 1,
- &reg_bound, &reg_visible,
- &reg_paint, &reg_paint_in_bound);
-
- // Only do this if size changes as otherwise moving
- // transparent windows will flicker and if you just
- // move so slightly they will keep flickering
- if (resizing) {
- process_window_for_painting(ps, w, w->old_win_image,
- 1.0 - w->animation_progress,
- &reg_bound, &reg_visible,
- &reg_paint, &reg_paint_in_bound);
- }
-
- } else {
- process_window_for_painting(ps, w, w->win_image,
- 1,
- &reg_bound, &reg_visible,
- &reg_paint, &reg_paint_in_bound);
- }
- }
- skip:
- pixman_region32_fini(&reg_bound);
- pixman_region32_fini(&reg_paint_in_bound);
- }
- pixman_region32_fini(&reg_paint);
- pixman_region32_fini(&reg_shadow_clip);
-
- if (ps->o.monitor_repaint) {
- const struct color DEBUG_COLOR = {0.5, 0, 0, 0.5};
- auto reg_damage_debug = get_damage(ps, false);
- ps->backend_data->ops->fill(ps->backend_data, DEBUG_COLOR, &reg_damage_debug);
- pixman_region32_fini(&reg_damage_debug);
- }
-
- // Move the head of the damage ring
- ps->damage = ps->damage - 1;
- if (ps->damage < ps->damage_ring) {
- ps->damage = ps->damage_ring + ps->ndamage - 1;
- }
- pixman_region32_clear(ps->damage);
-
- if (ps->backend_data->ops->present) {
- // Present the rendered scene
- // Vsync is done here
- ps->backend_data->ops->present(ps->backend_data, &reg_damage);
- }
-
- pixman_region32_fini(&reg_damage);
-
-#ifdef DEBUG_REPAINT
- struct timespec now = get_time_timespec();
- struct timespec diff = {0};
- timespec_subtract(&diff, &now, &last_paint);
- log_trace("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec);
- last_paint = now;
- log_trace("paint:");
- for (win *w = t; w; w = w->prev_trans)
- log_trace(" %#010lx", w->id);
-#endif
-}
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/backend/backend.h b/src/backend/backend.h
deleted file mode 100644
index ae107d3..0000000
--- a/src/backend/backend.h
+++ /dev/null
@@ -1,290 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018, Yuxuan Shui <[email protected]>
-
-#pragma once
-
-#include <stdbool.h>
-
-#include "compiler.h"
-#include "config.h"
-#include "driver.h"
-#include "kernel.h"
-#include "region.h"
-#include "types.h"
-#include "x.h"
-
-typedef struct session session_t;
-struct managed_win;
-
-struct ev_loop;
-struct backend_operations;
-
-typedef struct backend_base {
- struct backend_operations *ops;
- xcb_connection_t *c;
- xcb_window_t root;
- struct ev_loop *loop;
-
- /// Whether the backend can accept new render request at the moment
- bool busy;
- // ...
-} backend_t;
-
-typedef void (*backend_ready_callback_t)(void *);
-
-// When image properties are actually applied to the image, they are applied in a
-// particular order:
-//
-// Color inversion -> Dimming -> Opacity multiply -> Limit maximum brightness
-enum image_properties {
- // Whether the color of the image is inverted
- // 1 boolean, default: false
- IMAGE_PROPERTY_INVERTED,
- // How much the image is dimmed
- // 1 double, default: 0
- IMAGE_PROPERTY_DIM_LEVEL,
- // Image opacity, i.e. an alpha value multiplied to the alpha channel
- // 1 double, default: 1
- IMAGE_PROPERTY_OPACITY,
- // The effective size of the image, the image will be tiled to fit.
- // 2 int, default: the actual size of the image
- IMAGE_PROPERTY_EFFECTIVE_SIZE,
- // Limit how bright image can be. The image brightness is estimated by averaging
- // the pixels in the image, and dimming will be applied to scale the average
- // brightness down to the max brightness value.
- // 1 double, default: 1
- IMAGE_PROPERTY_MAX_BRIGHTNESS,
-};
-
-enum image_operations {
- // Multiply the alpha channel by the argument
- IMAGE_OP_APPLY_ALPHA,
-};
-
-struct gaussian_blur_args {
- int size;
- double deviation;
-};
-
-struct box_blur_args {
- int size;
-};
-
-struct kernel_blur_args {
- struct conv **kernels;
- int kernel_count;
-};
-
-struct dual_kawase_blur_args {
- int size;
- int strength;
-};
-
-struct backend_operations {
- // =========== Initialization ===========
-
- /// Initialize the backend, prepare for rendering to the target window.
- /// Here is how you should choose target window:
- /// 1) if ps->overlay is not XCB_NONE, use that
- /// 2) use ps->root otherwise
- // TODO(yshui) make the target window a parameter
- backend_t *(*init)(session_t *)attr_nonnull(1);
- void (*deinit)(backend_t *backend_data) attr_nonnull(1);
-
- /// Called when rendering will be stopped for an unknown amount of
- /// time (e.g. when screen is unredirected). Free some resources.
- ///
- /// Optional, not yet used
- void (*pause)(backend_t *backend_data, session_t *ps);
-
- /// Called before rendering is resumed
- ///
- /// Optional, not yet used
- void (*resume)(backend_t *backend_data, session_t *ps);
-
- /// Called when root property changed, returns the new
- /// backend_data. Even if the backend_data changed, all
- /// the existing image data returned by this backend should
- /// remain valid.
- ///
- /// Optional
- void *(*root_change)(backend_t *backend_data, session_t *ps);
-
- // =========== Rendering ============
-
- // NOTE: general idea about reg_paint/reg_op vs reg_visible is that reg_visible is
- // merely a hint. Ignoring reg_visible entirely don't affect the correctness of
- // the operation performed. OTOH reg_paint/reg_op is part of the parameters of the
- // operation, and must be honored in order to complete the operation correctly.
-
- // NOTE: due to complications introduced by use-damage and blur, the rendering API
- // is a bit weird. The idea is, `compose` and `blur` have to update a temporary
- // buffer, because `blur` requires data from an area slightly larger than the area
- // that will be visible. So the area outside the visible area has to be rendered,
- // but we have to discard the result (because the result of blurring that area
- // will be wrong). That's why we cannot render into the back buffer directly.
- // After rendering is done, `present` is called to update a portion of the actual
- // back buffer, then present it to the target (or update the target directly,
- // if not back buffered).
-
- /// Called before when a new frame starts.
- ///
- /// Optional
- void (*prepare)(backend_t *backend_data, const region_t *reg_damage);
-
- /**
- * Paint the content of an image onto the rendering buffer
- *
- * @param backend_data the backend data
- * @param image_data the image to paint
- * @param dst_x1, dst_y1 the top left corner of the image in the target
- * @param dst_x2, dst_y2 the top right corner of the image in the target
- * @param reg_paint the clip region, in target coordinates
- * @param reg_visible the visible region, in target coordinates
- */
- void (*compose)(backend_t *backend_data, void *image_data,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_paint, const region_t *reg_visible);
-
- /// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
- void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
-
- /// Blur a given region of the rendering buffer.
- bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx,
- const region_t *reg_blur, const region_t *reg_visible)
- attr_nonnull(1, 3, 4, 5);
-
- /// Update part of the back buffer with the rendering buffer, then present the
- /// back buffer onto the target window (if not back buffered, update part of the
- /// target window directly).
- ///
- /// Optional, if NULL, indicates the backend doesn't have render output
- ///
- /// @param region part of the target that should be updated
- void (*present)(backend_t *backend_data, const region_t *region) attr_nonnull(1, 2);
-
- /**
- * Bind a X pixmap to the backend's internal image data structure.
- *
- * @param backend_data backend data
- * @param pixmap X pixmap to bind
- * @param fmt information of the pixmap's visual
- * @param owned whether the ownership of the pixmap is transfered to the backend
- * @return backend internal data structure bound with this pixmap
- */
- void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
- struct xvisual_info fmt, bool owned);
-
- /// Create a shadow image based on the parameters
- /// Default implementation: default_backend_render_shadow
- void *(*render_shadow)(backend_t *backend_data, int width, int height,
- const conv *kernel, double r, double g, double b, double a);
-
- // ============ Resource management ===========
-
- /// Free resources associated with an image data structure
- void (*release_image)(backend_t *backend_data, void *img_data) attr_nonnull(1, 2);
-
- // =========== Query ===========
-
- /// Return if image is not completely opaque.
- ///
- /// This function is needed because some backend might change the content of the
- /// window (e.g. when using a custom shader with the glx backend), so only the
- /// backend knows if an image is transparent.
- bool (*is_image_transparent)(backend_t *backend_data, void *image_data)
- attr_nonnull(1, 2);
-
- /// Get the age of the buffer content we are currently rendering ontop
- /// of. The buffer that has just been `present`ed has a buffer age of 1.
- /// Everytime `present` is called, buffers get older. Return -1 if the
- /// buffer is empty.
- ///
- /// Optional
- int (*buffer_age)(backend_t *backend_data);
-
- /// The maximum number buffer_age might return.
- int max_buffer_age;
-
- // =========== Post-processing ============
-
- /* TODO(yshui) Consider preserving the order of image ops.
- * Currently in both backends, the image ops are applied lazily when needed.
- * However neither backends preserve the order of image ops, they just applied all
- * pending lazy ops in a pre-determined fixed order, regardless in which order
- * they were originally applied. This might lead to inconsistencies.*/
-
- /**
- * Change image properties
- *
- * @param backend_data backend data
- * @param prop the property to change
- * @param image_data an image data structure returned by the backend
- * @param args property value
- * @return whether the operation is successful
- */
- bool (*set_image_property)(backend_t *backend_data, enum image_properties prop,
- void *image_data, void *args);
-
- /**
- * Manipulate an image. Image properties are untouched.
- *
- * @param backend_data backend data
- * @param op the operation to perform
- * @param image_data an image data structure returned by the backend
- * @param reg_op the clip region, define the part of the image to be
- * operated on.
- * @param reg_visible define the part of the image that will eventually
- * be visible on target. this is a hint to the backend
- * for optimization purposes.
- * @param args extra arguments, operation specific
- * @return whether the operation is successful
- */
- bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
- const region_t *reg_op, const region_t *reg_visible, void *args);
-
- /**
- * Read the color of the pixel at given position of the given image. Image
- * properties have no effect.
- *
- * @param backend_data backend_data
- * @param image_data an image data structure previously returned by the
- * backend. the image to read pixel from.
- * @param x, y coordinate of the pixel to read
- * @param[out] color the color of the pixel
- * @return whether the operation is successful
- */
- bool (*read_pixel)(backend_t *backend_data, void *image_data, int x, int y,
- struct color *output);
-
- /// Create another instance of the `image_data`. All `image_op` and
- /// `set_image_property` calls on the returned image should not affect the
- /// original image
- void *(*clone_image)(backend_t *base, const void *image_data,
- const region_t *reg_visible);
-
- /// Create a blur context that can be used to call `blur`
- void *(*create_blur_context)(backend_t *base, enum blur_method, void *args);
- /// Destroy a blur context
- void (*destroy_blur_context)(backend_t *base, void *ctx);
- /// Get how many pixels outside of the blur area is needed for blur
- void (*get_blur_size)(void *blur_context, int *width, int *height);
-
- // =========== Hooks ============
- /// Let the backend hook into the event handling queue
- /// Not implemented yet
- void (*set_ready_callback)(backend_t *, backend_ready_callback_t cb);
- /// Called right after the core has handled its events.
- /// Not implemented yet
- void (*handle_events)(backend_t *);
- // =========== Misc ============
- /// Return the driver that is been used by the backend
- enum driver (*detect_driver)(backend_t *backend_data);
-
- void (*diagnostics)(backend_t *backend_data);
-};
-
-extern struct backend_operations *backend_list[];
-
-void paint_all_new(session_t *ps, struct managed_win *const t, bool ignore_damage)
- attr_nonnull(1);
diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c
deleted file mode 100644
index c0377d3..0000000
--- a/src/backend/backend_common.c
+++ /dev/null
@@ -1,480 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#include <math.h>
-#include <string.h>
-#include <xcb/render.h>
-#include <xcb/xcb_image.h>
-#include <xcb/xcb_renderutil.h>
-
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "common.h"
-#include "config.h"
-#include "kernel.h"
-#include "log.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-/**
- * Generate a 1x1 <code>Picture</code> of a particular color.
- */
-xcb_render_picture_t solid_picture(xcb_connection_t *c, xcb_drawable_t d, bool argb,
- double a, double r, double g, double b) {
- xcb_pixmap_t pixmap;
- xcb_render_picture_t picture;
- xcb_render_create_picture_value_list_t pa;
- xcb_render_color_t col;
- xcb_rectangle_t rect;
-
- pixmap = x_create_pixmap(c, argb ? 32 : 8, d, 1, 1);
- if (!pixmap)
- return XCB_NONE;
-
- pa.repeat = 1;
- picture = x_create_picture_with_standard_and_pixmap(
- c, argb ? XCB_PICT_STANDARD_ARGB_32 : XCB_PICT_STANDARD_A_8, pixmap,
- XCB_RENDER_CP_REPEAT, &pa);
-
- if (!picture) {
- xcb_free_pixmap(c, pixmap);
- return XCB_NONE;
- }
-
- col.alpha = (uint16_t)(a * 0xffff);
- col.red = (uint16_t)(r * 0xffff);
- col.green = (uint16_t)(g * 0xffff);
- col.blue = (uint16_t)(b * 0xffff);
-
- rect.x = 0;
- rect.y = 0;
- rect.width = 1;
- rect.height = 1;
-
- xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_SRC, picture, col, 1, &rect);
- xcb_free_pixmap(c, pixmap);
-
- return picture;
-}
-
-xcb_image_t *
-make_shadow(xcb_connection_t *c, const conv *kernel, double opacity, int width, int height) {
- /*
- * We classify shadows into 4 kinds of regions
- * r = shadow radius
- * (0, 0) is the top left of the window itself
- * -r r width-r width+r
- * -r +-----+---------+-----+
- * | 1 | 2 | 1 |
- * r +-----+---------+-----+
- * | 2 | 3 | 2 |
- * height-r +-----+---------+-----+
- * | 1 | 2 | 1 |
- * height+r +-----+---------+-----+
- */
- xcb_image_t *ximage;
- const double *shadow_sum = kernel->rsum;
- assert(shadow_sum);
- // We only support square kernels for shadow
- assert(kernel->w == kernel->h);
- int d = kernel->w;
- int r = d / 2;
- int swidth = width + r * 2, sheight = height + r * 2;
-
- assert(d % 2 == 1);
- assert(d > 0);
-
- ximage = xcb_image_create_native(c, to_u16_checked(swidth), to_u16_checked(sheight),
- XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL);
- if (!ximage) {
- log_error("failed to create an X image");
- return 0;
- }
-
- unsigned char *data = ximage->data;
- long sstride = ximage->stride;
-
- // If the window body is smaller than the kernel, we do convolution directly
- if (width < r * 2 && height < r * 2) {
- for (int y = 0; y < sheight; y++) {
- for (int x = 0; x < swidth; x++) {
- double sum = sum_kernel_normalized(
- kernel, d - x - 1, d - y - 1, width, height);
- data[y * sstride + x] = (uint8_t)(sum * 255.0);
- }
- }
- return ximage;
- }
-
- if (height < r * 2) {
- // Implies width >= r * 2
- // If the window height is smaller than the kernel, we divide
- // the window like this:
- // -r r width-r width+r
- // +------+-------------+------+
- // | | | |
- // +------+-------------+------+
- for (int y = 0; y < sheight; y++) {
- for (int x = 0; x < r * 2; x++) {
- double sum = sum_kernel_normalized(kernel, d - x - 1,
- d - y - 1, d, height) *
- 255.0;
- data[y * sstride + x] = (uint8_t)sum;
- data[y * sstride + swidth - x - 1] = (uint8_t)sum;
- }
- }
- for (int y = 0; y < sheight; y++) {
- double sum =
- sum_kernel_normalized(kernel, 0, d - y - 1, d, height) * 255.0;
- memset(&data[y * sstride + r * 2], (uint8_t)sum,
- (size_t)(width - 2 * r));
- }
- return ximage;
- }
- if (width < r * 2) {
- // Similarly, for width smaller than kernel
- for (int y = 0; y < r * 2; y++) {
- for (int x = 0; x < swidth; x++) {
- double sum = sum_kernel_normalized(kernel, d - x - 1,
- d - y - 1, width, d) *
- 255.0;
- data[y * sstride + x] = (uint8_t)sum;
- data[(sheight - y - 1) * sstride + x] = (uint8_t)sum;
- }
- }
- for (int x = 0; x < swidth; x++) {
- double sum =
- sum_kernel_normalized(kernel, d - x - 1, 0, width, d) * 255.0;
- for (int y = r * 2; y < height; y++) {
- data[y * sstride + x] = (uint8_t)sum;
- }
- }
- return ximage;
- }
-
- // Implies: width >= r * 2 && height >= r * 2
-
- // Fill part 3
- for (int y = r; y < height + r; y++) {
- memset(data + sstride * y + r, (uint8_t)(255 * opacity), (size_t)width);
- }
-
- // Part 1
- for (int y = 0; y < r * 2; y++) {
- for (int x = 0; x < r * 2; x++) {
- double tmpsum = shadow_sum[y * d + x] * opacity * 255.0;
- data[y * sstride + x] = (uint8_t)tmpsum;
- data[(sheight - y - 1) * sstride + x] = (uint8_t)tmpsum;
- data[(sheight - y - 1) * sstride + (swidth - x - 1)] = (uint8_t)tmpsum;
- data[y * sstride + (swidth - x - 1)] = (uint8_t)tmpsum;
- }
- }
-
- // Part 2, top/bottom
- for (int y = 0; y < r * 2; y++) {
- double tmpsum = shadow_sum[d * y + d - 1] * opacity * 255.0;
- memset(&data[y * sstride + r * 2], (uint8_t)tmpsum, (size_t)(width - r * 2));
- memset(&data[(sheight - y - 1) * sstride + r * 2], (uint8_t)tmpsum,
- (size_t)(width - r * 2));
- }
-
- // Part 2, left/right
- for (int x = 0; x < r * 2; x++) {
- double tmpsum = shadow_sum[d * (d - 1) + x] * opacity * 255.0;
- for (int y = r * 2; y < height; y++) {
- data[y * sstride + x] = (uint8_t)tmpsum;
- data[y * sstride + (swidth - x - 1)] = (uint8_t)tmpsum;
- }
- }
-
- return ximage;
-}
-
-/**
- * Generate shadow <code>Picture</code> for a window.
- */
-bool build_shadow(xcb_connection_t *c, xcb_drawable_t d, double opacity, const int width,
- const int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
- xcb_pixmap_t *pixmap, xcb_render_picture_t *pict) {
- xcb_image_t *shadow_image = NULL;
- xcb_pixmap_t shadow_pixmap = XCB_NONE, shadow_pixmap_argb = XCB_NONE;
- xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
- xcb_gcontext_t gc = XCB_NONE;
-
- shadow_image = make_shadow(c, kernel, opacity, width, height);
- if (!shadow_image) {
- log_error("Failed to make shadow");
- return false;
- }
-
- shadow_pixmap = x_create_pixmap(c, 8, d, shadow_image->width, shadow_image->height);
- shadow_pixmap_argb =
- x_create_pixmap(c, 32, d, shadow_image->width, shadow_image->height);
-
- if (!shadow_pixmap || !shadow_pixmap_argb) {
- log_error("Failed to create shadow pixmaps");
- goto shadow_picture_err;
- }
-
- shadow_picture = x_create_picture_with_standard_and_pixmap(
- c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
- shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
- c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
- if (!shadow_picture || !shadow_picture_argb) {
- goto shadow_picture_err;
- }
-
- gc = x_new_id(c);
- xcb_create_gc(c, gc, shadow_pixmap, 0, NULL);
-
- // We need to make room for protocol metadata in the request. The metadata should
- // be 24 bytes plus padding, let's be generous and give it 1kb
- auto maximum_image_size = xcb_get_maximum_request_length(c) * 4 - 1024;
- auto maximum_row =
- to_u16_checked(clamp(maximum_image_size / shadow_image->stride, 0, UINT16_MAX));
- if (maximum_row <= 0) {
- // TODO(yshui) Upload image with XShm
- log_error("X server request size limit is too restrictive, or the shadow "
- "image is too wide for us to send a single row of the shadow "
- "image. Shadow size: %dx%d",
- width, height);
- goto shadow_picture_err;
- }
-
- for (uint32_t row = 0; row < shadow_image->height; row += maximum_row) {
- auto batch_height = maximum_row;
- if (batch_height > shadow_image->height - row) {
- batch_height = to_u16_checked(shadow_image->height - row);
- }
-
- uint32_t offset = row * shadow_image->stride / sizeof(*shadow_image->data);
- xcb_put_image(c, (uint8_t)shadow_image->format, shadow_pixmap, gc,
- shadow_image->width, batch_height, 0, to_i16_checked(row),
- 0, shadow_image->depth, shadow_image->stride * batch_height,
- shadow_image->data + offset);
- }
-
- xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, shadow_pixel, shadow_picture,
- shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width,
- shadow_image->height);
-
- *pixmap = shadow_pixmap_argb;
- *pict = shadow_picture_argb;
-
- xcb_free_gc(c, gc);
- xcb_image_destroy(shadow_image);
- xcb_free_pixmap(c, shadow_pixmap);
- xcb_render_free_picture(c, shadow_picture);
-
- return true;
-
-shadow_picture_err:
- if (shadow_image) {
- xcb_image_destroy(shadow_image);
- }
- if (shadow_pixmap) {
- xcb_free_pixmap(c, shadow_pixmap);
- }
- if (shadow_pixmap_argb) {
- xcb_free_pixmap(c, shadow_pixmap_argb);
- }
- if (shadow_picture) {
- xcb_render_free_picture(c, shadow_picture);
- }
- if (shadow_picture_argb) {
- xcb_render_free_picture(c, shadow_picture_argb);
- }
- if (gc) {
- xcb_free_gc(c, gc);
- }
-
- return false;
-}
-
-void *
-default_backend_render_shadow(backend_t *backend_data, int width, int height,
- const conv *kernel, double r, double g, double b, double a) {
- xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root,
- true, 1, r, g, b),
- shadow = XCB_NONE;
- xcb_render_picture_t pict = XCB_NONE;
-
- if (!build_shadow(backend_data->c, backend_data->root, a, width, height, kernel,
- shadow_pixel, &shadow, &pict)) {
- return NULL;
- }
-
- auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
- void *ret = backend_data->ops->bind_pixmap(
- backend_data, shadow, x_get_visual_info(backend_data->c, visual), true);
- xcb_render_free_picture(backend_data->c, pict);
- return ret;
-}
-
-static struct conv **generate_box_blur_kernel(struct box_blur_args *args, int *kernel_count) {
- int r = args->size * 2 + 1;
- assert(r > 0);
- auto ret = ccalloc(2, struct conv *);
- ret[0] = cvalloc(sizeof(struct conv) + sizeof(double) * (size_t)r);
- ret[1] = cvalloc(sizeof(struct conv) + sizeof(double) * (size_t)r);
- ret[0]->w = r;
- ret[0]->h = 1;
- ret[1]->w = 1;
- ret[1]->h = r;
- for (int i = 0; i < r; i++) {
- ret[0]->data[i] = 1;
- ret[1]->data[i] = 1;
- }
- *kernel_count = 2;
- return ret;
-}
-
-static struct conv **
-generate_gaussian_blur_kernel(struct gaussian_blur_args *args, int *kernel_count) {
- int r = args->size * 2 + 1;
- assert(r > 0);
- auto ret = ccalloc(2, struct conv *);
- ret[0] = cvalloc(sizeof(struct conv) + sizeof(double) * (size_t)r);
- ret[1] = cvalloc(sizeof(struct conv) + sizeof(double) * (size_t)r);
- ret[0]->w = r;
- ret[0]->h = 1;
- ret[1]->w = 1;
- ret[1]->h = r;
- for (int i = 0; i <= args->size; i++) {
- ret[0]->data[i] = ret[0]->data[r - i - 1] =
- 1.0 / (sqrt(2.0 * M_PI) * args->deviation) *
- exp(-(args->size - i) * (args->size - i) /
- (2 * args->deviation * args->deviation));
- ret[1]->data[i] = ret[1]->data[r - i - 1] = ret[0]->data[i];
- }
- *kernel_count = 2;
- return ret;
-}
-
-/// Generate blur kernels for gaussian and box blur methods. Generated kernel is not
-/// normalized, and the center element will always be 1.
-struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count) {
- switch (method) {
- case BLUR_METHOD_BOX: return generate_box_blur_kernel(args, kernel_count);
- case BLUR_METHOD_GAUSSIAN:
- return generate_gaussian_blur_kernel(args, kernel_count);
- default: break;
- }
- return NULL;
-}
-
-/// Generate kernel parameters for dual-kawase blur method. Falls back on approximating
-/// standard gauss radius if strength is zero or below.
-struct dual_kawase_params *generate_dual_kawase_params(void *args) {
- struct dual_kawase_blur_args *blur_args = args;
- static const struct {
- int iterations; /// Number of down- and upsample iterations
- float offset; /// Sample offset in half-pixels
- int min_radius; /// Approximate gauss-blur with at least this
- /// radius and std-deviation
- } strength_levels[20] = {
- {.iterations = 1, .offset = 1.25f, .min_radius = 1}, // LVL 1
- {.iterations = 1, .offset = 2.25f, .min_radius = 6}, // LVL 2
- {.iterations = 2, .offset = 2.00f, .min_radius = 11}, // LVL 3
- {.iterations = 2, .offset = 3.00f, .min_radius = 17}, // LVL 4
- {.iterations = 2, .offset = 4.25f, .min_radius = 24}, // LVL 5
- {.iterations = 3, .offset = 2.50f, .min_radius = 32}, // LVL 6
- {.iterations = 3, .offset = 3.25f, .min_radius = 40}, // LVL 7
- {.iterations = 3, .offset = 4.25f, .min_radius = 51}, // LVL 8
- {.iterations = 3, .offset = 5.50f, .min_radius = 67}, // LVL 9
- {.iterations = 4, .offset = 3.25f, .min_radius = 83}, // LVL 10
- {.iterations = 4, .offset = 4.00f, .min_radius = 101}, // LVL 11
- {.iterations = 4, .offset = 5.00f, .min_radius = 123}, // LVL 12
- {.iterations = 4, .offset = 6.00f, .min_radius = 148}, // LVL 13
- {.iterations = 4, .offset = 7.25f, .min_radius = 178}, // LVL 14
- {.iterations = 4, .offset = 8.25f, .min_radius = 208}, // LVL 15
- {.iterations = 5, .offset = 4.50f, .min_radius = 236}, // LVL 16
- {.iterations = 5, .offset = 5.25f, .min_radius = 269}, // LVL 17
- {.iterations = 5, .offset = 6.25f, .min_radius = 309}, // LVL 18
- {.iterations = 5, .offset = 7.25f, .min_radius = 357}, // LVL 19
- {.iterations = 5, .offset = 8.50f, .min_radius = 417}, // LVL 20
- };
-
- auto params = ccalloc(1, struct dual_kawase_params);
- params->iterations = 0;
- params->offset = 1.0f;
-
- if (blur_args->strength <= 0 && blur_args->size) {
- // find highest level that approximates blur-strength with the selected
- // gaussian blur-radius
- int lvl = 1;
- while (strength_levels[lvl - 1].min_radius < blur_args->size && lvl < 20) {
- ++lvl;
- }
- blur_args->strength = lvl;
- }
- if (blur_args->strength <= 0) {
- // default value
- blur_args->strength = 5;
- }
-
- assert(blur_args->strength > 0 && blur_args->strength <= 20);
- params->iterations = strength_levels[blur_args->strength - 1].iterations;
- params->offset = strength_levels[blur_args->strength - 1].offset;
-
- // Expand sample area to cover the smallest texture / highest selected iteration:
- // - Smallest texture dimensions are halved `iterations`-times
- // - Upsample needs pixels two-times `offset` away from the border
- // - Plus one for interpolation differences
- params->expand = (1 << params->iterations) * 2 * (int)ceil(params->offset) + 1;
-
- return params;
-}
-
-void *default_clone_image(backend_t *base attr_unused, const void *image_data,
- const region_t *reg_visible attr_unused) {
- auto new_img = ccalloc(1, struct backend_image);
- *new_img = *(struct backend_image *)image_data;
- new_img->inner->refcount++;
- return new_img;
-}
-
-bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
- void *image_data, void *arg) {
- struct backend_image *tex = image_data;
- int *iargs = arg;
- bool *bargs = arg;
- double *dargs = arg;
- switch (op) {
- case IMAGE_PROPERTY_INVERTED: tex->color_inverted = bargs[0]; break;
- case IMAGE_PROPERTY_DIM_LEVEL: tex->dim = dargs[0]; break;
- case IMAGE_PROPERTY_OPACITY: tex->opacity = dargs[0]; break;
- case IMAGE_PROPERTY_EFFECTIVE_SIZE:
- // texture is already set to repeat, so nothing else we need to do
- tex->ewidth = iargs[0];
- tex->eheight = iargs[1];
- break;
- case IMAGE_PROPERTY_MAX_BRIGHTNESS: tex->max_brightness = dargs[0]; break;
- }
-
- return true;
-}
-
-bool default_is_image_transparent(backend_t *base attr_unused, void *image_data) {
- struct backend_image *img = image_data;
- return img->opacity < 1 || img->inner->has_alpha;
-}
-
-struct backend_image *default_new_backend_image(int w, int h) {
- auto ret = ccalloc(1, struct backend_image);
- ret->opacity = 1;
- ret->dim = 0;
- ret->max_brightness = 1;
- ret->eheight = h;
- ret->ewidth = w;
- ret->color_inverted = false;
- return ret;
-}
-
-void init_backend_base(struct backend_base *base, session_t *ps) {
- base->c = ps->c;
- base->loop = ps->loop;
- base->root = ps->root;
- base->busy = false;
- base->ops = NULL;
-}
diff --git a/src/backend/backend_common.h b/src/backend/backend_common.h
deleted file mode 100644
index 5c9c806..0000000
--- a/src/backend/backend_common.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-
-#include <xcb/render.h>
-#include <xcb/xcb_image.h>
-
-#include <stdbool.h>
-
-#include "backend.h"
-#include "config.h"
-#include "region.h"
-
-typedef struct session session_t;
-typedef struct win win;
-typedef struct conv conv;
-typedef struct backend_base backend_t;
-struct backend_operations;
-
-struct dual_kawase_params {
- /// Number of downsample passes
- int iterations;
- /// Pixel offset for down- and upsample
- float offset;
- /// Save area around blur target (@ref resize_width, @ref resize_height)
- int expand;
-};
-
-struct backend_image_inner_base {
- int refcount;
- bool has_alpha;
-};
-
-struct backend_image {
- // Backend dependent inner image data
- struct backend_image_inner_base *inner;
- double opacity;
- double dim;
- double max_brightness;
- // Effective size of the image
- int ewidth, eheight;
- bool color_inverted;
-};
-
-bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width,
- int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
- xcb_pixmap_t *pixmap, xcb_render_picture_t *pict);
-
-xcb_render_picture_t solid_picture(xcb_connection_t *, xcb_drawable_t, bool argb,
- double a, double r, double g, double b);
-
-xcb_image_t *
-make_shadow(xcb_connection_t *c, const conv *kernel, double opacity, int width, int height);
-
-/// The default implementation of `is_win_transparent`, it simply looks at win::mode. So
-/// this is not suitable for backends that alter the content of windows
-bool default_is_win_transparent(void *, win *, void *);
-
-/// The default implementation of `is_frame_transparent`, it uses win::frame_opacity. Same
-/// caveat as `default_is_win_transparent` applies.
-bool default_is_frame_transparent(void *, win *, void *);
-
-void *
-default_backend_render_shadow(backend_t *backend_data, int width, int height,
- const conv *kernel, double r, double g, double b, double a);
-
-void init_backend_base(struct backend_base *base, session_t *ps);
-
-struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count);
-struct dual_kawase_params *generate_dual_kawase_params(void *args);
-
-void *default_clone_image(backend_t *base, const void *image_data, const region_t *reg);
-void *default_resize_image(backend_t *base, const void *image_data, uint16_t desired_width,
- uint16_t desired_height, const region_t *reg);
-bool default_is_image_transparent(backend_t *base attr_unused, void *image_data);
-bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
- void *image_data, void *arg);
-struct backend_image *default_new_backend_image(int w, int h);
diff --git a/src/backend/driver.c b/src/backend/driver.c
deleted file mode 100644
index a41d2fd..0000000
--- a/src/backend/driver.c
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#include <stdlib.h>
-#include <string.h>
-
-#include <xcb/randr.h>
-#include <xcb/xcb.h>
-
-#include "backend/backend.h"
-#include "backend/driver.h"
-#include "common.h"
-#include "compiler.h"
-#include "log.h"
-
-/// Apply driver specified global workarounds. It's safe to call this multiple times.
-void apply_driver_workarounds(struct session *ps, enum driver driver) {
- if (driver & DRIVER_NVIDIA) {
- // setenv("__GL_YIELD", "usleep", true);
- setenv("__GL_MaxFramesAllowed", "1", true);
- ps->o.xrender_sync_fence = true;
- }
-}
-
-enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) {
- enum driver ret = 0;
- // First we try doing backend agnostic detection using RANDR
- // There's no way to query the X server about what driver is loaded, so RANDR is
- // our best shot.
- auto randr_version = xcb_randr_query_version_reply(
- c, xcb_randr_query_version(c, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION),
- NULL);
- if (randr_version &&
- (randr_version->major_version > 1 || randr_version->minor_version >= 4)) {
- auto r = xcb_randr_get_providers_reply(
- c, xcb_randr_get_providers(c, window), NULL);
- if (r == NULL) {
- log_warn("Failed to get RANDR providers");
- free(randr_version);
- return 0;
- }
-
- auto providers = xcb_randr_get_providers_providers(r);
- for (auto i = 0; i < xcb_randr_get_providers_providers_length(r); i++) {
- auto r2 = xcb_randr_get_provider_info_reply(
- c, xcb_randr_get_provider_info(c, providers[i], r->timestamp), NULL);
- if (r2 == NULL) {
- continue;
- }
- if (r2->num_outputs == 0) {
- free(r2);
- continue;
- }
-
- auto name_len = xcb_randr_get_provider_info_name_length(r2);
- assert(name_len >= 0);
- auto name =
- strndup(xcb_randr_get_provider_info_name(r2), (size_t)name_len);
- if (strcasestr(name, "modesetting") != NULL) {
- ret |= DRIVER_MODESETTING;
- } else if (strcasestr(name, "Radeon") != NULL) {
- // Be conservative, add both radeon drivers
- ret |= DRIVER_AMDGPU | DRIVER_RADEON;
- } else if (strcasestr(name, "NVIDIA") != NULL) {
- ret |= DRIVER_NVIDIA;
- } else if (strcasestr(name, "nouveau") != NULL) {
- ret |= DRIVER_NOUVEAU;
- } else if (strcasestr(name, "Intel") != NULL) {
- ret |= DRIVER_INTEL;
- }
- free(name);
- free(r2);
- }
- free(r);
- }
- free(randr_version);
-
- // If the backend supports driver detection, use that as well
- if (backend_data && backend_data->ops->detect_driver) {
- ret |= backend_data->ops->detect_driver(backend_data);
- }
- return ret;
-}
diff --git a/src/backend/driver.h b/src/backend/driver.h
deleted file mode 100644
index a37cda3..0000000
--- a/src/backend/driver.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-#pragma once
-
-#include <stddef.h>
-#include <stdio.h>
-#include <xcb/xcb.h>
-
-#include "utils.h"
-
-struct session;
-struct backend_base;
-
-// A list of known driver quirks:
-// * NVIDIA driver doesn't like seeing the same pixmap under different
-// ids, so avoid naming the pixmap again when it didn't actually change.
-
-/// A list of possible drivers.
-/// The driver situation is a bit complicated. There are two drivers we care about: the
-/// DDX, and the OpenGL driver. They are usually paired, but not always, since there is
-/// also the generic modesetting driver.
-/// This enum represents _both_ drivers.
-enum driver {
- DRIVER_AMDGPU = 1, // AMDGPU for DDX, radeonsi for OpenGL
- DRIVER_RADEON = 2, // ATI for DDX, mesa r600 for OpenGL
- DRIVER_FGLRX = 4,
- DRIVER_NVIDIA = 8,
- DRIVER_NOUVEAU = 16,
- DRIVER_INTEL = 32,
- DRIVER_MODESETTING = 64,
-};
-
-static const char *driver_names[] = {
- "AMDGPU", "Radeon", "fglrx", "NVIDIA", "nouveau", "Intel", "modesetting",
-};
-
-/// Return a list of all drivers currently in use by the X server.
-/// Note, this is a best-effort test, so no guarantee all drivers will be detected.
-enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_t);
-
-/// Apply driver specified global workarounds. It's safe to call this multiple times.
-void apply_driver_workarounds(struct session *ps, enum driver);
-
-// Print driver names to stdout, for diagnostics
-static inline void print_drivers(enum driver drivers) {
- const char *seen_drivers[ARR_SIZE(driver_names)];
- int driver_count = 0;
- for (size_t i = 0; i < ARR_SIZE(driver_names); i++) {
- if (drivers & (1ul << i)) {
- seen_drivers[driver_count++] = driver_names[i];
- }
- }
-
- if (driver_count > 0) {
- printf("%s", seen_drivers[0]);
- for (int i = 1; i < driver_count; i++) {
- printf(", %s", seen_drivers[i]);
- }
- }
- printf("\n");
-}
diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c
deleted file mode 100644
index a057b97..0000000
--- a/src/backend/dummy/dummy.c
+++ /dev/null
@@ -1,174 +0,0 @@
-#include <uthash.h>
-#include <xcb/xcb.h>
-
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h"
-#include "region.h"
-#include "types.h"
-#include "uthash_extra.h"
-#include "utils.h"
-#include "x.h"
-
-struct dummy_image {
- xcb_pixmap_t pixmap;
- bool transparent;
- int *refcount;
- UT_hash_handle hh;
-};
-
-struct dummy_data {
- struct backend_base base;
- struct dummy_image *images;
-};
-
-struct backend_base *dummy_init(struct session *ps attr_unused) {
- auto ret = (struct backend_base *)ccalloc(1, struct dummy_data);
- ret->c = ps->c;
- ret->loop = ps->loop;
- ret->root = ps->root;
- ret->busy = false;
- return ret;
-}
-
-void dummy_deinit(struct backend_base *data) {
- auto dummy = (struct dummy_data *)data;
- HASH_ITER2(dummy->images, img) {
- log_warn("Backend image for pixmap %#010x is not freed", img->pixmap);
- HASH_DEL(dummy->images, img);
- free(img->refcount);
- free(img);
- }
- free(dummy);
-}
-
-static void dummy_check_image(struct backend_base *base, const struct dummy_image *img) {
- auto dummy = (struct dummy_data *)base;
- struct dummy_image *tmp = NULL;
- HASH_FIND_INT(dummy->images, &img->pixmap, tmp);
- if (!tmp) {
- log_warn("Using an invalid (possibly freed) image");
- assert(false);
- }
- assert(*tmp->refcount > 0);
-}
-
-void dummy_compose(struct backend_base *base, void *image, int dst_x1 attr_unused,
- int dst_y1 attr_unused, int dst_x2 attr_unused, int dst_y2 attr_unused,
- const region_t *reg_paint attr_unused, const region_t *reg_visible attr_unused) {
- dummy_check_image(base, image);
-}
-
-void dummy_fill(struct backend_base *backend_data attr_unused, struct color c attr_unused,
- const region_t *clip attr_unused) {
-}
-
-bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity attr_unused,
- void *blur_ctx attr_unused, const region_t *reg_blur attr_unused,
- const region_t *reg_visible attr_unused) {
- return true;
-}
-
-void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
- struct xvisual_info fmt, bool owned attr_unused) {
- auto dummy = (struct dummy_data *)base;
- struct dummy_image *img = NULL;
- HASH_FIND_INT(dummy->images, &pixmap, img);
- if (img) {
- (*img->refcount)++;
- return img;
- }
-
- img = ccalloc(1, struct dummy_image);
- img->pixmap = pixmap;
- img->transparent = fmt.alpha_size != 0;
- img->refcount = ccalloc(1, int);
- *img->refcount = 1;
-
- HASH_ADD_INT(dummy->images, pixmap, img);
- return (void *)img;
-}
-
-void dummy_release_image(backend_t *base, void *image) {
- auto dummy = (struct dummy_data *)base;
- auto img = (struct dummy_image *)image;
- assert(*img->refcount > 0);
- (*img->refcount)--;
- if (*img->refcount == 0) {
- HASH_DEL(dummy->images, img);
- free(img->refcount);
- free(img);
- }
-}
-
-bool dummy_is_image_transparent(struct backend_base *base, void *image) {
- auto img = (struct dummy_image *)image;
- dummy_check_image(base, img);
- return img->transparent;
-}
-
-int dummy_buffer_age(struct backend_base *base attr_unused) {
- return 2;
-}
-
-bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unused,
- void *image, const region_t *reg_op attr_unused,
- const region_t *reg_visible attr_unused, void *args attr_unused) {
- dummy_check_image(base, image);
- return true;
-}
-
-bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused,
- void *image, void *arg attr_unused) {
- dummy_check_image(base, image);
- return true;
-}
-
-void *dummy_clone_image(struct backend_base *base, const void *image,
- const region_t *reg_visible attr_unused) {
- auto img = (const struct dummy_image *)image;
- dummy_check_image(base, img);
- (*img->refcount)++;
- return (void *)img;
-}
-
-void *dummy_create_blur_context(struct backend_base *base attr_unused,
- enum blur_method method attr_unused, void *args attr_unused) {
- static int dummy_context;
- return &dummy_context;
-}
-
-void dummy_destroy_blur_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
-}
-
-void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
- // These numbers are arbitrary, to make sure the reisze_region code path is
- // covered.
- *width = 5;
- *height = 5;
-}
-
-struct backend_operations dummy_ops = {
- .init = dummy_init,
- .deinit = dummy_deinit,
- .compose = dummy_compose,
- .fill = dummy_fill,
- .blur = dummy_blur,
- .bind_pixmap = dummy_bind_pixmap,
- .render_shadow = default_backend_render_shadow,
- .release_image = dummy_release_image,
- .is_image_transparent = dummy_is_image_transparent,
- .buffer_age = dummy_buffer_age,
- .max_buffer_age = 5,
-
- .image_op = dummy_image_op,
- .clone_image = dummy_clone_image,
- .set_image_property = dummy_set_image_property,
- .create_blur_context = dummy_create_blur_context,
- .destroy_blur_context = dummy_destroy_blur_context,
- .get_blur_size = dummy_get_blur_size,
-
-};
diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c
deleted file mode 100644
index 8cc5a05..0000000
--- a/src/backend/gl/gl_common.c
+++ /dev/null
@@ -1,1922 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#include <GL/gl.h>
-#include <GL/glext.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <xcb/render.h> // for xcb_render_fixed_t, XXX
-
-#include "backend/backend.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "string_utils.h"
-#include "types.h"
-#include "utils.h"
-
-#include "backend/backend_common.h"
-#include "backend/gl/gl_common.h"
-
-#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
-#define QUOTE(...) #__VA_ARGS__
-
-static const GLuint vert_coord_loc = 0;
-static const GLuint vert_in_texcoord_loc = 1;
-
-struct gl_blur_context {
- enum blur_method method;
- gl_blur_shader_t *blur_shader;
-
- /// Temporary textures used for blurring
- GLuint *blur_textures;
- int blur_texture_count;
- /// Temporary fbos used for blurring
- GLuint *blur_fbos;
- int blur_fbo_count;
-
- /// Cached dimensions of each blur_texture. They are the same size as the target,
- /// so they are always big enough without resizing.
- /// Turns out calling glTexImage to resize is expensive, so we avoid that.
- struct texture_size {
- int width;
- int height;
- } * texture_sizes;
-
- /// Cached dimensions of the offscreen framebuffer. It's the same size as the
- /// target but is expanded in either direction by resize_width / resize_height.
- int fb_width, fb_height;
-
- /// How much do we need to resize the damaged region for blurring.
- int resize_width, resize_height;
-
- int npasses;
-};
-
-static GLint glGetUniformLocationChecked(GLuint p, const char *name) {
- auto ret = glGetUniformLocation(p, name);
- if (ret < 0) {
- log_info("Failed to get location of uniform '%s'. This is normal when "
- "using custom shaders.",
- name);
- }
- return ret;
-}
-
-GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
- log_trace("===\n%s\n===", shader_str);
-
- bool success = false;
- GLuint shader = glCreateShader(shader_type);
- if (!shader) {
- log_error("Failed to create shader with type %#x.", shader_type);
- goto end;
- }
- glShaderSource(shader, 1, &shader_str, NULL);
- glCompileShader(shader);
-
- // Get shader status
- {
- GLint status = GL_FALSE;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (GL_FALSE == status) {
- GLint log_len = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
- if (log_len) {
- char log[log_len + 1];
- glGetShaderInfoLog(shader, log_len, NULL, log);
- log_error("Failed to compile shader with type %d: %s",
- shader_type, log);
- }
- goto end;
- }
- }
-
- success = true;
-
-end:
- if (shader && !success) {
- glDeleteShader(shader);
- shader = 0;
- }
-
- return shader;
-}
-
-GLuint gl_create_program(const GLuint *const shaders, int nshaders) {
- bool success = false;
- GLuint program = glCreateProgram();
- if (!program) {
- log_error("Failed to create program.");
- goto end;
- }
-
- for (int i = 0; i < nshaders; ++i)
- glAttachShader(program, shaders[i]);
- glLinkProgram(program);
-
- // Get program status
- {
- GLint status = GL_FALSE;
- glGetProgramiv(program, GL_LINK_STATUS, &status);
- if (GL_FALSE == status) {
- GLint log_len = 0;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
- if (log_len) {
- char log[log_len + 1];
- glGetProgramInfoLog(program, log_len, NULL, log);
- log_error("Failed to link program: %s", log);
- }
- goto end;
- }
- }
- success = true;
-
-end:
- if (program) {
- for (int i = 0; i < nshaders; ++i)
- glDetachShader(program, shaders[i]);
- }
- if (program && !success) {
- glDeleteProgram(program);
- program = 0;
- }
-
- return program;
-}
-
-/**
- * @brief Create a program from vertex and fragment shader strings.
- */
-GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str) {
- GLuint vert_shader = 0;
- GLuint frag_shader = 0;
- GLuint prog = 0;
-
- if (vert_shader_str)
- vert_shader = gl_create_shader(GL_VERTEX_SHADER, vert_shader_str);
- if (frag_shader_str)
- frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, frag_shader_str);
-
- {
- GLuint shaders[2];
- int count = 0;
- if (vert_shader) {
- shaders[count++] = vert_shader;
- }
- if (frag_shader) {
- shaders[count++] = frag_shader;
- }
- if (count) {
- prog = gl_create_program(shaders, count);
- }
- }
-
- if (vert_shader)
- glDeleteShader(vert_shader);
- if (frag_shader)
- glDeleteShader(frag_shader);
-
- return prog;
-}
-
-static void gl_free_prog_main(gl_win_shader_t *pprogram) {
- if (!pprogram)
- return;
- if (pprogram->prog) {
- glDeleteProgram(pprogram->prog);
- pprogram->prog = 0;
- }
-}
-
-/*
- * @brief Implements recursive part of gl_average_texture_color.
- *
- * @note In order to reduce number of textures which needs to be
- * allocated and deleted during this recursive render
- * we reuse the same two textures for render source and
- * destination simply by alterating between them.
- * Unfortunately on first iteration source_texture might
- * be read-only. In this case we will select auxiliary_texture as
- * destination_texture in order not to touch that read-only source
- * texture in following render iteration.
- * Otherwise we simply will switch source and destination textures
- * between each other on each render iteration.
- */
-static GLuint
-_gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destination_texture,
- GLuint auxiliary_texture, GLuint fbo, int width, int height) {
- const int max_width = 1;
- const int max_height = 1;
- const int from_width = next_power_of_two(width);
- const int from_height = next_power_of_two(height);
- const int to_width = from_width > max_width ? from_width / 2 : from_width;
- const int to_height = from_height > max_height ? from_height / 2 : from_height;
-
- // Prepare coordinates
- GLint coord[] = {
- // top left
- 0, 0, // vertex coord
- 0, 0, // texture coord
-
- // top right
- to_width, 0, // vertex coord
- width, 0, // texture coord
-
- // bottom right
- to_width, to_height, // vertex coord
- width, height, // texture coord
-
- // bottom left
- 0, to_height, // vertex coord
- 0, height, // texture coord
- };
- glBufferSubData(GL_ARRAY_BUFFER, 0, (long)sizeof(*coord) * 16, coord);
-
- // Prepare framebuffer for new render iteration
- glBindTexture(GL_TEXTURE_2D, destination_texture);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- destination_texture, 0);
- gl_check_fb_complete(GL_FRAMEBUFFER);
-
- // Bind source texture as downscaling shader uniform input
- glBindTexture(GL_TEXTURE_2D, source_texture);
-
- // Render into framebuffer
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
-
- // Have we downscaled enough?
- GLuint result;
- if (to_width > max_width || to_height > max_height) {
- GLuint new_source_texture = destination_texture;
- GLuint new_destination_texture =
- auxiliary_texture != 0 ? auxiliary_texture : source_texture;
- result = _gl_average_texture_color(base, new_source_texture,
- new_destination_texture, 0, fbo,
- to_width, to_height);
- } else {
- result = destination_texture;
- }
-
- return result;
-}
-
-/*
- * @brief Builds a 1x1 texture which has color corresponding to the average of all
- * pixels of img by recursively rendering into texture of quorter the size (half
- * width and half height).
- * Returned texture must not be deleted, since it's owned by the gl_image. It will be
- * deleted when the gl_image is released.
- */
-static GLuint gl_average_texture_color(backend_t *base, struct backend_image *img) {
- auto gd = (struct gl_data *)base;
- auto inner = (struct gl_texture *)img->inner;
-
- // Prepare textures which will be used for destination and source of rendering
- // during downscaling.
- const int texture_count = ARR_SIZE(inner->auxiliary_texture);
- if (!inner->auxiliary_texture[0]) {
- assert(!inner->auxiliary_texture[1]);
- glGenTextures(texture_count, inner->auxiliary_texture);
- glActiveTexture(GL_TEXTURE0);
- for (int i = 0; i < texture_count; i++) {
- glBindTexture(GL_TEXTURE_2D, inner->auxiliary_texture[i]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
- (GLint[]){0, 0, 0, 0});
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, inner->width,
- inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
- }
- }
-
- // Prepare framebuffer used for rendering and bind it
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
-
- // Enable shaders
- glUseProgram(gd->brightness_shader.prog);
- glUniform2f(glGetUniformLocationChecked(gd->brightness_shader.prog, "texsize"),
- (GLfloat)inner->width, (GLfloat)inner->height);
-
- // Prepare vertex attributes
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
- GLuint bo[2];
- glGenBuffers(2, bo);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
-
- // Allocate buffers for render input
- GLint coord[16] = {0};
- GLuint indices[] = {0, 1, 2, 2, 3, 0};
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_DYNAMIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
- GL_STATIC_DRAW);
-
- // Do actual recursive render to 1x1 texture
- GLuint result_texture = _gl_average_texture_color(
- base, inner->texture, inner->auxiliary_texture[0],
- inner->auxiliary_texture[1], fbo, inner->width, inner->height);
-
- // Cleanup vertex attributes
- glDisableVertexAttribArray(vert_coord_loc);
- glDisableVertexAttribArray(vert_in_texcoord_loc);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(2, bo);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- // Cleanup shaders
- glUseProgram(0);
-
- // Cleanup framebuffers
- glDeleteFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDrawBuffer(GL_BACK);
-
- // Cleanup render textures
- glBindTexture(GL_TEXTURE_2D, 0);
-
- gl_check_err();
-
- return result_texture;
-}
-
-/**
- * Render a region with texture data.
- *
- * @param ptex the texture
- * @param target the framebuffer to render into
- * @param dst_x,dst_y the top left corner of region where this texture
- * should go. In OpenGL coordinate system (important!).
- * @param reg_tgt the clip region, in Xorg coordinate system
- * @param reg_visible ignored
- */
-static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target,
- GLint *coord, GLuint *indices, int nrects) {
- auto gd = (struct gl_data *)base;
- auto inner = (struct gl_texture *)img->inner;
- if (!img || !inner->texture) {
- log_error("Missing texture.");
- return;
- }
-
- GLuint brightness = 0;
- if (img->max_brightness < 1.0) {
- brightness = gl_average_texture_color(base, img);
- }
-
- assert(gd->win_shader.prog);
- glUseProgram(gd->win_shader.prog);
- if (gd->win_shader.unifm_opacity >= 0) {
- glUniform1f(gd->win_shader.unifm_opacity, (float)img->opacity);
- }
- if (gd->win_shader.unifm_invert_color >= 0) {
- glUniform1i(gd->win_shader.unifm_invert_color, img->color_inverted);
- }
- if (gd->win_shader.unifm_tex >= 0) {
- glUniform1i(gd->win_shader.unifm_tex, 0);
- }
- if (gd->win_shader.unifm_dim >= 0) {
- glUniform1f(gd->win_shader.unifm_dim, (float)img->dim);
- }
- if (gd->win_shader.unifm_brightness >= 0) {
- glUniform1i(gd->win_shader.unifm_brightness, 1);
- }
- if (gd->win_shader.unifm_max_brightness >= 0) {
- glUniform1f(gd->win_shader.unifm_max_brightness, (float)img->max_brightness);
- }
-
- // log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n",
- // x, y, width, height, dx, dy, ptex->width, ptex->height, z);
-
- // Bind texture
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, brightness);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, inner->texture);
-
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-
- GLuint bo[2];
- glGenBuffers(2, bo);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
- indices, GL_STATIC_DRAW);
-
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
- glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
-
- glDisableVertexAttribArray(vert_coord_loc);
- glDisableVertexAttribArray(vert_in_texcoord_loc);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- // Cleanup
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDrawBuffer(GL_BACK);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(2, bo);
-
- glUseProgram(0);
-
- gl_check_err();
-
- return;
-}
-
-/// Convert rectangles in X coordinates to OpenGL vertex and texture coordinates
-/// @param[in] nrects, rects rectangles
-/// @param[in] dst_x, dst_y origin of the OpenGL texture, affect the calculated texture
-/// coordinates
-/// @param[in] texture_height height of the OpenGL texture
-/// @param[in] root_height height of the back buffer
-/// @param[in] y_inverted whether the texture is y inverted
-/// @param[out] coord, indices output
-static void
-x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int texture_height,
- int root_height, bool y_inverted, GLint *coord, GLuint *indices) {
- dst_y = root_height - dst_y;
- if (y_inverted) {
- dst_y -= texture_height;
- }
-
- for (int i = 0; i < nrects; i++) {
- // Y-flip. Note after this, crect.y1 > crect.y2
- rect_t crect = rects[i];
- crect.y1 = root_height - crect.y1;
- crect.y2 = root_height - crect.y2;
-
- // Calculate texture coordinates
- // (texture_x1, texture_y1), texture coord for the _bottom left_ corner
- GLint texture_x1 = crect.x1 - dst_x, texture_y1 = crect.y2 - dst_y,
- texture_x2 = texture_x1 + (crect.x2 - crect.x1),
- texture_y2 = texture_y1 + (crect.y1 - crect.y2);
-
- // X pixmaps might be Y inverted, invert the texture coordinates
- if (y_inverted) {
- texture_y1 = texture_height - texture_y1;
- texture_y2 = texture_height - texture_y2;
- }
-
- // Vertex coordinates
- auto vx1 = crect.x1;
- auto vy1 = crect.y2;
- auto vx2 = crect.x2;
- auto vy2 = crect.y1;
-
- // log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d",
- // ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
-
- memcpy(&coord[i * 16],
- ((GLint[][2]){
- {vx1, vy1},
- {texture_x1, texture_y1},
- {vx2, vy1},
- {texture_x2, texture_y1},
- {vx2, vy2},
- {texture_x2, texture_y2},
- {vx1, vy2},
- {texture_x1, texture_y2},
- }),
- sizeof(GLint[2]) * 8);
-
- GLuint u = (GLuint)(i * 4);
- memcpy(&indices[i * 6],
- ((GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}),
- sizeof(GLuint) * 6);
- }
-}
-
-// TODO(yshui) make use of reg_visible
-void gl_compose(backend_t *base, void *image_data,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_tgt, const region_t *reg_visible attr_unused) {
- auto gd = (struct gl_data *)base;
- struct backend_image *img = image_data;
- auto inner = (struct gl_texture *)img->inner;
-
- // Painting
- int nrects;
- const rect_t *rects;
- rects = pixman_region32_rectangles((region_t *)reg_tgt, &nrects);
- if (!nrects) {
- // Nothing to paint
- return;
- }
-
- // Until we start to use glClipControl, reg_tgt, dst_x and dst_y and
- // in a different coordinate system than the one OpenGL uses.
- // OpenGL window coordinate (or NDC) has the origin at the lower left of the
- // screen, with y axis pointing up; Xorg has the origin at the upper left of the
- // screen, with y axis pointing down. We have to do some coordinate conversion in
- // this function
-
- auto coord = ccalloc(nrects * 16, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- x_rect_to_coords(nrects, rects, dst_x1, dst_y1, inner->height, gd->height,
- inner->y_inverted, coord, indices);
-
- // Interpolate the texture coordinates into the specified range
- for (unsigned int i = 2; i < 16; i+=4) {
- coord[i+0] = lerp_range(0, dst_x2 - dst_x1, 0, inner->width, coord[i+0]);
- coord[i+1] = lerp_range(0, dst_y2 - dst_y1, 0, inner->height, coord[i+1]);
- }
-
- _gl_compose(base, img, gd->back_fbo, coord, indices, nrects);
-
- free(indices);
- free(coord);
-}
-
-/**
- * Blur contents in a particular region.
- */
-bool gl_kernel_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
- const GLuint vao[2], const int vao_nelems[2]) {
- auto bctx = (struct gl_blur_context *)ctx;
- auto gd = (struct gl_data *)base;
-
- int dst_y_fb_coord = bctx->fb_height - extent->y2;
-
- int curr = 0;
- for (int i = 0; i < bctx->npasses; ++i) {
- const gl_blur_shader_t *p = &bctx->blur_shader[i];
- assert(p->prog);
-
- assert(bctx->blur_textures[curr]);
-
- // The origin to use when sampling from the source texture
- GLint texorig_x = extent->x1, texorig_y = dst_y_fb_coord;
- GLint tex_width, tex_height;
- GLuint src_texture;
-
- if (i == 0) {
- src_texture = gd->back_texture;
- tex_width = gd->width;
- tex_height = gd->height;
- } else {
- src_texture = bctx->blur_textures[curr];
- auto src_size = bctx->texture_sizes[curr];
- tex_width = src_size.width;
- tex_height = src_size.height;
- }
-
- glBindTexture(GL_TEXTURE_2D, src_texture);
- glUseProgram(p->prog);
- glUniform2f(p->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
- 1.0f / (GLfloat)tex_height);
-
- // The number of indices in the selected vertex array
- GLsizei nelems;
-
- if (i < bctx->npasses - 1) {
- assert(bctx->blur_fbos[0]);
- assert(bctx->blur_textures[!curr]);
-
- // not last pass, draw into framebuffer, with resized regions
- glBindVertexArray(vao[1]);
- nelems = vao_nelems[1];
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[0]);
-
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, bctx->blur_textures[!curr], 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
- return false;
- }
-
- glUniform1f(p->unifm_opacity, 1.0);
- } else {
- // last pass, draw directly into the back buffer, with origin
- // regions
- glBindVertexArray(vao[0]);
- nelems = vao_nelems[0];
- glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
-
- glUniform1f(p->unifm_opacity, (float)opacity);
- }
-
- glUniform2f(p->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
- glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
-
- // XXX use multiple draw calls is probably going to be slow than
- // just simply blur the whole area.
-
- curr = !curr;
- }
-
- return true;
-}
-
-bool gl_dual_kawase_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
- const GLuint vao[2], const int vao_nelems[2]) {
- auto bctx = (struct gl_blur_context *)ctx;
- auto gd = (struct gl_data *)base;
-
- int dst_y_fb_coord = bctx->fb_height - extent->y2;
-
- int iterations = bctx->blur_texture_count;
- int scale_factor = 1;
-
- // Kawase downsample pass
- const gl_blur_shader_t *down_pass = &bctx->blur_shader[0];
- assert(down_pass->prog);
- glUseProgram(down_pass->prog);
-
- glUniform2f(down_pass->texorig_loc, (GLfloat)extent->x1, (GLfloat)dst_y_fb_coord);
-
- for (int i = 0; i < iterations; ++i) {
- // Scale output width / height by half in each iteration
- scale_factor <<= 1;
-
- GLuint src_texture;
- int tex_width, tex_height;
-
- if (i == 0) {
- // first pass: copy from back buffer
- src_texture = gd->back_texture;
- tex_width = gd->width;
- tex_height = gd->height;
- } else {
- // copy from previous pass
- src_texture = bctx->blur_textures[i - 1];
- auto src_size = bctx->texture_sizes[i - 1];
- tex_width = src_size.width;
- tex_height = src_size.height;
- }
-
- assert(src_texture);
- assert(bctx->blur_fbos[i]);
-
- glBindTexture(GL_TEXTURE_2D, src_texture);
- glBindVertexArray(vao[1]);
- auto nelems = vao_nelems[1];
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
-
- glUniform1f(down_pass->scale_loc, (GLfloat)scale_factor);
-
- glUniform2f(down_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
- 1.0f / (GLfloat)tex_height);
-
- glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
- }
-
- // Kawase upsample pass
- const gl_blur_shader_t *up_pass = &bctx->blur_shader[1];
- assert(up_pass->prog);
- glUseProgram(up_pass->prog);
-
- glUniform2f(up_pass->texorig_loc, (GLfloat)extent->x1, (GLfloat)dst_y_fb_coord);
-
- for (int i = iterations - 1; i >= 0; --i) {
- // Scale output width / height back by two in each iteration
- scale_factor >>= 1;
-
- const GLuint src_texture = bctx->blur_textures[i];
- assert(src_texture);
-
- // Calculate normalized half-width/-height of a src pixel
- auto src_size = bctx->texture_sizes[i];
- int tex_width = src_size.width;
- int tex_height = src_size.height;
-
- // The number of indices in the selected vertex array
- GLsizei nelems;
-
- glBindTexture(GL_TEXTURE_2D, src_texture);
- if (i > 0) {
- assert(bctx->blur_fbos[i - 1]);
-
- // not last pass, draw into next framebuffer
- glBindVertexArray(vao[1]);
- nelems = vao_nelems[1];
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i - 1]);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
-
- glUniform1f(up_pass->unifm_opacity, (GLfloat)1);
- } else {
- // last pass, draw directly into the back buffer
- glBindVertexArray(vao[0]);
- nelems = vao_nelems[0];
- glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
-
- glUniform1f(up_pass->unifm_opacity, (GLfloat)opacity);
- }
-
- glUniform1f(up_pass->scale_loc, (GLfloat)scale_factor);
- glUniform2f(up_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
- 1.0f / (GLfloat)tex_height);
-
- glDrawElements(GL_TRIANGLES, nelems, GL_UNSIGNED_INT, NULL);
- }
-
- return true;
-}
-
-bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur,
- const region_t *reg_visible attr_unused) {
- auto bctx = (struct gl_blur_context *)ctx;
- auto gd = (struct gl_data *)base;
-
- bool ret = false;
-
- if (gd->width != bctx->fb_width || gd->height != bctx->fb_height) {
- // Resize the temporary textures used for blur in case the root
- // size changed
- bctx->fb_width = gd->width;
- bctx->fb_height = gd->height;
-
- for (int i = 0; i < bctx->blur_texture_count; ++i) {
- auto tex_size = bctx->texture_sizes + i;
- if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
- // Use smaller textures for each iteration (quarter of the
- // previous texture)
- tex_size->width = 1 + ((bctx->fb_width - 1) >> (i + 1));
- tex_size->height = 1 + ((bctx->fb_height - 1) >> (i + 1));
- } else {
- tex_size->width = bctx->fb_width;
- tex_size->height = bctx->fb_height;
- }
-
- glBindTexture(GL_TEXTURE_2D, bctx->blur_textures[i]);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_size->width,
- tex_size->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-
- if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
- // Attach texture to FBO target
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
- GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- bctx->blur_textures[i], 0);
- if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- return false;
- }
- }
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- }
-
- // Remainder: regions are in Xorg coordinates
- auto reg_blur_resized =
- resize_region(reg_blur, bctx->resize_width, bctx->resize_height);
- const rect_t *extent = pixman_region32_extents((region_t *)reg_blur),
- *extent_resized = pixman_region32_extents(&reg_blur_resized);
- int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
- if (width == 0 || height == 0) {
- return true;
- }
-
- int nrects, nrects_resized;
- const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects),
- *rects_resized =
- pixman_region32_rectangles(&reg_blur_resized, &nrects_resized);
- if (!nrects || !nrects_resized) {
- return true;
- }
-
- auto coord = ccalloc(nrects * 16, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- x_rect_to_coords(nrects, rects, extent_resized->x1, extent_resized->y2,
- bctx->fb_height, gd->height, false, coord, indices);
-
- auto coord_resized = ccalloc(nrects_resized * 16, GLint);
- auto indices_resized = ccalloc(nrects_resized * 6, GLuint);
- x_rect_to_coords(nrects_resized, rects_resized, extent_resized->x1,
- extent_resized->y2, bctx->fb_height, bctx->fb_height, false,
- coord_resized, indices_resized);
- pixman_region32_fini(&reg_blur_resized);
-
- GLuint vao[2];
- glGenVertexArrays(2, vao);
- GLuint bo[4];
- glGenBuffers(4, bo);
-
- glBindVertexArray(vao[0]);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
- indices, GL_STATIC_DRAW);
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
-
- glBindVertexArray(vao[1]);
- glBindBuffer(GL_ARRAY_BUFFER, bo[2]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[3]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord_resized) * nrects_resized * 16,
- coord_resized, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- (long)sizeof(*indices_resized) * nrects_resized * 6, indices_resized,
- GL_STATIC_DRAW);
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
-
- int vao_nelems[2] = {nrects * 6, nrects_resized * 6};
-
- if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
- ret = gl_dual_kawase_blur(base, opacity, ctx, extent_resized, vao, vao_nelems);
- } else {
- ret = gl_kernel_blur(base, opacity, ctx, extent_resized, vao, vao_nelems);
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(4, bo);
- glBindVertexArray(0);
- glDeleteVertexArrays(2, vao);
- glUseProgram(0);
-
- free(indices);
- free(coord);
- free(indices_resized);
- free(coord_resized);
-
- gl_check_err();
- return ret;
-}
-
-// clang-format off
-const char *vertex_shader = GLSL(330,
- uniform mat4 projection;
- uniform float scale = 1.0;
- uniform vec2 texorig;
- layout(location = 0) in vec2 coord;
- layout(location = 1) in vec2 in_texcoord;
- out vec2 texcoord;
- void main() {
- gl_Position = projection * vec4(coord, 0, scale);
- texcoord = in_texcoord + texorig;
- }
-);
-// clang-format on
-
-/**
- * Load a GLSL main program from shader strings.
- */
-static int gl_win_shader_from_string(const char *vshader_str, const char *fshader_str,
- gl_win_shader_t *ret) {
- // Build program
- ret->prog = gl_create_program_from_str(vshader_str, fshader_str);
- if (!ret->prog) {
- log_error("Failed to create GLSL program.");
- return -1;
- }
-
- // Get uniform addresses
- ret->unifm_opacity = glGetUniformLocationChecked(ret->prog, "opacity");
- ret->unifm_invert_color = glGetUniformLocationChecked(ret->prog, "invert_color");
- ret->unifm_tex = glGetUniformLocationChecked(ret->prog, "tex");
- ret->unifm_dim = glGetUniformLocationChecked(ret->prog, "dim");
- ret->unifm_brightness = glGetUniformLocationChecked(ret->prog, "brightness");
- ret->unifm_max_brightness =
- glGetUniformLocationChecked(ret->prog, "max_brightness");
-
- gl_check_err();
-
- return true;
-}
-
-/**
- * Callback to run on root window size change.
- */
-void gl_resize(struct gl_data *gd, int width, int height) {
- GLint viewport_dimensions[2];
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
-
- gd->height = height;
- gd->width = width;
-
- assert(viewport_dimensions[0] >= gd->width);
- assert(viewport_dimensions[1] >= gd->height);
-
- glBindTexture(GL_TEXTURE_2D, gd->back_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR,
- GL_UNSIGNED_BYTE, NULL);
-
- gl_check_err();
-}
-
-// clang-format off
-static const char dummy_frag[] = GLSL(330,
- uniform sampler2D tex;
- in vec2 texcoord;
- void main() {
- gl_FragColor = texelFetch(tex, ivec2(texcoord.xy), 0);
- }
-);
-
-static const char fill_frag[] = GLSL(330,
- uniform vec4 color;
- void main() {
- gl_FragColor = color;
- }
-);
-
-static const char fill_vert[] = GLSL(330,
- layout(location = 0) in vec2 in_coord;
- uniform mat4 projection;
- void main() {
- gl_Position = projection * vec4(in_coord, 0, 1);
- }
-);
-
-static const char interpolating_frag[] = GLSL(330,
- uniform sampler2D tex;
- in vec2 texcoord;
- void main() {
- gl_FragColor = vec4(texture2D(tex, vec2(texcoord.xy), 0).rgb, 1);
- }
-);
-
-static const char interpolating_vert[] = GLSL(330,
- uniform mat4 projection;
- uniform vec2 texsize;
- layout(location = 0) in vec2 in_coord;
- layout(location = 1) in vec2 in_texcoord;
- out vec2 texcoord;
- void main() {
- gl_Position = projection * vec4(in_coord, 0, 1);
- texcoord = in_texcoord / texsize;
- }
-);
-// clang-format on
-
-/// Fill a given region in bound framebuffer.
-/// @param[in] y_inverted whether the y coordinates in `clip` should be inverted
-static void _gl_fill(backend_t *base, struct color c, const region_t *clip, GLuint target,
- int height, bool y_inverted) {
- static const GLuint fill_vert_in_coord_loc = 0;
- int nrects;
- const rect_t *rect = pixman_region32_rectangles((region_t *)clip, &nrects);
- auto gd = (struct gl_data *)base;
-
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-
- GLuint bo[2];
- glGenBuffers(2, bo);
- glUseProgram(gd->fill_shader.prog);
- glUniform4f(gd->fill_shader.color_loc, (GLfloat)c.red, (GLfloat)c.green,
- (GLfloat)c.blue, (GLfloat)c.alpha);
- glEnableVertexAttribArray(fill_vert_in_coord_loc);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
-
- auto coord = ccalloc(nrects * 8, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- for (int i = 0; i < nrects; i++) {
- GLint y1 = y_inverted ? height - rect[i].y2 : rect[i].y1,
- y2 = y_inverted ? height - rect[i].y1 : rect[i].y2;
- // clang-format off
- memcpy(&coord[i * 8],
- ((GLint[][2]){
- {rect[i].x1, y1}, {rect[i].x2, y1},
- {rect[i].x2, y2}, {rect[i].x1, y2}}),
- sizeof(GLint[2]) * 4);
- // clang-format on
- indices[i * 6 + 0] = (GLuint)i * 4 + 0;
- indices[i * 6 + 1] = (GLuint)i * 4 + 1;
- indices[i * 6 + 2] = (GLuint)i * 4 + 2;
- indices[i * 6 + 3] = (GLuint)i * 4 + 2;
- indices[i * 6 + 4] = (GLuint)i * 4 + 3;
- indices[i * 6 + 5] = (GLuint)i * 4 + 0;
- }
- glBufferData(GL_ARRAY_BUFFER, nrects * 8 * (long)sizeof(*coord), coord, GL_STREAM_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, nrects * 6 * (long)sizeof(*indices),
- indices, GL_STREAM_DRAW);
-
- glVertexAttribPointer(fill_vert_in_coord_loc, 2, GL_INT, GL_FALSE,
- sizeof(*coord) * 2, (void *)0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
- glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(fill_vert_in_coord_loc);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- glDeleteBuffers(2, bo);
- free(indices);
- free(coord);
-
- gl_check_err();
-}
-
-void gl_fill(backend_t *base, struct color c, const region_t *clip) {
- auto gd = (struct gl_data *)base;
- return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
-}
-
-static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
- auto gd = (struct gl_data *)base;
- gd->release_user_data(base, inner);
- assert(inner->user_data == NULL);
-
- glDeleteTextures(1, &inner->texture);
- glDeleteTextures(2, inner->auxiliary_texture);
- free(inner);
- gl_check_err();
-}
-
-void gl_release_image(backend_t *base, void *image_data) {
- struct backend_image *wd = image_data;
- auto inner = (struct gl_texture *)wd->inner;
- inner->refcount--;
- assert(inner->refcount >= 0);
- if (inner->refcount == 0) {
- gl_release_image_inner(base, inner);
- }
- free(wd);
-}
-
-static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
- if (shader->prog) {
- glDeleteProgram(shader->prog);
- }
-
- shader->prog = 0;
-}
-
-void gl_destroy_blur_context(backend_t *base attr_unused, void *ctx) {
- auto bctx = (struct gl_blur_context *)ctx;
- // Free GLSL shaders/programs
- for (int i = 0; i < bctx->npasses; ++i) {
- gl_free_blur_shader(&bctx->blur_shader[i]);
- }
- free(bctx->blur_shader);
-
- if (bctx->blur_texture_count && bctx->blur_textures) {
- glDeleteTextures(bctx->blur_texture_count, bctx->blur_textures);
- free(bctx->blur_textures);
- }
- if (bctx->blur_texture_count && bctx->texture_sizes) {
- free(bctx->texture_sizes);
- }
- if (bctx->blur_fbo_count && bctx->blur_fbos) {
- glDeleteFramebuffers(bctx->blur_fbo_count, bctx->blur_fbos);
- free(bctx->blur_fbos);
- }
-
- bctx->blur_texture_count = 0;
- bctx->blur_fbo_count = 0;
-
- free(bctx);
-
- gl_check_err();
-}
-
-/**
- * Initialize GL blur filters.
- */
-bool gl_create_kernel_blur_context(void *blur_context, GLfloat *projection,
- enum blur_method method, void *args) {
- bool success = false;
- auto ctx = (struct gl_blur_context *)blur_context;
-
- struct conv **kernels;
-
- int nkernels;
- ctx->method = BLUR_METHOD_KERNEL;
- if (method == BLUR_METHOD_KERNEL) {
- nkernels = ((struct kernel_blur_args *)args)->kernel_count;
- kernels = ((struct kernel_blur_args *)args)->kernels;
- } else {
- kernels = generate_blur_kernel(method, args, &nkernels);
- }
-
- if (!nkernels) {
- ctx->method = BLUR_METHOD_NONE;
- return true;
- }
-
- // Specify required textures and FBOs
- ctx->blur_texture_count = 2;
- ctx->blur_fbo_count = 1;
-
- ctx->blur_shader = ccalloc(max2(2, nkernels), gl_blur_shader_t);
-
- char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
- // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
- // Thanks to hiciu for reporting.
- setlocale(LC_NUMERIC, "C");
-
- // clang-format off
- static const char *FRAG_SHADER_BLUR = GLSL(330,
- %s\n // other extension pragmas
- uniform sampler2D tex_src;
- uniform vec2 pixel_norm;
- uniform float opacity;
- in vec2 texcoord;
- out vec4 out_color;
- void main() {
- vec2 uv = texcoord * pixel_norm;
- vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
- %s //body of the convolution
- out_color = sum / float(%.7g) * opacity;
- }
- );
- static const char *FRAG_SHADER_BLUR_ADD = QUOTE(
- sum += float(%.7g) * texture2D(tex_src, uv + pixel_norm * vec2(%.7g, %.7g));
- );
- // clang-format on
-
- const char *shader_add = FRAG_SHADER_BLUR_ADD;
- char *extension = strdup("");
-
- for (int i = 0; i < nkernels; i++) {
- auto kern = kernels[i];
- // Build shader
- int width = kern->w, height = kern->h;
- int nele = width * height;
- // '%.7g' is at most 14 characters, inserted 3 times
- size_t body_len = (strlen(shader_add) + 42) * (uint)nele;
- char *shader_body = ccalloc(body_len, char);
- char *pc = shader_body;
-
- // Make use of the linear interpolation hardware by sampling 2 pixels with
- // one texture access by sampling between both pixels based on their
- // relative weight. Easiest done in a single dimension as 2D bilinear
- // filtering would raise additional constraints on the kernels. Therefore
- // only use interpolation along the larger dimension.
- double sum = 0.0;
- if (width > height) {
- // use interpolation in x dimension (width)
- for (int j = 0; j < height; ++j) {
- for (int k = 0; k < width; k += 2) {
- double val1, val2;
- val1 = kern->data[j * width + k];
- val2 = (k + 1 < width)
- ? kern->data[j * width + k + 1]
- : 0;
-
- double combined_weight = val1 + val2;
- if (combined_weight == 0) {
- continue;
- }
- sum += combined_weight;
-
- double offset_x =
- k + (val2 / combined_weight) - (width / 2);
- double offset_y = j - (height / 2);
- pc += snprintf(
- pc, body_len - (ulong)(pc - shader_body),
- shader_add, combined_weight, offset_x, offset_y);
- assert(pc < shader_body + body_len);
- }
- }
- } else {
- // use interpolation in y dimension (height)
- for (int j = 0; j < height; j += 2) {
- for (int k = 0; k < width; ++k) {
- double val1, val2;
- val1 = kern->data[j * width + k];
- val2 = (j + 1 < height)
- ? kern->data[(j + 1) * width + k]
- : 0;
-
- double combined_weight = val1 + val2;
- if (combined_weight == 0) {
- continue;
- }
- sum += combined_weight;
-
- double offset_x = k - (width / 2);
- double offset_y =
- j + (val2 / combined_weight) - (height / 2);
- pc += snprintf(
- pc, body_len - (ulong)(pc - shader_body),
- shader_add, combined_weight, offset_x, offset_y);
- assert(pc < shader_body + body_len);
- }
- }
- }
-
- auto pass = ctx->blur_shader + i;
- size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) +
- strlen(shader_body) + 10 /* sum */ +
- 1 /* null terminator */;
- char *shader_str = ccalloc(shader_len, char);
- auto real_shader_len = snprintf(shader_str, shader_len, FRAG_SHADER_BLUR,
- extension, shader_body, sum);
- CHECK(real_shader_len >= 0);
- CHECK((size_t)real_shader_len < shader_len);
- free(shader_body);
-
- // Build program
- pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
- free(shader_str);
- if (!pass->prog) {
- log_error("Failed to create GLSL program.");
- success = false;
- goto out;
- }
- glBindFragDataLocation(pass->prog, 0, "out_color");
-
- // Get uniform addresses
- pass->unifm_pixel_norm =
- glGetUniformLocationChecked(pass->prog, "pixel_norm");
- pass->unifm_opacity = glGetUniformLocationChecked(pass->prog, "opacity");
- pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
-
- // Setup projection matrix
- glUseProgram(pass->prog);
- int pml = glGetUniformLocationChecked(pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
-
- ctx->resize_width += kern->w / 2;
- ctx->resize_height += kern->h / 2;
- }
-
- if (nkernels == 1) {
- // Generate an extra null pass so we don't need special code path for
- // the single pass case
- auto pass = &ctx->blur_shader[1];
- pass->prog = gl_create_program_from_str(vertex_shader, dummy_frag);
- pass->unifm_pixel_norm = -1;
- pass->unifm_opacity = -1;
- pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
-
- // Setup projection matrix
- glUseProgram(pass->prog);
- int pml = glGetUniformLocationChecked(pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
-
- ctx->npasses = 2;
- } else {
- ctx->npasses = nkernels;
- }
-
- success = true;
-out:
- if (method != BLUR_METHOD_KERNEL) {
- // We generated the blur kernels, so we need to free them
- for (int i = 0; i < nkernels; i++) {
- free(kernels[i]);
- }
- free(kernels);
- }
-
- free(extension);
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
-
- return success;
-}
-
-bool gl_create_dual_kawase_blur_context(void *blur_context, GLfloat *projection,
- enum blur_method method, void *args) {
- bool success = false;
- auto ctx = (struct gl_blur_context *)blur_context;
-
- ctx->method = method;
-
- auto blur_params = generate_dual_kawase_params(args);
-
- // Specify required textures and FBOs
- ctx->blur_texture_count = blur_params->iterations;
- ctx->blur_fbo_count = blur_params->iterations;
-
- ctx->resize_width += blur_params->expand;
- ctx->resize_height += blur_params->expand;
-
- ctx->npasses = 2;
- ctx->blur_shader = ccalloc(ctx->npasses, gl_blur_shader_t);
-
- char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
- // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
- // Thanks to hiciu for reporting.
- setlocale(LC_NUMERIC, "C");
-
- // Dual-kawase downsample shader / program
- auto down_pass = ctx->blur_shader;
- {
- // clang-format off
- static const char *FRAG_SHADER_DOWN = GLSL(330,
- uniform sampler2D tex_src;
- uniform float scale = 1.0;
- uniform vec2 pixel_norm;
- in vec2 texcoord;
- out vec4 out_color;
- void main() {
- vec2 offset = %.7g * pixel_norm;
- vec2 uv = texcoord * pixel_norm * (2.0 / scale);
- vec4 sum = texture2D(tex_src, uv) * 4.0;
- sum += texture2D(tex_src, uv - vec2(0.5, 0.5) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, 0.5) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, -0.5) * offset);
- sum += texture2D(tex_src, uv - vec2(0.5, -0.5) * offset);
- out_color = sum / 8.0;
- }
- );
- // clang-format on
-
- // Build shader
- size_t shader_len =
- strlen(FRAG_SHADER_DOWN) + 10 /* offset */ + 1 /* null terminator */;
- char *shader_str = ccalloc(shader_len, char);
- auto real_shader_len =
- snprintf(shader_str, shader_len, FRAG_SHADER_DOWN, blur_params->offset);
- CHECK(real_shader_len >= 0);
- CHECK((size_t)real_shader_len < shader_len);
-
- // Build program
- down_pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
- free(shader_str);
- if (!down_pass->prog) {
- log_error("Failed to create GLSL program.");
- success = false;
- goto out;
- }
- glBindFragDataLocation(down_pass->prog, 0, "out_color");
-
- // Get uniform addresses
- down_pass->unifm_pixel_norm =
- glGetUniformLocationChecked(down_pass->prog, "pixel_norm");
- down_pass->texorig_loc =
- glGetUniformLocationChecked(down_pass->prog, "texorig");
- down_pass->scale_loc =
- glGetUniformLocationChecked(down_pass->prog, "scale");
-
- // Setup projection matrix
- glUseProgram(down_pass->prog);
- int pml = glGetUniformLocationChecked(down_pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
- }
-
- // Dual-kawase upsample shader / program
- auto up_pass = ctx->blur_shader + 1;
- {
- // clang-format off
- static const char *FRAG_SHADER_UP = GLSL(330,
- uniform sampler2D tex_src;
- uniform float scale = 1.0;
- uniform vec2 pixel_norm;
- uniform float opacity;
- in vec2 texcoord;
- out vec4 out_color;
- void main() {
- vec2 offset = %.7g * pixel_norm;
- vec2 uv = texcoord * pixel_norm / (2 * scale);
- vec4 sum = texture2D(tex_src, uv + vec2(-1.0, 0.0) * offset);
- sum += texture2D(tex_src, uv + vec2(-0.5, 0.5) * offset) * 2.0;
- sum += texture2D(tex_src, uv + vec2(0.0, 1.0) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, 0.5) * offset) * 2.0;
- sum += texture2D(tex_src, uv + vec2(1.0, 0.0) * offset);
- sum += texture2D(tex_src, uv + vec2(0.5, -0.5) * offset) * 2.0;
- sum += texture2D(tex_src, uv + vec2(0.0, -1.0) * offset);
- sum += texture2D(tex_src, uv + vec2(-0.5, -0.5) * offset) * 2.0;
- out_color = sum / 12.0 * opacity;
- }
- );
- // clang-format on
-
- // Build shader
- size_t shader_len =
- strlen(FRAG_SHADER_UP) + 10 /* offset */ + 1 /* null terminator */;
- char *shader_str = ccalloc(shader_len, char);
- auto real_shader_len =
- snprintf(shader_str, shader_len, FRAG_SHADER_UP, blur_params->offset);
- CHECK(real_shader_len >= 0);
- CHECK((size_t)real_shader_len < shader_len);
-
- // Build program
- up_pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
- free(shader_str);
- if (!up_pass->prog) {
- log_error("Failed to create GLSL program.");
- success = false;
- goto out;
- }
- glBindFragDataLocation(up_pass->prog, 0, "out_color");
-
- // Get uniform addresses
- up_pass->unifm_pixel_norm =
- glGetUniformLocationChecked(up_pass->prog, "pixel_norm");
- up_pass->unifm_opacity =
- glGetUniformLocationChecked(up_pass->prog, "opacity");
- up_pass->texorig_loc =
- glGetUniformLocationChecked(up_pass->prog, "texorig");
- up_pass->scale_loc = glGetUniformLocationChecked(up_pass->prog, "scale");
-
- // Setup projection matrix
- glUseProgram(up_pass->prog);
- int pml = glGetUniformLocationChecked(up_pass->prog, "projection");
- glUniformMatrix4fv(pml, 1, false, projection);
- glUseProgram(0);
- }
-
- success = true;
-out:
- free(blur_params);
-
- if (!success) {
- ctx = NULL;
- }
-
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
-
- return success;
-}
-
-void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) {
- bool success;
- auto gd = (struct gl_data *)base;
-
- auto ctx = ccalloc(1, struct gl_blur_context);
-
- if (!method || method >= BLUR_METHOD_INVALID) {
- ctx->method = BLUR_METHOD_NONE;
- return ctx;
- }
-
- // Set projection matrix to gl viewport dimensions so we can use screen
- // coordinates for all vertices
- // Note: OpenGL matrices are column major
- GLint viewport_dimensions[2];
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
- GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
- {0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
- {0, 0, 0, 0},
- {-1, -1, 0, 1}};
-
- if (method == BLUR_METHOD_DUAL_KAWASE) {
- success = gl_create_dual_kawase_blur_context(ctx, projection_matrix[0],
- method, args);
- } else {
- success =
- gl_create_kernel_blur_context(ctx, projection_matrix[0], method, args);
- }
- if (!success || ctx->method == BLUR_METHOD_NONE) {
- goto out;
- }
-
- // Texture size will be defined by gl_blur
- ctx->blur_textures = ccalloc(ctx->blur_texture_count, GLuint);
- ctx->texture_sizes = ccalloc(ctx->blur_texture_count, struct texture_size);
- glGenTextures(ctx->blur_texture_count, ctx->blur_textures);
-
- for (int i = 0; i < ctx->blur_texture_count; ++i) {
- glBindTexture(GL_TEXTURE_2D, ctx->blur_textures[i]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- // Generate FBO and textures when needed
- ctx->blur_fbos = ccalloc(ctx->blur_fbo_count, GLuint);
- glGenFramebuffers(ctx->blur_fbo_count, ctx->blur_fbos);
-
- for (int i = 0; i < ctx->blur_fbo_count; ++i) {
- if (!ctx->blur_fbos[i]) {
- log_error("Failed to generate framebuffer objects for blur");
- success = false;
- goto out;
- }
- }
-
-out:
- if (!success) {
- gl_destroy_blur_context(&gd->base, ctx);
- ctx = NULL;
- }
-
- gl_check_err();
- return ctx;
-}
-
-void gl_get_blur_size(void *blur_context, int *width, int *height) {
- auto ctx = (struct gl_blur_context *)blur_context;
- *width = ctx->resize_width;
- *height = ctx->resize_height;
-}
-
-// clang-format off
-const char *win_shader_glsl = GLSL(330,
- uniform float opacity;
- uniform float dim;
- uniform bool invert_color;
- in vec2 texcoord;
- uniform sampler2D tex;
- uniform sampler2D brightness;
- uniform float max_brightness;
-
- void main() {
- vec4 c = texelFetch(tex, ivec2(texcoord), 0);
- if (invert_color) {
- c = vec4(c.aaa - c.rgb, c.a);
- }
- c = vec4(c.rgb * (1.0 - dim), c.a) * opacity;
-
- vec3 rgb_brightness = texelFetch(brightness, ivec2(0, 0), 0).rgb;
- // Ref: https://en.wikipedia.org/wiki/Relative_luminance
- float brightness = rgb_brightness.r * 0.21 +
- rgb_brightness.g * 0.72 +
- rgb_brightness.b * 0.07;
- if (brightness > max_brightness)
- c.rgb = c.rgb * (max_brightness / brightness);
-
- gl_FragColor = c;
- }
-);
-
-const char *present_vertex_shader = GLSL(330,
- uniform mat4 projection;
- layout(location = 0) in vec2 coord;
- out vec2 texcoord;
- void main() {
- gl_Position = projection * vec4(coord, 0, 1);
- texcoord = coord;
- }
-);
-// clang-format on
-
-bool gl_init(struct gl_data *gd, session_t *ps) {
- // Initialize GLX data structure
- glDisable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
-
- glEnable(GL_BLEND);
- // X pixmap is in premultiplied alpha, so we might just as well use it too.
- // Thanks to derhass for help.
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-
- // Initialize stencil buffer
- glDisable(GL_STENCIL_TEST);
- glStencilMask(0x1);
- glStencilFunc(GL_EQUAL, 0x1, 0x1);
-
- // Set gl viewport to the maximum supported size so we won't have to worry about
- // it later on when the screen is resized. The corresponding projection matrix can
- // be set now and won't have to be updated. Since fragments outside the target
- // buffer are skipped anyways, this should have no impact on performance.
- GLint viewport_dimensions[2];
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
- glViewport(0, 0, viewport_dimensions[0], viewport_dimensions[1]);
-
- // Clear screen
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
- glGenFramebuffers(1, &gd->back_fbo);
- glGenTextures(1, &gd->back_texture);
- if (!gd->back_fbo || !gd->back_texture) {
- log_error("Failed to generate a framebuffer object");
- return false;
- }
-
- glBindTexture(GL_TEXTURE_2D, gd->back_texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // Set projection matrix to gl viewport dimensions so we can use screen
- // coordinates for all vertices
- // Note: OpenGL matrices are column major
- GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
- {0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
- {0, 0, 0, 0},
- {-1, -1, 0, 1}};
-
- // Initialize shaders
- gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader);
- int pml = glGetUniformLocationChecked(gd->win_shader.prog, "projection");
- glUseProgram(gd->win_shader.prog);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- gd->fill_shader.prog = gl_create_program_from_str(fill_vert, fill_frag);
- gd->fill_shader.color_loc = glGetUniformLocation(gd->fill_shader.prog, "color");
- pml = glGetUniformLocationChecked(gd->fill_shader.prog, "projection");
- glUseProgram(gd->fill_shader.prog);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- gd->present_prog = gl_create_program_from_str(present_vertex_shader, dummy_frag);
- if (!gd->present_prog) {
- log_error("Failed to create the present shader");
- return false;
- }
- pml = glGetUniformLocationChecked(gd->present_prog, "projection");
- glUseProgram(gd->present_prog);
- glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- gd->brightness_shader.prog =
- gl_create_program_from_str(interpolating_vert, interpolating_frag);
- if (!gd->brightness_shader.prog) {
- log_error("Failed to create the brightness shader");
- return false;
- }
- pml = glGetUniformLocationChecked(gd->brightness_shader.prog, "projection");
- glUseProgram(gd->brightness_shader.prog);
- glUniform1i(glGetUniformLocationChecked(gd->brightness_shader.prog, "tex"), 0);
- glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
- glUseProgram(0);
-
- // Set up the size of the back texture
- gl_resize(gd, ps->root_width, ps->root_height);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- gd->back_texture, 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- if (!gl_check_fb_complete(GL_FRAMEBUFFER)) {
- return false;
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-
- gd->logger = gl_string_marker_logger_new();
- if (gd->logger) {
- log_add_target_tls(gd->logger);
- }
-
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- log_debug("GL_VENDOR = %s", vendor);
- if (strcmp(vendor, "NVIDIA Corporation") == 0) {
- log_info("GL vendor is NVIDIA, don't use glFinish");
- gd->is_nvidia = true;
- } else {
- gd->is_nvidia = false;
- }
-
- return true;
-}
-
-void gl_deinit(struct gl_data *gd) {
- gl_free_prog_main(&gd->win_shader);
-
- if (gd->logger) {
- log_remove_target_tls(gd->logger);
- gd->logger = NULL;
- }
-
- gl_check_err();
-}
-
-GLuint gl_new_texture(GLenum target) {
- GLuint texture;
- glGenTextures(1, &texture);
- if (!texture) {
- log_error("Failed to generate texture");
- return 0;
- }
-
- glBindTexture(target, texture);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glBindTexture(target, 0);
-
- return texture;
-}
-
-/// Actually duplicate a texture into a new one, if this texture is shared
-static inline void gl_image_decouple(backend_t *base, struct backend_image *img) {
- if (img->inner->refcount == 1) {
- return;
- }
- auto gd = (struct gl_data *)base;
- auto inner = (struct gl_texture *)img->inner;
- auto new_tex = ccalloc(1, struct gl_texture);
-
- new_tex->texture = gl_new_texture(GL_TEXTURE_2D);
- new_tex->y_inverted = true;
- new_tex->height = inner->height;
- new_tex->width = inner->width;
- new_tex->refcount = 1;
- new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
-
- glBindTexture(GL_TEXTURE_2D, new_tex->texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, new_tex->width, new_tex->height, 0,
- GL_BGRA, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- assert(gd->present_prog);
- glUseProgram(gd->present_prog);
- glBindTexture(GL_TEXTURE_2D, inner->texture);
-
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- new_tex->texture, 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- gl_check_fb_complete(GL_DRAW_FRAMEBUFFER);
-
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- // clang-format off
- GLint coord[] = {
- // top left
- 0, 0, // vertex coord
- 0, 0, // texture coord
-
- // top right
- new_tex->width, 0, // vertex coord
- new_tex->width, 0, // texture coord
-
- // bottom right
- new_tex->width, new_tex->height,
- new_tex->width, new_tex->height,
-
- // bottom left
- 0, new_tex->height,
- 0, new_tex->height,
- };
- // clang-format on
- GLuint indices[] = {0, 1, 2, 2, 3, 0};
-
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-
- GLuint bo[2];
- glGenBuffers(2, bo);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_STATIC_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
- GL_STATIC_DRAW);
-
- glEnableVertexAttribArray(vert_coord_loc);
- glEnableVertexAttribArray(vert_in_texcoord_loc);
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
- glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
- sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
-
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
-
- glDisableVertexAttribArray(vert_coord_loc);
- glDisableVertexAttribArray(vert_in_texcoord_loc);
- glBindVertexArray(0);
- glDeleteVertexArrays(1, &vao);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDeleteBuffers(2, bo);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDeleteFramebuffers(1, &fbo);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glUseProgram(0);
-
- gl_check_err();
-
- img->inner = (struct backend_image_inner_base *)new_tex;
- inner->refcount--;
-}
-
-static void gl_image_apply_alpha(backend_t *base, struct backend_image *img,
- const region_t *reg_op, double alpha) {
- // Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color
- auto inner = (struct gl_texture *)img->inner;
- glBlendFunc(GL_ZERO, GL_CONSTANT_ALPHA);
- glBlendColor(0, 0, 0, (GLclampf)alpha);
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- inner->texture, 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- _gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, 0, false);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- glDeleteFramebuffers(1, &fbo);
-}
-
-void gl_present(backend_t *base, const region_t *region) {
- auto gd = (struct gl_data *)base;
-
- int nrects;
- const rect_t *rect = pixman_region32_rectangles((region_t *)region, &nrects);
- auto coord = ccalloc(nrects * 8, GLint);
- auto indices = ccalloc(nrects * 6, GLuint);
- for (int i = 0; i < nrects; i++) {
- // clang-format off
- memcpy(&coord[i * 8],
- ((GLint[]){rect[i].x1, gd->height - rect[i].y2,
- rect[i].x2, gd->height - rect[i].y2,
- rect[i].x2, gd->height - rect[i].y1,
- rect[i].x1, gd->height - rect[i].y1}),
- sizeof(GLint) * 8);
- // clang-format on
-
- GLuint u = (GLuint)(i * 4);
- memcpy(&indices[i * 6],
- ((GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}),
- sizeof(GLuint) * 6);
- }
-
- glUseProgram(gd->present_prog);
- glBindTexture(GL_TEXTURE_2D, gd->back_texture);
-
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-
- GLuint bo[2];
- glGenBuffers(2, bo);
- glEnableVertexAttribArray(vert_coord_loc);
- glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
- glBufferData(GL_ARRAY_BUFFER, (long)sizeof(GLint) * nrects * 8, coord, GL_STREAM_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(GLuint) * nrects * 6, indices,
- GL_STREAM_DRAW);
-
- glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 2, NULL);
- glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
- glDeleteBuffers(2, bo);
- glDeleteVertexArrays(1, &vao);
-
- free(coord);
- free(indices);
-}
-
-bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
- const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
- struct backend_image *tex = image_data;
- switch (op) {
- case IMAGE_OP_APPLY_ALPHA:
- gl_image_decouple(base, tex);
- assert(tex->inner->refcount == 1);
- gl_image_apply_alpha(base, tex, reg_op, *(double *)arg);
- break;
- }
-
- return true;
-}
-
-bool gl_read_pixel(backend_t *base attr_unused, void *image_data, int x, int y,
- struct color *output) {
- struct backend_image *tex = image_data;
- auto inner = (struct gl_texture *)tex->inner;
- GLfloat color[4];
- glReadPixels(x, inner->y_inverted ? inner->height - y : y, 1, 1, GL_RGBA,
- GL_FLOAT, color);
- output->alpha = color[3];
- output->red = color[0];
- output->green = color[1];
- output->blue = color[2];
-
- bool ret = glGetError() == GL_NO_ERROR;
- gl_clear_err();
- return ret;
-}
diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h
deleted file mode 100644
index b1d93b0..0000000
--- a/src/backend/gl/gl_common.h
+++ /dev/null
@@ -1,220 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-#include <GL/gl.h>
-#include <GL/glext.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "backend/backend.h"
-#include "log.h"
-#include "region.h"
-
-#define CASESTRRET(s) \
- case s: return #s
-
-// Program and uniforms for window shader
-typedef struct {
- GLuint prog;
- GLint unifm_opacity;
- GLint unifm_invert_color;
- GLint unifm_tex;
- GLint unifm_dim;
- GLint unifm_brightness;
- GLint unifm_max_brightness;
-} gl_win_shader_t;
-
-// Program and uniforms for brightness shader
-typedef struct {
- GLuint prog;
-} gl_brightness_shader_t;
-
-// Program and uniforms for blur shader
-typedef struct {
- GLuint prog;
- GLint unifm_pixel_norm;
- GLint unifm_opacity;
- GLint texorig_loc;
- GLint scale_loc;
-} gl_blur_shader_t;
-
-typedef struct {
- GLuint prog;
- GLint color_loc;
-} gl_fill_shader_t;
-
-/// @brief Wrapper of a binded GLX texture.
-struct gl_texture {
- int refcount;
- bool has_alpha;
- GLuint texture;
- int width, height;
- bool y_inverted;
-
- // Textures for auxiliary uses.
- GLuint auxiliary_texture[2];
- void *user_data;
-};
-
-struct gl_data {
- backend_t base;
- // If we are using proprietary NVIDIA driver
- bool is_nvidia;
- // Height and width of the root window
- int height, width;
- gl_win_shader_t win_shader;
- gl_brightness_shader_t brightness_shader;
- gl_fill_shader_t fill_shader;
- GLuint back_texture, back_fbo;
- GLuint present_prog;
-
- /// Called when an gl_texture is decoupled from the texture it refers. Returns
- /// the decoupled user_data
- void *(*decouple_texture_user_data)(backend_t *base, void *user_data);
-
- /// Release the user data attached to a gl_texture
- void (*release_user_data)(backend_t *base, struct gl_texture *);
-
- struct log_target *logger;
-};
-
-typedef struct session session_t;
-
-#define GL_PROG_MAIN_INIT \
- { .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, }
-
-GLuint gl_create_shader(GLenum shader_type, const char *shader_str);
-GLuint gl_create_program(const GLuint *const shaders, int nshaders);
-GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
-
-/**
- * @brief Render a region with texture data.
- */
-void gl_compose(backend_t *, void *ptex,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_tgt, const region_t *reg_visible);
-
-void gl_resize(struct gl_data *, int width, int height);
-
-bool gl_init(struct gl_data *gd, session_t *);
-void gl_deinit(struct gl_data *gd);
-
-GLuint gl_new_texture(GLenum target);
-
-bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
- const region_t *reg_op, const region_t *reg_visible, void *arg);
-
-void gl_release_image(backend_t *base, void *image_data);
-
-void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
-
-bool gl_blur(backend_t *base, double opacity, void *, const region_t *reg_blur,
- const region_t *reg_visible);
-void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
-void gl_destroy_blur_context(backend_t *base, void *ctx);
-void gl_get_blur_size(void *blur_context, int *width, int *height);
-
-void gl_fill(backend_t *base, struct color, const region_t *clip);
-
-void gl_present(backend_t *base, const region_t *);
-bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output);
-
-static inline void gl_delete_texture(GLuint texture) {
- glDeleteTextures(1, &texture);
-}
-
-/**
- * Get a textual representation of an OpenGL error.
- */
-static inline const char *gl_get_err_str(GLenum err) {
- switch (err) {
- CASESTRRET(GL_NO_ERROR);
- CASESTRRET(GL_INVALID_ENUM);
- CASESTRRET(GL_INVALID_VALUE);
- CASESTRRET(GL_INVALID_OPERATION);
- CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION);
- CASESTRRET(GL_OUT_OF_MEMORY);
- CASESTRRET(GL_STACK_UNDERFLOW);
- CASESTRRET(GL_STACK_OVERFLOW);
- CASESTRRET(GL_FRAMEBUFFER_UNDEFINED);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
- CASESTRRET(GL_FRAMEBUFFER_UNSUPPORTED);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
- CASESTRRET(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
- }
- return NULL;
-}
-
-/**
- * Check for GLX error.
- *
- * http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
- */
-static inline void gl_check_err_(const char *func, int line) {
- GLenum err = GL_NO_ERROR;
-
- while (GL_NO_ERROR != (err = glGetError())) {
- const char *errtext = gl_get_err_str(err);
- if (errtext) {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "GLX error at line %d: %s", line, errtext);
- } else {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "GLX error at line %d: %d", line, err);
- }
- }
-}
-
-static inline void gl_clear_err(void) {
- while (glGetError() != GL_NO_ERROR)
- ;
-}
-
-#define gl_check_err() gl_check_err_(__func__, __LINE__)
-
-/**
- * Check for GL framebuffer completeness.
- */
-static inline bool gl_check_fb_complete_(const char *func, int line, GLenum fb) {
- GLenum status = glCheckFramebufferStatus(fb);
-
- if (status == GL_FRAMEBUFFER_COMPLETE) {
- return true;
- }
-
- const char *stattext = gl_get_err_str(status);
- if (stattext) {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "Framebuffer attachment failed at line %d: %s", line, stattext);
- } else {
- log_printf(tls_logger, LOG_LEVEL_ERROR, func,
- "Framebuffer attachment failed at line %d: %d", line, status);
- }
-
- return false;
-}
-
-#define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb))
-
-/**
- * Check if a GLX extension exists.
- */
-static inline bool gl_has_extension(const char *ext) {
- int nexts = 0;
- glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
- for (int i = 0; i < nexts || !nexts; i++) {
- const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, (GLuint)i);
- if (exti == NULL) {
- break;
- }
- if (strcmp(ext, exti) == 0) {
- return true;
- }
- }
- gl_clear_err();
- log_info("Missing GL extension %s.", ext);
- return false;
-}
diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c
deleted file mode 100644
index 1397d19..0000000
--- a/src/backend/gl/glx.c
+++ /dev/null
@@ -1,648 +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
- * Copyright (c) 2019 Yuxuan Shui <[email protected]>
- * See LICENSE-mit for more information.
- *
- */
-
-#include <X11/Xlib-xcb.h>
-#include <assert.h>
-#include <limits.h>
-#include <pixman.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/composite.h>
-#include <xcb/xcb.h>
-
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "backend/gl/gl_common.h"
-#include "backend/gl/glx.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h"
-#include "picom.h"
-#include "region.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-struct _glx_pixmap {
- GLXPixmap glpixmap;
- xcb_pixmap_t pixmap;
- bool owned;
-};
-
-struct _glx_data {
- struct gl_data gl;
- Display *display;
- int screen;
- xcb_window_t target_win;
- GLXContext ctx;
-};
-
-#define glXGetFBConfigAttribChecked(a, b, attr, c) \
- do { \
- if (glXGetFBConfigAttrib(a, b, attr, c)) { \
- log_info("Cannot get FBConfig attribute " #attr); \
- continue; \
- } \
- } while (0)
-
-struct glx_fbconfig_info *glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) {
- log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size,
- m.blue_size, m.green_size, m.alpha_size, m.visual_depth);
-
- int ncfg;
- // clang-format off
- GLXFBConfig *cfg =
- glXChooseFBConfig(dpy, screen, (int[]){
- GLX_RENDER_TYPE, GLX_RGBA_BIT,
- GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
- GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
- GLX_X_RENDERABLE, true,
- GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, (GLint)GLX_DONT_CARE,
- GLX_BUFFER_SIZE, m.red_size + m.green_size +
- m.blue_size + m.alpha_size,
- GLX_RED_SIZE, m.red_size,
- GLX_BLUE_SIZE, m.blue_size,
- GLX_GREEN_SIZE, m.green_size,
- GLX_ALPHA_SIZE, m.alpha_size,
- GLX_STENCIL_SIZE, 0,
- GLX_DEPTH_SIZE, 0,
- 0
- }, &ncfg);
- // clang-format on
-
- int texture_tgts, y_inverted, texture_fmt;
- bool found = false;
- int min_cost = INT_MAX;
- GLXFBConfig ret;
- for (int i = 0; i < ncfg; i++) {
- int depthbuf, stencil, doublebuf, bufsize;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BUFFER_SIZE, &bufsize);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DEPTH_SIZE, &depthbuf);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_STENCIL_SIZE, &stencil);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DOUBLEBUFFER, &doublebuf);
- if (depthbuf + stencil + bufsize * (doublebuf + 1) >= min_cost) {
- continue;
- }
- int red, green, blue;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_RED_SIZE, &red);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BLUE_SIZE, &blue);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_GREEN_SIZE, &green);
- if (red != m.red_size || green != m.green_size || blue != m.blue_size) {
- // Color size doesn't match, this cannot work
- continue;
- }
-
- int rgb, rgba;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &rgb);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &rgba);
- if (!rgb && !rgba) {
- log_info("FBConfig is neither RGBA nor RGB, we cannot "
- "handle this setup.");
- continue;
- }
-
- int visual;
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_VISUAL_ID, &visual);
- if (m.visual_depth != -1 &&
- x_get_visual_depth(XGetXCBConnection(dpy), (xcb_visualid_t)visual) !=
- m.visual_depth) {
- // FBConfig and the correspondent X Visual might not have the same
- // depth. (e.g. 32 bit FBConfig with a 24 bit Visual). This is
- // quite common, seen in both open source and proprietary drivers.
- //
- // If the FBConfig has a matching depth but its visual doesn't, we
- // still cannot use it.
- continue;
- }
-
- // All check passed, we are using this one.
- found = true;
- ret = cfg[i];
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT,
- &texture_tgts);
- glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_Y_INVERTED_EXT, &y_inverted);
-
- // Prefer the texture format with matching alpha, with the other one as
- // fallback
- if (m.alpha_size) {
- texture_fmt = rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT
- : GLX_TEXTURE_FORMAT_RGB_EXT;
- } else {
- texture_fmt =
- rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
- }
- min_cost = depthbuf + stencil + bufsize * (doublebuf + 1);
- }
- free(cfg);
- if (!found) {
- return NULL;
- }
-
- auto info = cmalloc(struct glx_fbconfig_info);
- info->cfg = ret;
- info->texture_tgts = texture_tgts;
- info->texture_fmt = texture_fmt;
- info->y_inverted = y_inverted;
- return info;
-}
-
-/**
- * Free a glx_texture_t.
- */
-static void glx_release_image(backend_t *base, struct gl_texture *tex) {
- struct _glx_data *gd = (void *)base;
-
- struct _glx_pixmap *p = tex->user_data;
- // Release binding
- if (p->glpixmap && tex->texture) {
- glBindTexture(GL_TEXTURE_2D, tex->texture);
- glXReleaseTexImageEXT(gd->display, p->glpixmap, GLX_FRONT_LEFT_EXT);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- // Free GLX Pixmap
- if (p->glpixmap) {
- glXDestroyPixmap(gd->display, p->glpixmap);
- p->glpixmap = 0;
- }
-
- if (p->owned) {
- xcb_free_pixmap(base->c, p->pixmap);
- p->pixmap = XCB_NONE;
- }
-
- free(p);
- tex->user_data = NULL;
-}
-
-/**
- * Destroy GLX related resources.
- */
-void glx_deinit(backend_t *base) {
- struct _glx_data *gd = (void *)base;
-
- gl_deinit(&gd->gl);
-
- // Destroy GLX context
- if (gd->ctx) {
- glXMakeCurrent(gd->display, None, NULL);
- glXDestroyContext(gd->display, gd->ctx);
- gd->ctx = 0;
- }
-
- free(gd);
-}
-
-static void *glx_decouple_user_data(backend_t *base attr_unused, void *ud attr_unused) {
- auto ret = cmalloc(struct _glx_pixmap);
- ret->owned = false;
- ret->glpixmap = 0;
- ret->pixmap = 0;
- return ret;
-}
-
-static bool glx_set_swap_interval(int interval, Display *dpy, GLXDrawable drawable) {
- bool vsync_enabled = false;
- if (glxext.has_GLX_MESA_swap_control) {
- vsync_enabled = (glXSwapIntervalMESA((uint)interval) == 0);
- }
- if (!vsync_enabled && glxext.has_GLX_SGI_swap_control) {
- vsync_enabled = (glXSwapIntervalSGI(interval) == 0);
- }
- if (!vsync_enabled && glxext.has_GLX_EXT_swap_control) {
- // glXSwapIntervalEXT doesn't return if it's successful
- glXSwapIntervalEXT(dpy, drawable, interval);
- vsync_enabled = true;
- }
- return vsync_enabled;
-}
-
-/**
- * Initialize OpenGL.
- */
-static backend_t *glx_init(session_t *ps) {
- bool success = false;
- glxext_init(ps->dpy, ps->scr);
- auto gd = ccalloc(1, struct _glx_data);
- init_backend_base(&gd->gl.base, ps);
-
- gd->display = ps->dpy;
- gd->screen = ps->scr;
- gd->target_win = session_get_target_window(ps);
-
- XVisualInfo *pvis = NULL;
-
- // Check for GLX extension
- if (!ps->glx_exists) {
- log_error("No GLX extension.");
- goto end;
- }
-
- // Get XVisualInfo
- int nitems = 0;
- XVisualInfo vreq = {.visualid = ps->vis};
- pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
- if (!pvis) {
- log_error("Failed to acquire XVisualInfo for current visual.");
- goto end;
- }
-
- // Ensure the visual is double-buffered
- int value = 0;
- if (glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
- log_error("Root visual is not a GL visual.");
- goto end;
- }
-
- if (glXGetConfig(ps->dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
- log_error("Root visual lacks stencil buffer.");
- goto end;
- }
-
- if (glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
- log_error("Root visual is not a double buffered GL visual.");
- goto end;
- }
-
- if (glXGetConfig(ps->dpy, pvis, GLX_RGBA, &value) || !value) {
- log_error("Root visual is a color index visual, not supported");
- goto end;
- }
-
- if (!glxext.has_GLX_EXT_texture_from_pixmap) {
- log_error("GLX_EXT_texture_from_pixmap is not supported by your driver");
- goto end;
- }
-
- if (!glxext.has_GLX_ARB_create_context) {
- log_error("GLX_ARB_create_context is not supported by your driver");
- goto end;
- }
-
- // Find a fbconfig with visualid matching the one from the target win, so we can
- // be sure that the fbconfig is compatible with our target window.
- int ncfgs;
- GLXFBConfig *cfg = glXGetFBConfigs(gd->display, gd->screen, &ncfgs);
- bool found = false;
- for (int i = 0; i < ncfgs; i++) {
- int visualid;
- glXGetFBConfigAttribChecked(gd->display, cfg[i], GLX_VISUAL_ID, &visualid);
- if ((VisualID)visualid != pvis->visualid) {
- continue;
- }
-
- gd->ctx = glXCreateContextAttribsARB(ps->dpy, cfg[i], 0, true,
- (int[]){
- GLX_CONTEXT_MAJOR_VERSION_ARB,
- 3,
- GLX_CONTEXT_MINOR_VERSION_ARB,
- 3,
- GLX_CONTEXT_PROFILE_MASK_ARB,
- GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
- 0,
- });
- free(cfg);
-
- if (!gd->ctx) {
- log_error("Failed to get GLX context.");
- goto end;
- }
- found = true;
- break;
- }
-
- if (!found) {
- log_error("Couldn't find a suitable fbconfig for the target window");
- goto end;
- }
-
- // Attach GLX context
- GLXDrawable tgt = gd->target_win;
- if (!glXMakeCurrent(ps->dpy, tgt, gd->ctx)) {
- log_error("Failed to attach GLX context.");
- goto end;
- }
-
- if (!gl_init(&gd->gl, ps)) {
- log_error("Failed to setup OpenGL");
- goto end;
- }
-
- gd->gl.decouple_texture_user_data = glx_decouple_user_data;
- gd->gl.release_user_data = glx_release_image;
-
- if (ps->o.vsync) {
- if (!glx_set_swap_interval(1, ps->dpy, tgt)) {
- log_error("Failed to enable vsync.");
- }
- } else {
- glx_set_swap_interval(0, ps->dpy, tgt);
- }
-
- success = true;
-
-end:
- if (pvis) {
- XFree(pvis);
- }
-
- if (!success) {
- glx_deinit(&gd->gl.base);
- return NULL;
- }
-
- return &gd->gl.base;
-}
-
-static void *
-glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
- struct _glx_data *gd = (void *)base;
- struct _glx_pixmap *glxpixmap = NULL;
- // Retrieve pixmap parameters, if they aren't provided
- if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
- log_error("Requested depth %d higher than max possible depth %d.",
- fmt.visual_depth, OPENGL_MAX_DEPTH);
- return false;
- }
-
- if (fmt.visual_depth < 0) {
- log_error("Pixmap %#010x with invalid depth %d", pixmap, fmt.visual_depth);
- return false;
- }
-
- auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), NULL);
- if (!r) {
- log_error("Invalid pixmap %#010x", pixmap);
- return NULL;
- }
-
- log_trace("Binding pixmap %#010x", pixmap);
- auto wd = ccalloc(1, struct backend_image);
- wd->max_brightness = 1;
- auto inner = ccalloc(1, struct gl_texture);
- inner->width = wd->ewidth = r->width;
- inner->height = wd->eheight = r->height;
- wd->inner = (struct backend_image_inner_base *)inner;
- free(r);
-
- auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt);
- if (!fbcfg) {
- log_error("Couldn't find FBConfig with requested visual %x", fmt.visual);
- goto err;
- }
-
- // Choose a suitable texture target for our pixmap.
- // Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
- // of the bits in texture_tgts
- if (!(fbcfg->texture_tgts & GLX_TEXTURE_2D_BIT_EXT)) {
- log_error("Cannot bind pixmap to GL_TEXTURE_2D, giving up");
- goto err;
- }
-
- log_debug("depth %d, rgba %d", fmt.visual_depth,
- (fbcfg->texture_fmt == GLX_TEXTURE_FORMAT_RGBA_EXT));
-
- GLint attrs[] = {
- GLX_TEXTURE_FORMAT_EXT,
- fbcfg->texture_fmt,
- GLX_TEXTURE_TARGET_EXT,
- GLX_TEXTURE_2D_EXT,
- 0,
- };
-
- inner->y_inverted = fbcfg->y_inverted;
-
- glxpixmap = cmalloc(struct _glx_pixmap);
- glxpixmap->pixmap = pixmap;
- glxpixmap->glpixmap = glXCreatePixmap(gd->display, fbcfg->cfg, pixmap, attrs);
- glxpixmap->owned = owned;
- free(fbcfg);
-
- if (!glxpixmap->glpixmap) {
- log_error("Failed to create glpixmap for pixmap %#010x", pixmap);
- goto err;
- }
-
- log_trace("GLXPixmap %#010lx", glxpixmap->glpixmap);
-
- // Create texture
- inner->user_data = glxpixmap;
- inner->texture = gl_new_texture(GL_TEXTURE_2D);
- inner->has_alpha = fmt.alpha_size != 0;
- wd->opacity = 1;
- wd->color_inverted = false;
- wd->dim = 0;
- wd->inner->refcount = 1;
- glBindTexture(GL_TEXTURE_2D, inner->texture);
- glXBindTexImageEXT(gd->display, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- gl_check_err();
- return wd;
-err:
- if (glxpixmap && glxpixmap->glpixmap) {
- glXDestroyPixmap(gd->display, glxpixmap->glpixmap);
- }
- free(glxpixmap);
-
- if (owned) {
- xcb_free_pixmap(base->c, pixmap);
- }
- free(wd);
- return NULL;
-}
-
-static void glx_present(backend_t *base, const region_t *region attr_unused) {
- struct _glx_data *gd = (void *)base;
- gl_present(base, region);
- glXSwapBuffers(gd->display, gd->target_win);
- if (!gd->gl.is_nvidia) {
- glFinish();
- }
-}
-
-static int glx_buffer_age(backend_t *base) {
- if (!glxext.has_GLX_EXT_buffer_age) {
- return -1;
- }
-
- struct _glx_data *gd = (void *)base;
- unsigned int val;
- glXQueryDrawable(gd->display, gd->target_win, GLX_BACK_BUFFER_AGE_EXT, &val);
- return (int)val ?: -1;
-}
-
-static void glx_diagnostics(backend_t *base) {
- struct _glx_data *gd = (void *)base;
- bool warn_software_rendering = false;
- const char *software_renderer_names[] = {"llvmpipe", "SWR", "softpipe"};
- auto glx_vendor = glXGetClientString(gd->display, GLX_VENDOR);
- printf("* Driver vendors:\n");
- printf(" * GLX: %s\n", glx_vendor);
- printf(" * GL: %s\n", glGetString(GL_VENDOR));
-
- auto gl_renderer = (const char *)glGetString(GL_RENDERER);
- printf("* GL renderer: %s\n", gl_renderer);
- if (strcmp(glx_vendor, "Mesa Project and SGI")) {
- for (size_t i = 0; i < ARR_SIZE(software_renderer_names); i++) {
- if (strstr(gl_renderer, software_renderer_names[i]) != NULL) {
- warn_software_rendering = true;
- break;
- }
- }
- }
-
-#ifdef GLX_MESA_query_renderer
- if (glxext.has_GLX_MESA_query_renderer) {
- unsigned int accelerated = 0;
- glXQueryCurrentRendererIntegerMESA(GLX_RENDERER_ACCELERATED_MESA, &accelerated);
- printf("* Accelerated: %d\n", accelerated);
-
- // Trust GLX_MESA_query_renderer when it's available
- warn_software_rendering = (accelerated == 0);
- }
-#endif
-
- if (warn_software_rendering) {
- printf("\n(You are using a software renderer. Unless you are doing this\n"
- "intentionally, this means you don't have a graphics driver\n"
- "properly installed. Performance will suffer. Please fix this\n"
- "before reporting your issue.)\n");
- }
-}
-
-struct backend_operations glx_ops = {
- .init = glx_init,
- .deinit = glx_deinit,
- .bind_pixmap = glx_bind_pixmap,
- .release_image = gl_release_image,
- .compose = gl_compose,
- .image_op = gl_image_op,
- .set_image_property = default_set_image_property,
- .read_pixel = gl_read_pixel,
- .clone_image = default_clone_image,
- .blur = gl_blur,
- .is_image_transparent = default_is_image_transparent,
- .present = glx_present,
- .buffer_age = glx_buffer_age,
- .render_shadow = default_backend_render_shadow,
- .fill = gl_fill,
- .create_blur_context = gl_create_blur_context,
- .destroy_blur_context = gl_destroy_blur_context,
- .get_blur_size = gl_get_blur_size,
- .diagnostics = glx_diagnostics,
- .max_buffer_age = 5, // Why?
-};
-
-/**
- * Check if a GLX extension exists.
- */
-static inline bool glx_has_extension(Display *dpy, int screen, const char *ext) {
- const char *glx_exts = glXQueryExtensionsString(dpy, screen);
- if (!glx_exts) {
- log_error("Failed get GLX extension list.");
- return false;
- }
-
- auto inlen = strlen(ext);
- const char *curr = glx_exts;
- bool match = false;
- while (curr && !match) {
- const char *end = strchr(curr, ' ');
- if (!end) {
- // Last extension string
- match = strcmp(ext, curr) == 0;
- } else if (curr + inlen == end) {
- // Length match, do match string
- match = strncmp(ext, curr, (unsigned long)(end - curr)) == 0;
- }
- curr = end ? end + 1 : NULL;
- }
-
- if (!match) {
- log_info("Missing GLX extension %s.", ext);
- } else {
- log_info("Found GLX extension %s.", ext);
- }
-
- return match;
-}
-
-struct glxext_info glxext = {0};
-PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
-PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
-PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
-PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
-PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
-PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
-PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
-PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
-PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
-PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
-
-#ifdef GLX_MESA_query_renderer
-PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA;
-#endif
-
-void glxext_init(Display *dpy, int screen) {
- if (glxext.initialized) {
- return;
- }
- glxext.initialized = true;
-#define check_ext(name) glxext.has_##name = glx_has_extension(dpy, screen, #name)
- check_ext(GLX_SGI_video_sync);
- check_ext(GLX_SGI_swap_control);
- check_ext(GLX_OML_sync_control);
- check_ext(GLX_MESA_swap_control);
- check_ext(GLX_EXT_swap_control);
- check_ext(GLX_EXT_texture_from_pixmap);
- check_ext(GLX_ARB_create_context);
- check_ext(GLX_EXT_buffer_age);
-#ifdef GLX_MESA_query_renderer
- check_ext(GLX_MESA_query_renderer);
-#endif
-#undef check_ext
-
-#define lookup(name) (name = (__typeof__(name))glXGetProcAddress((GLubyte *)#name))
- // Checking if the returned function pointer is NULL is not really necessary,
- // or maybe not even useful, since glXGetProcAddress might always return
- // something. We are doing it just for completeness' sake.
- if (!lookup(glXGetVideoSyncSGI) || !lookup(glXWaitVideoSyncSGI)) {
- glxext.has_GLX_SGI_video_sync = false;
- }
- if (!lookup(glXSwapIntervalEXT)) {
- glxext.has_GLX_EXT_swap_control = false;
- }
- if (!lookup(glXSwapIntervalMESA)) {
- glxext.has_GLX_MESA_swap_control = false;
- }
- if (!lookup(glXSwapIntervalSGI)) {
- glxext.has_GLX_SGI_swap_control = false;
- }
- if (!lookup(glXWaitForMscOML) || !lookup(glXGetSyncValuesOML)) {
- glxext.has_GLX_OML_sync_control = false;
- }
- if (!lookup(glXBindTexImageEXT) || !lookup(glXReleaseTexImageEXT)) {
- glxext.has_GLX_EXT_texture_from_pixmap = false;
- }
- if (!lookup(glXCreateContextAttribsARB)) {
- glxext.has_GLX_ARB_create_context = false;
- }
-#ifdef GLX_MESA_query_renderer
- if (!lookup(glXQueryCurrentRendererIntegerMESA)) {
- glxext.has_GLX_MESA_query_renderer = false;
- }
-#endif
-#undef lookup
-}
diff --git a/src/backend/gl/glx.h b/src/backend/gl/glx.h
deleted file mode 100644
index 1061f0b..0000000
--- a/src/backend/gl/glx.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-#include <stdbool.h>
-// Older version of glx.h defines function prototypes for these extensions...
-// Rename them to avoid conflicts
-#define glXSwapIntervalMESA glXSwapIntervalMESA_
-#define glXBindTexImageEXT glXBindTexImageEXT_
-#define glXReleaseTexImageEXT glXReleaseTexImageEXT
-#include <GL/glx.h>
-#undef glXSwapIntervalMESA
-#undef glXBindTexImageEXT
-#undef glXReleaseTexImageEXT
-#include <X11/Xlib.h>
-#include <xcb/xcb.h>
-#include <xcb/render.h>
-
-#include "log.h"
-#include "compiler.h"
-#include "utils.h"
-#include "x.h"
-
-struct glx_fbconfig_info {
- GLXFBConfig cfg;
- int texture_tgts;
- int texture_fmt;
- int y_inverted;
-};
-
-/// The search criteria for glx_find_fbconfig
-struct glx_fbconfig_criteria {
- /// Bit width of the red component
- int red_size;
- /// Bit width of the green component
- int green_size;
- /// Bit width of the blue component
- int blue_size;
- /// Bit width of the alpha component
- int alpha_size;
- /// The depth of X visual
- int visual_depth;
-};
-
-struct glx_fbconfig_info *glx_find_fbconfig(Display *, int screen, struct xvisual_info);
-
-
-struct glxext_info {
- bool initialized;
- bool has_GLX_SGI_video_sync;
- bool has_GLX_SGI_swap_control;
- bool has_GLX_OML_sync_control;
- bool has_GLX_MESA_swap_control;
- bool has_GLX_EXT_swap_control;
- bool has_GLX_EXT_texture_from_pixmap;
- bool has_GLX_ARB_create_context;
- bool has_GLX_EXT_buffer_age;
- bool has_GLX_MESA_query_renderer;
-};
-
-extern struct glxext_info glxext;
-
-extern PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
-extern PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
-extern PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
-extern PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
-extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
-extern PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
-extern PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
-extern PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
-extern PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
-extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
-
-#ifdef GLX_MESA_query_renderer
-extern PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA;
-#endif
-
-void glxext_init(Display *, int screen);
diff --git a/src/backend/meson.build b/src/backend/meson.build
deleted file mode 100644
index b8f0ad9..0000000
--- a/src/backend/meson.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# enable xrender
-srcs += [ files('backend_common.c', 'xrender/xrender.c', 'dummy/dummy.c', 'backend.c', 'driver.c') ]
-
-# enable opengl
-if get_option('opengl')
- srcs += [ files('gl/gl_common.c', 'gl/glx.c') ]
-endif
diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c
deleted file mode 100644
index ccf358b..0000000
--- a/src/backend/xrender/xrender.c
+++ /dev/null
@@ -1,779 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#include <assert.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <xcb/composite.h>
-#include <xcb/present.h>
-#include <xcb/render.h>
-#include <xcb/sync.h>
-#include <xcb/xcb.h>
-
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "common.h"
-#include "config.h"
-#include "kernel.h"
-#include "log.h"
-#include "picom.h"
-#include "region.h"
-#include "types.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-typedef struct _xrender_data {
- backend_t base;
- /// If vsync is enabled and supported by the current system
- bool vsync;
- xcb_visualid_t default_visual;
- /// Target window
- xcb_window_t target_win;
- /// Painting target, it is either the root or the overlay
- xcb_render_picture_t target;
- /// Back buffers. Double buffer, with 1 for temporary render use
- xcb_render_picture_t back[3];
- /// The back buffer that is for temporary use
- /// Age of each back buffer.
- int buffer_age[3];
- /// The back buffer we should be painting into
- int curr_back;
- /// The corresponding pixmap to the back buffer
- xcb_pixmap_t back_pixmap[3];
- /// Pictures of pixel of different alpha value, used as a mask to
- /// paint transparent images
- xcb_render_picture_t alpha_pict[256];
-
- // XXX don't know if these are really needed
-
- /// 1x1 white picture
- xcb_render_picture_t white_pixel;
- /// 1x1 black picture
- xcb_render_picture_t black_pixel;
-
- /// Width and height of the target pixmap
- int target_width, target_height;
-
- xcb_special_event_t *present_event;
-} xrender_data;
-
-struct _xrender_blur_context {
- enum blur_method method;
- /// Blur kernels converted to X format
- struct x_convolution_kernel **x_blur_kernel;
-
- int resize_width, resize_height;
-
- /// Number of blur kernels
- int x_blur_kernel_count;
-};
-
-struct _xrender_image_data_inner {
- // struct backend_image_inner_base
- int refcount;
- bool has_alpha;
-
- // Pixmap that the client window draws to,
- // it will contain the content of client window.
- xcb_pixmap_t pixmap;
- // A Picture links to the Pixmap
- xcb_render_picture_t pict;
- int width, height;
- xcb_visualid_t visual;
- uint8_t depth;
- // Whether we own this image, e.g. we allocated it;
- // or not, e.g. this is a named pixmap of a X window.
- bool owned;
-};
-
-static void compose_impl(struct _xrender_data *xd, const struct backend_image *img,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_paint, const region_t *reg_visible,
- xcb_render_picture_t result) {
- auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
- auto inner = (struct _xrender_image_data_inner *)img->inner;
- region_t reg;
-
- bool has_alpha = inner->has_alpha || img->opacity != 1;
- const auto tmpw = to_u16_checked(dst_x2 - dst_x1);
- const auto tmph = to_u16_checked(dst_y2 - dst_y1);
- const auto tmpew = to_u16_checked(dst_x2 - dst_x1);
- const auto tmpeh = to_u16_checked(dst_y2 - dst_y1);
- const xcb_render_color_t dim_color = {
- .red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)};
-
- // Clip region of rendered_pict might be set during rendering, clear it to
- // make sure we get everything into the buffer
- x_clear_picture_clip_region(xd->base.c, inner->pict);
-
- pixman_region32_init(&reg);
- pixman_region32_intersect(&reg, (region_t *)reg_paint, (region_t *)reg_visible);
- x_set_picture_clip_region(xd->base.c, result, 0, 0, &reg);
-
-#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536))
- {
- const xcb_render_transform_t transform = {
- DOUBLE_TO_XFIXED((double)img->ewidth / (double)tmpew), DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(0.0),
- DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED((double)img->eheight / (double)tmpeh), DOUBLE_TO_XFIXED(0.0),
- DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(1.0),
- };
- xcb_render_set_picture_transform(xd->base.c, inner->pict, transform);
- xcb_render_set_picture_filter(xd->base.c, inner->pict, 7, "nearest", 0, NULL);
- }
-#undef DOUBLE_TO_XFIXED
-
- if ((img->color_inverted || img->dim != 0) && has_alpha) {
- // Apply image properties using a temporary image, because the source
- // image is transparent. Otherwise the properties can be applied directly
- // on the target image.
- auto tmp_pict =
- x_create_picture_with_visual(xd->base.c, xd->base.root, inner->width,
- inner->height, inner->visual, 0, NULL);
-
- // Set clip region translated to source coordinate
- x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst_x1),
- to_i16_checked(-dst_y1), &reg);
- // Copy source -> tmp
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, inner->pict,
- XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
- if (img->color_inverted) {
- if (inner->has_alpha) {
- auto tmp_pict2 = x_create_picture_with_visual(
- xd->base.c, xd->base.root, tmpw, tmph, inner->visual,
- 0, NULL);
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER,
- tmp_pict, XCB_NONE, tmp_pict2, 0, 0,
- 0, 0, 0, 0, tmpw, tmph);
-
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
- xd->white_pixel, XCB_NONE, tmp_pict,
- 0, 0, 0, 0, 0, 0, tmpw, tmph);
- xcb_render_composite(
- xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict2,
- XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
- xcb_render_free_picture(xd->base.c, tmp_pict2);
- } else {
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
- xd->white_pixel, XCB_NONE, tmp_pict,
- 0, 0, 0, 0, 0, 0, tmpw, tmph);
- }
- }
- if (img->dim != 0) {
- // Dim the actually content of window
- xcb_rectangle_t rect = {
- .x = 0,
- .y = 0,
- .width = tmpw,
- .height = tmph,
- };
-
- xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER,
- tmp_pict, dim_color, 1, &rect);
- }
-
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
- alpha_pict, result, 0, 0, 0, 0, to_i16_checked(dst_x1),
- to_i16_checked(dst_y1), tmpew, tmpeh);
- xcb_render_free_picture(xd->base.c, tmp_pict);
- } else {
- uint8_t op = (has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
-
- xcb_render_composite(xd->base.c, op, inner->pict, alpha_pict, result, 0,
- 0, 0, 0, to_i16_checked(dst_x1),
- to_i16_checked(dst_y1), tmpew, tmpeh);
- if (img->dim != 0 || img->color_inverted) {
- // Apply properties, if we reach here, then has_alpha == false
- assert(!has_alpha);
- if (img->color_inverted) {
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
- xd->white_pixel, XCB_NONE, result, 0,
- 0, 0, 0, to_i16_checked(dst_x1),
- to_i16_checked(dst_y1), tmpew, tmpeh);
- }
-
- if (img->dim != 0) {
- // Dim the actually content of window
- xcb_rectangle_t rect = {
- .x = to_i16_checked(dst_x1),
- .y = to_i16_checked(dst_y1),
- .width = tmpew,
- .height = tmpeh,
- };
-
- xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER,
- result, dim_color, 1, &rect);
- }
- }
- }
- pixman_region32_fini(&reg);
-}
-
-static void compose(backend_t *base, void *img_data,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- const region_t *reg_paint, const region_t *reg_visible) {
- // TODO(dccsillag): use dst_{x,y}2
- struct _xrender_data *xd = (void *)base;
- return compose_impl(xd, img_data, dst_x1, dst_y1, dst_x2, dst_y2, reg_paint, reg_visible, xd->back[2]);
-}
-
-static void fill(backend_t *base, struct color c, const region_t *clip) {
- struct _xrender_data *xd = (void *)base;
- const rect_t *extent = pixman_region32_extents((region_t *)clip);
- x_set_picture_clip_region(base->c, xd->back[2], 0, 0, clip);
- // color is in X fixed point representation
- xcb_render_fill_rectangles(
- base->c, XCB_RENDER_PICT_OP_OVER, xd->back[2],
- (xcb_render_color_t){.red = (uint16_t)(c.red * 0xffff),
- .green = (uint16_t)(c.green * 0xffff),
- .blue = (uint16_t)(c.blue * 0xffff),
- .alpha = (uint16_t)(c.alpha * 0xffff)},
- 1,
- (xcb_rectangle_t[]){{.x = to_i16_checked(extent->x1),
- .y = to_i16_checked(extent->y1),
- .width = to_u16_checked(extent->x2 - extent->x1),
- .height = to_u16_checked(extent->y2 - extent->y1)}});
-}
-
-static bool blur(backend_t *backend_data, double opacity, void *ctx_,
- const region_t *reg_blur, const region_t *reg_visible) {
- struct _xrender_blur_context *bctx = ctx_;
- if (bctx->method == BLUR_METHOD_NONE) {
- return true;
- }
-
- struct _xrender_data *xd = (void *)backend_data;
- xcb_connection_t *c = xd->base.c;
- region_t reg_op;
- pixman_region32_init(&reg_op);
- pixman_region32_intersect(&reg_op, (region_t *)reg_blur, (region_t *)reg_visible);
- if (!pixman_region32_not_empty(&reg_op)) {
- pixman_region32_fini(&reg_op);
- return true;
- }
-
- region_t reg_op_resized =
- resize_region(&reg_op, bctx->resize_width, bctx->resize_height);
-
- const pixman_box32_t *extent_resized = pixman_region32_extents(&reg_op_resized);
- const auto height_resized = to_u16_checked(extent_resized->y2 - extent_resized->y1);
- const auto width_resized = to_u16_checked(extent_resized->x2 - extent_resized->x1);
- static const char *filter0 = "Nearest"; // The "null" filter
- static const char *filter = "convolution";
-
- // Create a buffer for storing blurred picture, make it just big enough
- // for the blur region
- const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT;
- const xcb_render_create_picture_value_list_t pic_attrs = {.repeat = XCB_RENDER_REPEAT_PAD};
- xcb_render_picture_t tmp_picture[2] = {
- x_create_picture_with_visual(xd->base.c, xd->base.root, width_resized, height_resized,
- xd->default_visual, pic_attrs_mask, &pic_attrs),
- x_create_picture_with_visual(xd->base.c, xd->base.root, width_resized, height_resized,
- xd->default_visual, pic_attrs_mask, &pic_attrs)};
-
- if (!tmp_picture[0] || !tmp_picture[1]) {
- log_error("Failed to build intermediate Picture.");
- pixman_region32_fini(&reg_op);
- pixman_region32_fini(&reg_op_resized);
- return false;
- }
-
- region_t clip;
- pixman_region32_init(&clip);
- pixman_region32_copy(&clip, &reg_op_resized);
- pixman_region32_translate(&clip, -extent_resized->x1, -extent_resized->y1);
- x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip);
- x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip);
- pixman_region32_fini(&clip);
-
- xcb_render_picture_t src_pict = xd->back[2], dst_pict = tmp_picture[0];
- auto alpha_pict = xd->alpha_pict[(int)(opacity * MAX_ALPHA)];
- int current = 0;
- x_set_picture_clip_region(c, src_pict, 0, 0, &reg_op_resized);
-
- // For more than 1 pass, we do:
- // back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ...
- // -(pass n-1)-> tmp0 or tmp1 -(pass n)-> back
- // For 1 pass, we do
- // back -(pass 1)-> tmp0 -(copy)-> target_buffer
- int i;
- for (i = 0; i < bctx->x_blur_kernel_count; i++) {
- // Copy from source picture to destination. The filter must
- // be applied on source picture, to get the nearby pixels outside the
- // window.
- xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)),
- filter,
- to_u32_checked(bctx->x_blur_kernel[i]->size),
- bctx->x_blur_kernel[i]->kernel);
-
- if (i == 0) {
- // First pass, back buffer -> tmp picture
- // (we do this even if this is also the last pass, because we
- // cannot do back buffer -> back buffer)
- xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
- dst_pict, to_i16_checked(extent_resized->x1),
- to_i16_checked(extent_resized->y1), 0, 0, 0,
- 0, width_resized, height_resized);
- } else if (i < bctx->x_blur_kernel_count - 1) {
- // This is not the last pass or the first pass,
- // tmp picture 1 -> tmp picture 2
- xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict,
- XCB_NONE, dst_pict, 0, 0, 0, 0, 0, 0,
- width_resized, height_resized);
- } else {
- x_set_picture_clip_region(c, xd->back[2], 0, 0, &reg_op);
- // This is the last pass, and we are doing more than 1 pass
- xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict,
- alpha_pict, xd->back[2], 0, 0, 0, 0,
- to_i16_checked(extent_resized->x1),
- to_i16_checked(extent_resized->y1),
- width_resized, height_resized);
- }
-
- // reset filter
- xcb_render_set_picture_filter(
- c, src_pict, to_u16_checked(strlen(filter0)), filter0, 0, NULL);
-
- src_pict = tmp_picture[current];
- dst_pict = tmp_picture[!current];
- current = !current;
- }
-
- // There is only 1 pass
- if (i == 1) {
- x_set_picture_clip_region(c, xd->back[2], 0, 0, &reg_op);
- xcb_render_composite(
- c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[2], 0, 0,
- 0, 0, to_i16_checked(extent_resized->x1),
- to_i16_checked(extent_resized->y1), width_resized, height_resized);
- }
-
- xcb_render_free_picture(c, tmp_picture[0]);
- xcb_render_free_picture(c, tmp_picture[1]);
- pixman_region32_fini(&reg_op);
- pixman_region32_fini(&reg_op_resized);
- return true;
-}
-
-static void *
-bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
- xcb_generic_error_t *e;
- auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), &e);
- if (!r) {
- log_error("Invalid pixmap: %#010x", pixmap);
- x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code);
- return NULL;
- }
-
- auto img = ccalloc(1, struct backend_image);
- auto inner = ccalloc(1, struct _xrender_image_data_inner);
- inner->depth = (uint8_t)fmt.visual_depth;
- inner->width = img->ewidth = r->width;
- inner->height = img->eheight = r->height;
- inner->pixmap = pixmap;
- inner->has_alpha = fmt.alpha_size != 0;
- inner->pict =
- x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL);
- inner->owned = owned;
- inner->visual = fmt.visual;
- inner->refcount = 1;
-
- img->inner = (struct backend_image_inner_base *)inner;
- img->opacity = 1;
- free(r);
-
- if (inner->pict == XCB_NONE) {
- free(inner);
- free(img);
- return NULL;
- }
- return img;
-}
-static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) {
- xcb_render_free_picture(base->c, inner->pict);
- if (inner->owned) {
- xcb_free_pixmap(base->c, inner->pixmap);
- }
- free(inner);
-}
-static void release_image(backend_t *base, void *image) {
- struct backend_image *img = image;
- img->inner->refcount--;
- if (img->inner->refcount == 0) {
- release_image_inner(base, (void *)img->inner);
- }
- free(img);
-}
-
-static void deinit(backend_t *backend_data) {
- struct _xrender_data *xd = (void *)backend_data;
- for (int i = 0; i < 256; i++) {
- xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]);
- }
- xcb_render_free_picture(xd->base.c, xd->target);
- for (int i = 0; i < 2; i++) {
- xcb_render_free_picture(xd->base.c, xd->back[i]);
- xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]);
- }
- if (xd->present_event) {
- xcb_unregister_for_special_event(xd->base.c, xd->present_event);
- }
- xcb_render_free_picture(xd->base.c, xd->white_pixel);
- xcb_render_free_picture(xd->base.c, xd->black_pixel);
- free(xd);
-}
-
-static void present(backend_t *base, const region_t *region) {
- struct _xrender_data *xd = (void *)base;
- const rect_t *extent = pixman_region32_extents((region_t *)region);
- int16_t orig_x = to_i16_checked(extent->x1), orig_y = to_i16_checked(extent->y1);
- uint16_t region_width = to_u16_checked(extent->x2 - extent->x1),
- region_height = to_u16_checked(extent->y2 - extent->y1);
-
- // compose() sets clip region on the back buffer, so clear it first
- x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]);
-
- // limit the region of update
- x_set_picture_clip_region(base->c, xd->back[2], 0, 0, region);
-
- if (xd->vsync) {
- // Update the back buffer first, then present
- xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2],
- XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0,
- 0, orig_x, orig_y, region_width, region_height);
-
- // Make sure we got reply from PresentPixmap before waiting for events,
- // to avoid deadlock
- auto e = xcb_request_check(
- base->c, xcb_present_pixmap_checked(
- xd->base.c, xd->target_win,
- xd->back_pixmap[xd->curr_back], 0, XCB_NONE, XCB_NONE, 0,
- 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
- if (e) {
- log_error("Failed to present pixmap");
- free(e);
- return;
- }
- // TODO(yshui) don't block wait for present completion
- xcb_present_generic_event_t *pev =
- (void *)xcb_wait_for_special_event(base->c, xd->present_event);
- if (!pev) {
- // We don't know what happened, maybe X died
- // But reset buffer age, so in case we do recover, we will
- // render correctly.
- xd->buffer_age[0] = xd->buffer_age[1] = -1;
- return;
- }
- assert(pev->evtype == XCB_PRESENT_COMPLETE_NOTIFY);
- xcb_present_complete_notify_event_t *pcev = (void *)pev;
- // log_trace("Present complete: %d %ld", pcev->mode, pcev->msc);
- xd->buffer_age[xd->curr_back] = 1;
-
- // buffer_age < 0 means that back buffer is empty
- if (xd->buffer_age[1 - xd->curr_back] > 0) {
- xd->buffer_age[1 - xd->curr_back]++;
- }
- if (pcev->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
- // We cannot use the pixmap we used anymore
- xd->curr_back = 1 - xd->curr_back;
- }
- free(pev);
- } else {
- // No vsync needed, draw into the target picture directly
- xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2],
- XCB_NONE, xd->target, orig_x, orig_y, 0, 0, orig_x,
- orig_y, region_width, region_height);
- }
-}
-
-static int buffer_age(backend_t *backend_data) {
- struct _xrender_data *xd = (void *)backend_data;
- if (!xd->vsync) {
- // Only the target picture really holds the screen content, and its
- // content is always up to date. So buffer age is always 1.
- return 1;
- }
- return xd->buffer_age[xd->curr_back];
-}
-
-static struct _xrender_image_data_inner *
-new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
- auto new_inner = ccalloc(1, struct _xrender_image_data_inner);
- new_inner->pixmap = x_create_pixmap(base->c, depth, base->root, w, h);
- if (new_inner->pixmap == XCB_NONE) {
- log_error("Failed to create pixmap for copy");
- free(new_inner);
- return NULL;
- }
- new_inner->pict = x_create_picture_with_visual_and_pixmap(
- base->c, visual, new_inner->pixmap, 0, NULL);
- if (new_inner->pict == XCB_NONE) {
- log_error("Failed to create picture for copy");
- xcb_free_pixmap(base->c, new_inner->pixmap);
- free(new_inner);
- return NULL;
- }
- new_inner->width = w;
- new_inner->height = h;
- new_inner->visual = visual;
- new_inner->depth = depth;
- new_inner->refcount = 1;
- new_inner->owned = true;
- return new_inner;
-}
-
-static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
- if (img->inner->refcount == 1) {
- return true;
- }
- auto inner = (struct _xrender_image_data_inner *)img->inner;
- // Force new pixmap to a 32-bit ARGB visual to allow for transparent frames around
- // non-transparent windows
- auto visual = (inner->depth == 32)
- ? inner->visual
- : x_get_visual_for_standard(base->c, XCB_PICT_STANDARD_ARGB_32);
- auto inner2 = new_inner(base, inner->width, inner->height, visual, 32);
- if (!inner2) {
- return false;
- }
-
- x_set_picture_clip_region(base->c, inner->pict, 0, 0, reg);
- xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
- inner2->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width),
- to_u16_checked(inner->height));
-
- img->inner = (struct backend_image_inner_base *)inner2;
- inner->refcount--;
- return true;
-}
-
-static bool image_op(backend_t *base, enum image_operations op, void *image,
- const region_t *reg_op, const region_t *reg_visible, void *arg) {
- struct _xrender_data *xd = (void *)base;
- struct backend_image *img = image;
- region_t reg;
- double *dargs = arg;
-
- pixman_region32_init(&reg);
- pixman_region32_intersect(&reg, (region_t *)reg_op, (region_t *)reg_visible);
-
- switch (op) {
- case IMAGE_OP_APPLY_ALPHA:
- assert(reg_op);
-
- if (!pixman_region32_not_empty(&reg)) {
- break;
- }
-
- if (dargs[0] == 1) {
- break;
- }
-
- if (!decouple_image(base, img, reg_visible)) {
- pixman_region32_fini(&reg);
- return false;
- }
-
- auto inner = (struct _xrender_image_data_inner *)img->inner;
- auto alpha_pict = xd->alpha_pict[(int)((1 - dargs[0]) * MAX_ALPHA)];
- x_set_picture_clip_region(base->c, inner->pict, 0, 0, &reg);
- xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OUT_REVERSE, alpha_pict,
- XCB_NONE, inner->pict, 0, 0, 0, 0, 0, 0,
- to_u16_checked(inner->width),
- to_u16_checked(inner->height));
- inner->has_alpha = true;
- break;
- }
- pixman_region32_fini(&reg);
- return true;
-}
-
-static void *
-create_blur_context(backend_t *base attr_unused, enum blur_method method, void *args) {
- auto ret = ccalloc(1, struct _xrender_blur_context);
- if (!method || method >= BLUR_METHOD_INVALID) {
- ret->method = BLUR_METHOD_NONE;
- return ret;
- }
- if (method == BLUR_METHOD_DUAL_KAWASE) {
- log_warn("Blur method 'dual_kawase' is not compatible with the 'xrender' "
- "backend.");
- ret->method = BLUR_METHOD_NONE;
- return ret;
- }
-
- ret->method = BLUR_METHOD_KERNEL;
- struct conv **kernels;
- int kernel_count;
- if (method == BLUR_METHOD_KERNEL) {
- kernels = ((struct kernel_blur_args *)args)->kernels;
- kernel_count = ((struct kernel_blur_args *)args)->kernel_count;
- } else {
- kernels = generate_blur_kernel(method, args, &kernel_count);
- }
-
- ret->x_blur_kernel = ccalloc(kernel_count, struct x_convolution_kernel *);
- for (int i = 0; i < kernel_count; i++) {
- int center = kernels[i]->h * kernels[i]->w / 2;
- x_create_convolution_kernel(kernels[i], kernels[i]->data[center],
- &ret->x_blur_kernel[i]);
- ret->resize_width += kernels[i]->w / 2;
- ret->resize_height += kernels[i]->h / 2;
- }
- ret->x_blur_kernel_count = kernel_count;
-
- if (method != BLUR_METHOD_KERNEL) {
- // Kernels generated by generate_blur_kernel, so we need to free them.
- for (int i = 0; i < kernel_count; i++) {
- free(kernels[i]);
- }
- free(kernels);
- }
- return ret;
-}
-
-static void destroy_blur_context(backend_t *base attr_unused, void *ctx_) {
- struct _xrender_blur_context *ctx = ctx_;
- for (int i = 0; i < ctx->x_blur_kernel_count; i++) {
- free(ctx->x_blur_kernel[i]);
- }
- free(ctx->x_blur_kernel);
- free(ctx);
-}
-
-static void get_blur_size(void *blur_context, int *width, int *height) {
- struct _xrender_blur_context *ctx = blur_context;
- *width = ctx->resize_width;
- *height = ctx->resize_height;
-}
-
-static bool
-read_pixel(backend_t *backend_data, void *image_data, int x, int y, struct color *output) {
- auto xd = (struct _xrender_data *)backend_data;
- auto img = (struct backend_image *)image_data;
- auto inner = (struct _xrender_image_data_inner *)img->inner;
-
- auto r = XCB_AWAIT(xcb_get_image, xd->base.c, XCB_IMAGE_FORMAT_XY_PIXMAP, inner->pixmap,
- to_i16_checked(x), to_i16_checked(y), 1, 1, (uint32_t)-1L);
-
- if (!r) {
- return false;
- }
-
- // Color format seems to be BGRA8888, see glamor_format_for_pixmap from the
- // Xserver codebase.
- uint8_t *pixels = xcb_get_image_data(r);
- output->blue = pixels[0] / 255.0;
- output->green = pixels[1] / 255.0;
- output->red = pixels[2] / 255.0;
- output->alpha = pixels[3] / 255.0;
-
- return true;
-}
-
-static backend_t *backend_xrender_init(session_t *ps) {
- auto xd = ccalloc(1, struct _xrender_data);
- init_backend_base(&xd->base, ps);
-
- for (int i = 0; i <= MAX_ALPHA; ++i) {
- double o = (double)i / (double)MAX_ALPHA;
- xd->alpha_pict[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
- assert(xd->alpha_pict[i] != XCB_NONE);
- }
-
- xd->target_width = ps->root_width;
- xd->target_height = ps->root_height;
- xd->default_visual = ps->vis;
- xd->black_pixel = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0);
- xd->white_pixel = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1);
-
- xd->target_win = session_get_target_window(ps);
- xcb_render_create_picture_value_list_t pa = {
- .subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS,
- };
- xd->target = x_create_picture_with_visual_and_pixmap(
- ps->c, ps->vis, xd->target_win, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
-
- auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis);
- if (!pictfmt) {
- log_fatal("Default visual is invalid");
- abort();
- }
-
- xd->vsync = ps->o.vsync;
- if (ps->present_exists) {
- auto eid = x_new_id(ps->c);
- auto e =
- xcb_request_check(ps->c, xcb_present_select_input_checked(
- ps->c, eid, xd->target_win,
- XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
- if (e) {
- log_error("Cannot select present input, vsync will be disabled");
- xd->vsync = false;
- free(e);
- }
-
- xd->present_event =
- xcb_register_for_special_xge(ps->c, &xcb_present_id, eid, NULL);
- if (!xd->present_event) {
- log_error("Cannot register for special XGE, vsync will be "
- "disabled");
- xd->vsync = false;
- }
- } else {
- xd->vsync = false;
- }
-
- // We might need to do double buffering for vsync, and buffer 0 and 1 are for
- // double buffering.
- int first_buffer_index = xd->vsync ? 0 : 2;
- for (int i = first_buffer_index; i < 3; i++) {
- xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root,
- to_u16_checked(ps->root_width),
- to_u16_checked(ps->root_height));
- const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT;
- const xcb_render_create_picture_value_list_t pic_attrs = {
- .repeat = XCB_RENDER_REPEAT_PAD};
- xd->back[i] = x_create_picture_with_pictfmt_and_pixmap(
- ps->c, pictfmt, xd->back_pixmap[i], pic_attrs_mask, &pic_attrs);
- xd->buffer_age[i] = -1;
- if (xd->back_pixmap[i] == XCB_NONE || xd->back[i] == XCB_NONE) {
- log_error("Cannot create pixmap for rendering");
- goto err;
- }
- }
- xd->curr_back = 0;
-
- return &xd->base;
-err:
- deinit(&xd->base);
- return NULL;
-}
-
-struct backend_operations xrender_ops = {
- .init = backend_xrender_init,
- .deinit = deinit,
- .blur = blur,
- .present = present,
- .compose = compose,
- .fill = fill,
- .bind_pixmap = bind_pixmap,
- .release_image = release_image,
- .render_shadow = default_backend_render_shadow,
- //.prepare_win = prepare_win,
- //.release_win = release_win,
- .is_image_transparent = default_is_image_transparent,
- .buffer_age = buffer_age,
- .max_buffer_age = 2,
-
- .image_op = image_op,
- .read_pixel = read_pixel,
- .clone_image = default_clone_image,
- .set_image_property = default_set_image_property,
- .create_blur_context = create_blur_context,
- .destroy_blur_context = destroy_blur_context,
- .get_blur_size = get_blur_size,
-};
-
-// vim: set noet sw=8 ts=8:
diff --git a/src/c2.c b/src/c2.c
deleted file mode 100644
index 3500f7b..0000000
--- a/src/c2.c
+++ /dev/null
@@ -1,1674 +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 <ctype.h>
-#include <fnmatch.h>
-#include <stdio.h>
-#include <string.h>
-
-// libpcre
-#ifdef CONFIG_REGEX_PCRE
-#include <pcre.h>
-
-// For compatibility with <libpcre-8.20
-#ifndef PCRE_STUDY_JIT_COMPILE
-#define PCRE_STUDY_JIT_COMPILE 0
-#define LPCRE_FREE_STUDY(extra) pcre_free(extra)
-#else
-#define LPCRE_FREE_STUDY(extra) pcre_free_study(extra)
-#endif
-
-#endif
-
-#include <X11/Xlib.h>
-#include <xcb/xcb.h>
-
-#include "atom.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h"
-#include "string_utils.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-#include "c2.h"
-
-#pragma GCC diagnostic error "-Wunused-parameter"
-
-#define C2_MAX_LEVELS 10
-
-typedef struct _c2_b c2_b_t;
-typedef struct _c2_l c2_l_t;
-
-/// Pointer to a condition tree.
-typedef struct {
- bool isbranch : 1;
- union {
- c2_b_t *b;
- c2_l_t *l;
- };
-} c2_ptr_t;
-
-/// Initializer for c2_ptr_t.
-#define C2_PTR_INIT \
- { .isbranch = false, .l = NULL, }
-
-static const c2_ptr_t C2_PTR_NULL = C2_PTR_INIT;
-
-/// Operator of a branch element.
-typedef enum {
- C2_B_OUNDEFINED,
- C2_B_OAND,
- C2_B_OOR,
- C2_B_OXOR,
-} c2_b_op_t;
-
-/// Structure for branch element in a window condition
-struct _c2_b {
- bool neg : 1;
- c2_b_op_t op;
- c2_ptr_t opr1;
- c2_ptr_t opr2;
-};
-
-/// Initializer for c2_b_t.
-#define C2_B_INIT \
- { .neg = false, .op = C2_B_OUNDEFINED, .opr1 = C2_PTR_INIT, .opr2 = C2_PTR_INIT, }
-
-/// Structure for leaf element in a window condition
-struct _c2_l {
- bool neg : 1;
- enum { C2_L_OEXISTS,
- C2_L_OEQ,
- C2_L_OGT,
- C2_L_OGTEQ,
- C2_L_OLT,
- C2_L_OLTEQ,
- } op : 3;
- enum { C2_L_MEXACT,
- C2_L_MSTART,
- C2_L_MCONTAINS,
- C2_L_MWILDCARD,
- C2_L_MPCRE,
- } match : 3;
- bool match_ignorecase : 1;
- char *tgt;
- xcb_atom_t tgtatom;
- bool tgt_onframe;
- int index;
- enum { C2_L_PUNDEFINED = -1,
- C2_L_PID = 0,
- C2_L_PX,
- C2_L_PY,
- C2_L_PX2,
- C2_L_PY2,
- C2_L_PWIDTH,
- C2_L_PHEIGHT,
- C2_L_PWIDTHB,
- C2_L_PHEIGHTB,
- C2_L_PBDW,
- C2_L_PFULLSCREEN,
- C2_L_POVREDIR,
- C2_L_PARGB,
- C2_L_PFOCUSED,
- C2_L_PWMWIN,
- C2_L_PBSHAPED,
- C2_L_PROUNDED,
- C2_L_PCLIENT,
- C2_L_PWINDOWTYPE,
- C2_L_PLEADER,
- C2_L_PNAME,
- C2_L_PCLASSG,
- C2_L_PCLASSI,
- C2_L_PROLE,
- } predef;
- enum c2_l_type {
- C2_L_TUNDEFINED,
- C2_L_TSTRING,
- C2_L_TCARDINAL,
- C2_L_TWINDOW,
- C2_L_TATOM,
- C2_L_TDRAWABLE,
- } type;
- int format;
- enum { C2_L_PTUNDEFINED,
- C2_L_PTSTRING,
- C2_L_PTINT,
- } ptntype;
- char *ptnstr;
- long ptnint;
-#ifdef CONFIG_REGEX_PCRE
- pcre *regex_pcre;
- pcre_extra *regex_pcre_extra;
-#endif
-};
-
-/// Initializer for c2_l_t.
-#define C2_L_INIT \
- { \
- .neg = false, .op = C2_L_OEXISTS, .match = C2_L_MEXACT, \
- .match_ignorecase = false, .tgt = NULL, .tgtatom = 0, .tgt_onframe = false, \
- .predef = C2_L_PUNDEFINED, .index = 0, .type = C2_L_TUNDEFINED, \
- .format = 0, .ptntype = C2_L_PTUNDEFINED, .ptnstr = NULL, .ptnint = 0, \
- }
-
-static const c2_l_t leaf_def = C2_L_INIT;
-
-/// Linked list type of conditions.
-struct _c2_lptr {
- c2_ptr_t ptr;
- void *data;
- struct _c2_lptr *next;
-};
-
-/// Initializer for c2_lptr_t.
-#define C2_LPTR_INIT \
- { .ptr = C2_PTR_INIT, .data = NULL, .next = NULL, }
-
-/// Structure representing a predefined target.
-typedef struct {
- const char *name;
- enum c2_l_type type;
- int format;
-} c2_predef_t;
-
-// Predefined targets.
-static const c2_predef_t C2_PREDEFS[] = {
- [C2_L_PID] = {"id", C2_L_TCARDINAL, 0},
- [C2_L_PX] = {"x", C2_L_TCARDINAL, 0},
- [C2_L_PY] = {"y", C2_L_TCARDINAL, 0},
- [C2_L_PX2] = {"x2", C2_L_TCARDINAL, 0},
- [C2_L_PY2] = {"y2", C2_L_TCARDINAL, 0},
- [C2_L_PWIDTH] = {"width", C2_L_TCARDINAL, 0},
- [C2_L_PHEIGHT] = {"height", C2_L_TCARDINAL, 0},
- [C2_L_PWIDTHB] = {"widthb", C2_L_TCARDINAL, 0},
- [C2_L_PHEIGHTB] = {"heightb", C2_L_TCARDINAL, 0},
- [C2_L_PBDW] = {"border_width", C2_L_TCARDINAL, 0},
- [C2_L_PFULLSCREEN] = {"fullscreen", C2_L_TCARDINAL, 0},
- [C2_L_POVREDIR] = {"override_redirect", C2_L_TCARDINAL, 0},
- [C2_L_PARGB] = {"argb", C2_L_TCARDINAL, 0},
- [C2_L_PFOCUSED] = {"focused", C2_L_TCARDINAL, 0},
- [C2_L_PWMWIN] = {"wmwin", C2_L_TCARDINAL, 0},
- [C2_L_PBSHAPED] = {"bounding_shaped", C2_L_TCARDINAL, 0},
- [C2_L_PROUNDED] = {"rounded_corners", C2_L_TCARDINAL, 0},
- [C2_L_PCLIENT] = {"client", C2_L_TWINDOW, 0},
- [C2_L_PWINDOWTYPE] = {"window_type", C2_L_TSTRING, 0},
- [C2_L_PLEADER] = {"leader", C2_L_TWINDOW, 0},
- [C2_L_PNAME] = {"name", C2_L_TSTRING, 0},
- [C2_L_PCLASSG] = {"class_g", C2_L_TSTRING, 0},
- [C2_L_PCLASSI] = {"class_i", C2_L_TSTRING, 0},
- [C2_L_PROLE] = {"role", C2_L_TSTRING, 0},
-};
-
-/**
- * Get the numeric property value from a win_prop_t.
- */
-static inline long winprop_get_int(winprop_t prop, size_t index) {
- long tgt = 0;
-
- if (!prop.nitems || index >= prop.nitems) {
- return 0;
- }
-
- switch (prop.format) {
- case 8: tgt = *(prop.p8 + index); break;
- case 16: tgt = *(prop.p16 + index); break;
- case 32: tgt = *(prop.p32 + index); break;
- default: assert(0); break;
- }
-
- return tgt;
-}
-
-/**
- * Compare next word in a string with another string.
- */
-static inline int strcmp_wd(const char *needle, const char *src) {
- int ret = mstrncmp(needle, src);
- if (ret)
- return ret;
-
- char c = src[strlen(needle)];
- if (isalnum((unsigned char)c) || '_' == c)
- return 1;
- else
- return 0;
-}
-
-/**
- * Return whether a c2_ptr_t is empty.
- */
-static inline attr_unused bool c2_ptr_isempty(const c2_ptr_t p) {
- return !(p.isbranch ? (bool)p.b : (bool)p.l);
-}
-
-/**
- * Reset a c2_ptr_t.
- */
-static inline void c2_ptr_reset(c2_ptr_t *pp) {
- if (pp)
- memcpy(pp, &C2_PTR_NULL, sizeof(c2_ptr_t));
-}
-
-/**
- * Combine two condition trees.
- */
-static inline c2_ptr_t c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
- c2_ptr_t p = {.isbranch = true, .b = NULL};
- p.b = cmalloc(c2_b_t);
-
- p.b->neg = false;
- p.b->op = op;
- p.b->opr1 = p1;
- p.b->opr2 = p2;
-
- return p;
-}
-
-/**
- * Get the precedence value of a condition branch operator.
- */
-static inline int c2h_b_opp(c2_b_op_t op) {
- switch (op) {
- case C2_B_OAND: return 2;
- case C2_B_OOR: return 1;
- case C2_B_OXOR: return 1;
- default: break;
- }
-
- assert(0);
- return 0;
-}
-
-/**
- * Compare precedence of two condition branch operators.
- *
- * Associativity is left-to-right, forever.
- *
- * @return positive number if op1 > op2, 0 if op1 == op2 in precedence,
- * negative number otherwise
- */
-static inline int c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) {
- return c2h_b_opp(op1) - c2h_b_opp(op2);
-}
-
-static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int level);
-
-static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult);
-
-static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult);
-
-static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult);
-
-static int c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult);
-
-static void c2_free(c2_ptr_t p);
-
-/**
- * Wrapper of c2_free().
- */
-static inline void c2_freep(c2_ptr_t *pp) {
- if (pp) {
- c2_free(*pp);
- c2_ptr_reset(pp);
- }
-}
-
-static const char *c2h_dump_str_tgt(const c2_l_t *pleaf);
-
-static const char *c2h_dump_str_type(const c2_l_t *pleaf);
-
-static void attr_unused c2_dump(c2_ptr_t p);
-
-static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf);
-
-static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_ptr_t cond);
-
-/**
- * Parse a condition string.
- */
-c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data) {
- if (!pattern)
- return NULL;
-
- // Parse the pattern
- c2_ptr_t result = C2_PTR_INIT;
- int offset = -1;
-
- if (strlen(pattern) >= 2 && ':' == pattern[1])
- offset = c2_parse_legacy(pattern, 0, &result);
- else
- offset = c2_parse_grp(pattern, 0, &result, 0);
-
- if (offset < 0) {
- c2_freep(&result);
- return NULL;
- }
-
- // Insert to pcondlst
- {
- static const c2_lptr_t lptr_def = C2_LPTR_INIT;
- auto plptr = cmalloc(c2_lptr_t);
- memcpy(plptr, &lptr_def, sizeof(c2_lptr_t));
- plptr->ptr = result;
- plptr->data = data;
- if (pcondlst) {
- plptr->next = *pcondlst;
- *pcondlst = plptr;
- }
-
-#ifdef DEBUG_C2
- log_trace("(\"%s\"): ", pattern);
- c2_dump(plptr->ptr);
- putchar('\n');
-#endif
-
- return plptr;
- }
-}
-
-#define c2_error(format, ...) \
- do { \
- log_error("Pattern \"%s\" pos %d: " format, pattern, offset, ##__VA_ARGS__); \
- goto fail; \
- } while (0)
-
-// TODO(yshui) Not a very good macro, should probably be a function
-#define C2H_SKIP_SPACES() \
- { \
- while (isspace((unsigned char)pattern[offset])) \
- ++offset; \
- }
-
-/**
- * Parse a group in condition string.
- *
- * @return offset of next character in string
- */
-static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int level) {
- // Check for recursion levels
- if (level > C2_MAX_LEVELS)
- c2_error("Exceeded maximum recursion levels.");
-
- if (!pattern)
- return -1;
-
- // Expected end character
- const char endchar = (offset ? ')' : '\0');
-
- // We use a system that a maximum of 2 elements are kept. When we find
- // the third element, we combine the elements according to operator
- // precedence. This design limits operators to have at most two-levels
- // of precedence and fixed left-to-right associativity.
-
- // For storing branch operators. ops[0] is actually unused
- c2_b_op_t ops[3] = {};
- // For storing elements
- c2_ptr_t eles[2] = {C2_PTR_INIT, C2_PTR_INIT};
- // Index of next free element slot in eles
- int elei = 0;
- // Pointer to the position of next element
- c2_ptr_t *pele = eles;
- // Negation flag of next operator
- bool neg = false;
- // Whether we are expecting an element immediately, is true at first, or
- // after encountering a logical operator
- bool next_expected = true;
-
- // Parse the pattern character-by-character
- for (; pattern[offset]; ++offset) {
- assert(elei <= 2);
-
- // Jump over spaces
- if (isspace((unsigned char)pattern[offset]))
- continue;
-
- // Handle end of group
- if (')' == pattern[offset])
- break;
-
- // Handle "!"
- if ('!' == pattern[offset]) {
- if (!next_expected)
- c2_error("Unexpected \"!\".");
-
- neg = !neg;
- continue;
- }
-
- // Handle AND and OR
- if ('&' == pattern[offset] || '|' == pattern[offset]) {
- if (next_expected)
- c2_error("Unexpected logical operator.");
-
- next_expected = true;
- if (!mstrncmp("&&", pattern + offset)) {
- ops[elei] = C2_B_OAND;
- ++offset;
- } else if (!mstrncmp("||", pattern + offset)) {
- ops[elei] = C2_B_OOR;
- ++offset;
- } else
- c2_error("Illegal logical operator.");
-
- continue;
- }
-
- // Parsing an element
- if (!next_expected)
- c2_error("Unexpected expression.");
-
- assert(!elei || ops[elei]);
-
- // If we are out of space
- if (2 == elei) {
- --elei;
- // If the first operator has higher or equal precedence, combine
- // the first two elements
- if (c2h_b_opcmp(ops[1], ops[2]) >= 0) {
- eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]);
- c2_ptr_reset(&eles[1]);
- pele = &eles[elei];
- ops[1] = ops[2];
- }
- // Otherwise, combine the second and the incoming one
- else {
- eles[1] = c2h_comb_tree(ops[2], eles[1], C2_PTR_NULL);
- assert(eles[1].isbranch);
- pele = &eles[1].b->opr2;
- }
- // The last operator always needs to be reset
- ops[2] = C2_B_OUNDEFINED;
- }
-
- // It's a subgroup if it starts with '('
- if ('(' == pattern[offset]) {
- if ((offset = c2_parse_grp(pattern, offset + 1, pele, level + 1)) < 0)
- goto fail;
- }
- // Otherwise it's a leaf
- else {
- if ((offset = c2_parse_target(pattern, offset, pele)) < 0)
- goto fail;
-
- assert(!pele->isbranch && !c2_ptr_isempty(*pele));
-
- if ((offset = c2_parse_op(pattern, offset, pele)) < 0)
- goto fail;
-
- if ((offset = c2_parse_pattern(pattern, offset, pele)) < 0)
- goto fail;
- }
- // Decrement offset -- we will increment it in loop update
- --offset;
-
- // Apply negation
- if (neg) {
- neg = false;
- if (pele->isbranch)
- pele->b->neg = !pele->b->neg;
- else
- pele->l->neg = !pele->l->neg;
- }
-
- next_expected = false;
- ++elei;
- pele = &eles[elei];
- }
-
- // Wrong end character?
- if (pattern[offset] && !endchar)
- c2_error("Expected end of string but found '%c'.", pattern[offset]);
- if (!pattern[offset] && endchar)
- c2_error("Expected '%c' but found end of string.", endchar);
-
- // Handle end of group
- if (!elei) {
- c2_error("Empty group.");
- } else if (next_expected) {
- c2_error("Missing rule before end of group.");
- } else if (elei > 1) {
- assert(2 == elei);
- assert(ops[1]);
- eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]);
- c2_ptr_reset(&eles[1]);
- }
-
- *presult = eles[0];
-
- if (')' == pattern[offset])
- ++offset;
-
- return offset;
-
-fail:
- c2_freep(&eles[0]);
- c2_freep(&eles[1]);
-
- return -1;
-}
-
-/**
- * Parse the target part of a rule.
- */
-static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) {
- // Initialize leaf
- presult->isbranch = false;
- presult->l = cmalloc(c2_l_t);
-
- c2_l_t *const pleaf = presult->l;
- memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
-
- // Parse negation marks
- while ('!' == pattern[offset]) {
- pleaf->neg = !pleaf->neg;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Copy target name out
- int tgtlen = 0;
- for (; pattern[offset] &&
- (isalnum((unsigned char)pattern[offset]) || '_' == pattern[offset]);
- ++offset) {
- ++tgtlen;
- }
- if (!tgtlen) {
- c2_error("Empty target.");
- }
- pleaf->tgt = strndup(&pattern[offset - tgtlen], (size_t)tgtlen);
-
- // Check for predefined targets
- static const int npredefs = (int)(sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]));
- for (int i = 0; i < npredefs; ++i) {
- if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) {
- pleaf->predef = i;
- pleaf->type = C2_PREDEFS[i].type;
- pleaf->format = C2_PREDEFS[i].format;
- break;
- }
- }
-
- C2H_SKIP_SPACES();
-
- // Parse target-on-frame flag
- if ('@' == pattern[offset]) {
- pleaf->tgt_onframe = true;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse index
- if ('[' == pattern[offset]) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- c2_error("Predefined targets can't have index.");
- }
-
- offset++;
-
- C2H_SKIP_SPACES();
-
- long index = -1;
- const char *endptr = NULL;
-
- if ('*' == pattern[offset]) {
- index = -1;
- endptr = pattern + offset + 1;
- } else {
- index = strtol(pattern + offset, (char **)&endptr, 0);
- if (index < 0) {
- c2_error("Index number invalid.");
- }
- }
-
- if (!endptr || pattern + offset == endptr) {
- c2_error("No index number found after bracket.");
- }
-
- pleaf->index = to_int_checked(index);
- offset = to_int_checked(endptr - pattern);
-
- C2H_SKIP_SPACES();
-
- if (pattern[offset] != ']') {
- c2_error("Index end marker not found.");
- }
-
- ++offset;
-
- C2H_SKIP_SPACES();
- }
-
- // Parse target type and format
- if (':' == pattern[offset]) {
- ++offset;
- C2H_SKIP_SPACES();
-
- // Look for format
- bool hasformat = false;
- long format = 0;
- {
- char *endptr = NULL;
- format = strtol(pattern + offset, &endptr, 0);
- assert(endptr);
- if ((hasformat = (endptr && endptr != pattern + offset))) {
- offset = to_int_checked(endptr - pattern);
- }
- C2H_SKIP_SPACES();
- }
-
- // Look for type
- enum c2_l_type type = C2_L_TUNDEFINED;
- switch (pattern[offset]) {
- case 'w': type = C2_L_TWINDOW; break;
- case 'd': type = C2_L_TDRAWABLE; break;
- case 'c': type = C2_L_TCARDINAL; break;
- case 's': type = C2_L_TSTRING; break;
- case 'a': type = C2_L_TATOM; break;
- default: c2_error("Invalid type character.");
- }
-
- if (type) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- log_warn("Type specified for a default target "
- "will be ignored.");
- } else {
- if (pleaf->type && type != pleaf->type) {
- log_warn("Default type overridden on "
- "target.");
- }
- pleaf->type = type;
- }
- }
-
- offset++;
- C2H_SKIP_SPACES();
-
- // Default format
- if (!pleaf->format) {
- switch (pleaf->type) {
- case C2_L_TWINDOW:
- case C2_L_TDRAWABLE:
- case C2_L_TATOM: pleaf->format = 32; break;
- case C2_L_TSTRING: pleaf->format = 8; break;
- default: break;
- }
- }
-
- // Write format
- if (hasformat) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- log_warn("Format \"%ld\" specified on a default target "
- "will be ignored.",
- format);
- } else if (pleaf->type == C2_L_TSTRING) {
- log_warn("Format \"%ld\" specified on a string target "
- "will be ignored.",
- format);
- } else {
- if (pleaf->format && pleaf->format != format) {
- log_warn("Default format %d overridden on "
- "target.",
- pleaf->format);
- }
- pleaf->format = to_int_checked(format);
- }
- }
- }
-
- if (!pleaf->type) {
- c2_error("Target type cannot be determined.");
- }
-
- // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type)
- // c2_error("Target format cannot be determined.");
-
- if (pleaf->format && 8 != pleaf->format && 16 != pleaf->format && 32 != pleaf->format) {
- c2_error("Invalid format.");
- }
-
- return offset;
-
-fail:
- return -1;
-}
-
-/**
- * Parse the operator part of a leaf.
- */
-static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) {
- c2_l_t *const pleaf = presult->l;
-
- // Parse negation marks
- C2H_SKIP_SPACES();
- while ('!' == pattern[offset]) {
- pleaf->neg = !pleaf->neg;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse qualifiers
- if ('*' == pattern[offset] || '^' == pattern[offset] || '%' == pattern[offset] ||
- '~' == pattern[offset]) {
- switch (pattern[offset]) {
- case '*': pleaf->match = C2_L_MCONTAINS; break;
- case '^': pleaf->match = C2_L_MSTART; break;
- case '%': pleaf->match = C2_L_MWILDCARD; break;
- case '~': pleaf->match = C2_L_MPCRE; break;
- default: assert(0);
- }
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse flags
- while ('?' == pattern[offset]) {
- pleaf->match_ignorecase = true;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse operator
- while ('=' == pattern[offset] || '>' == pattern[offset] || '<' == pattern[offset]) {
- if ('=' == pattern[offset] && C2_L_OGT == pleaf->op)
- pleaf->op = C2_L_OGTEQ;
- else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op)
- pleaf->op = C2_L_OLTEQ;
- else if (pleaf->op) {
- c2_error("Duplicate operator.");
- } else {
- switch (pattern[offset]) {
- case '=': pleaf->op = C2_L_OEQ; break;
- case '>': pleaf->op = C2_L_OGT; break;
- case '<': pleaf->op = C2_L_OLT; break;
- default: assert(0);
- }
- }
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Check for problems
- if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase))
- c2_error("Exists/greater-than/less-than operators cannot have a "
- "qualifier.");
-
- return offset;
-
-fail:
- return -1;
-}
-
-/**
- * Parse the pattern part of a leaf.
- */
-static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult) {
- c2_l_t *const pleaf = presult->l;
-
- // Exists operator cannot have pattern
- if (!pleaf->op) {
- return offset;
- }
-
- C2H_SKIP_SPACES();
-
- char *endptr = NULL;
- if (!strcmp_wd("true", &pattern[offset])) {
- pleaf->ptntype = C2_L_PTINT;
- pleaf->ptnint = true;
- offset += 4; // length of "true";
- } else if (!strcmp_wd("false", &pattern[offset])) {
- pleaf->ptntype = C2_L_PTINT;
- pleaf->ptnint = false;
- offset += 5; // length of "false";
- } else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0),
- pattern + offset != endptr) {
- pleaf->ptntype = C2_L_PTINT;
- offset = to_int_checked(endptr - pattern);
- // Make sure we are stopping at the end of a word
- if (isalnum((unsigned char)pattern[offset])) {
- c2_error("Trailing characters after a numeric pattern.");
- }
- } else {
- // Parse string patterns
- bool raw = false;
- char delim = '\0';
-
- // String flags
- if (tolower((unsigned char)pattern[offset]) == 'r') {
- raw = true;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Check for delimiters
- if (pattern[offset] == '\"' || pattern[offset] == '\'') {
- pleaf->ptntype = C2_L_PTSTRING;
- delim = pattern[offset];
- ++offset;
- }
-
- if (pleaf->ptntype != C2_L_PTSTRING) {
- c2_error("Invalid pattern type.");
- }
-
- // Parse the string now
- // We can't determine the length of the pattern, so we use the length
- // to the end of the pattern string -- currently escape sequences
- // cannot be converted to a string longer than itself.
- auto tptnstr = ccalloc((strlen(pattern + offset) + 1), char);
- char *ptptnstr = tptnstr;
- pleaf->ptnstr = tptnstr;
- for (; pattern[offset] && delim != pattern[offset]; ++offset) {
- // Handle escape sequences if it's not a raw string
- if ('\\' == pattern[offset] && !raw) {
- switch (pattern[++offset]) {
- case '\\': *(ptptnstr++) = '\\'; break;
- case '\'': *(ptptnstr++) = '\''; break;
- case '\"': *(ptptnstr++) = '\"'; break;
- case 'a': *(ptptnstr++) = '\a'; break;
- case 'b': *(ptptnstr++) = '\b'; break;
- case 'f': *(ptptnstr++) = '\f'; break;
- case 'n': *(ptptnstr++) = '\n'; break;
- case 'r': *(ptptnstr++) = '\r'; break;
- case 't': *(ptptnstr++) = '\t'; break;
- case 'v': *(ptptnstr++) = '\v'; break;
- case 'o':
- case 'x': {
- char *tstr = strndup(pattern + offset + 1, 2);
- char *pstr = NULL;
- long val = strtol(
- tstr, &pstr, ('o' == pattern[offset] ? 8 : 16));
- free(tstr);
- if (pstr != &tstr[2] || val <= 0)
- c2_error("Invalid octal/hex escape "
- "sequence.");
- *(ptptnstr++) = to_char_checked(val);
- offset += 2;
- break;
- }
- default: c2_error("Invalid escape sequence.");
- }
- } else {
- *(ptptnstr++) = pattern[offset];
- }
- }
- if (!pattern[offset])
- c2_error("Premature end of pattern string.");
- ++offset;
- *ptptnstr = '\0';
- pleaf->ptnstr = strdup(tptnstr);
- free(tptnstr);
- }
-
- C2H_SKIP_SPACES();
-
- if (!pleaf->ptntype)
- c2_error("Invalid pattern type.");
-
- // Check if the type is correct
- if (!(((C2_L_TSTRING == pleaf->type || C2_L_TATOM == pleaf->type) &&
- C2_L_PTSTRING == pleaf->ptntype) ||
- ((C2_L_TCARDINAL == pleaf->type || C2_L_TWINDOW == pleaf->type ||
- C2_L_TDRAWABLE == pleaf->type) &&
- C2_L_PTINT == pleaf->ptntype)))
- c2_error("Pattern type incompatible with target type.");
-
- if (C2_L_PTINT == pleaf->ptntype && pleaf->match)
- c2_error("Integer/boolean pattern cannot have operator qualifiers.");
-
- if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase)
- c2_error("Integer/boolean pattern cannot have flags.");
-
- if (C2_L_PTSTRING == pleaf->ptntype &&
- (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op || C2_L_OLT == pleaf->op ||
- C2_L_OLTEQ == pleaf->op))
- c2_error("String pattern cannot have an arithmetic operator.");
-
- return offset;
-
-fail:
- return -1;
-}
-
-/**
- * Parse a condition with legacy syntax.
- */
-static int c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult) {
- if (strlen(pattern + offset) < 4 || pattern[offset + 1] != ':' ||
- !strchr(pattern + offset + 2, ':')) {
- c2_error("Legacy parser: Invalid format.");
- }
-
- // Allocate memory for new leaf
- auto pleaf = cmalloc(c2_l_t);
- presult->isbranch = false;
- presult->l = pleaf;
- memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
- pleaf->type = C2_L_TSTRING;
- pleaf->op = C2_L_OEQ;
- pleaf->ptntype = C2_L_PTSTRING;
-
- // Determine the pattern target
-#define TGTFILL(pdefid) \
- (pleaf->predef = pdefid, pleaf->type = C2_PREDEFS[pdefid].type, \
- pleaf->format = C2_PREDEFS[pdefid].format)
- switch (pattern[offset]) {
- case 'n': TGTFILL(C2_L_PNAME); break;
- case 'i': TGTFILL(C2_L_PCLASSI); break;
- case 'g': TGTFILL(C2_L_PCLASSG); break;
- case 'r': TGTFILL(C2_L_PROLE); break;
- default: c2_error("Target \"%c\" invalid.\n", pattern[offset]);
- }
-#undef TGTFILL
-
- offset += 2;
-
- // Determine the match type
- switch (pattern[offset]) {
- case 'e': pleaf->match = C2_L_MEXACT; break;
- case 'a': pleaf->match = C2_L_MCONTAINS; break;
- case 's': pleaf->match = C2_L_MSTART; break;
- case 'w': pleaf->match = C2_L_MWILDCARD; break;
- case 'p': pleaf->match = C2_L_MPCRE; break;
- default: c2_error("Type \"%c\" invalid.\n", pattern[offset]);
- }
- ++offset;
-
- // Determine the pattern flags
- while (':' != pattern[offset]) {
- switch (pattern[offset]) {
- case 'i': pleaf->match_ignorecase = true; break;
- default: c2_error("Flag \"%c\" invalid.", pattern[offset]);
- }
- ++offset;
- }
- ++offset;
-
- // Copy the pattern
- pleaf->ptnstr = strdup(pattern + offset);
-
- return offset;
-
-fail:
- return -1;
-}
-
-#undef c2_error
-
-/**
- * Do postprocessing on a condition leaf.
- */
-static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
- // Give a pattern type to a leaf with exists operator, if needed
- if (C2_L_OEXISTS == pleaf->op && !pleaf->ptntype) {
- pleaf->ptntype = (C2_L_TSTRING == pleaf->type ? C2_L_PTSTRING : C2_L_PTINT);
- }
-
- // Get target atom if it's not a predefined one
- if (pleaf->predef == C2_L_PUNDEFINED) {
- pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt);
- if (!pleaf->tgtatom) {
- log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
- return false;
- }
- }
-
- // Insert target Atom into atom track list
- if (pleaf->tgtatom) {
- bool found = false;
- for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
- if (pleaf->tgtatom == platom->atom) {
- found = true;
- break;
- }
- }
- if (!found) {
- auto pnew = cmalloc(latom_t);
- pnew->next = ps->track_atom_lst;
- pnew->atom = pleaf->tgtatom;
- ps->track_atom_lst = pnew;
- }
- }
-
- // Warn about lower case characters in target name
- if (pleaf->predef == C2_L_PUNDEFINED) {
- for (const char *pc = pleaf->tgt; *pc; ++pc) {
- if (islower((unsigned char)*pc)) {
- log_warn("Lowercase character in target name \"%s\".",
- pleaf->tgt);
- break;
- }
- }
- }
-
- // PCRE patterns
- if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) {
-#ifdef CONFIG_REGEX_PCRE
- const char *error = NULL;
- int erroffset = 0;
- int options = 0;
-
- // Ignore case flag
- if (pleaf->match_ignorecase)
- options |= PCRE_CASELESS;
-
- // Compile PCRE expression
- pleaf->regex_pcre =
- pcre_compile(pleaf->ptnstr, options, &error, &erroffset, NULL);
- if (!pleaf->regex_pcre) {
- log_error("Pattern \"%s\": PCRE regular expression parsing "
- "failed on "
- "offset %d: %s",
- pleaf->ptnstr, erroffset, error);
- return false;
- }
-#ifdef CONFIG_REGEX_PCRE_JIT
- pleaf->regex_pcre_extra =
- pcre_study(pleaf->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error);
- if (!pleaf->regex_pcre_extra) {
- printf("Pattern \"%s\": PCRE regular expression study failed: %s",
- pleaf->ptnstr, error);
- }
-#endif
-
- // Free the target string
- // free(pleaf->tgt);
- // pleaf->tgt = NULL;
-#else
- log_error("PCRE regular expression support not compiled in.");
- return false;
-#endif
- }
-
- return true;
-}
-
-static bool c2_tree_postprocess(session_t *ps, c2_ptr_t node) {
- if (!node.isbranch) {
- return c2_l_postprocess(ps, node.l);
- }
- if (!c2_tree_postprocess(ps, node.b->opr1))
- return false;
- return c2_tree_postprocess(ps, node.b->opr2);
-}
-
-bool c2_list_postprocess(session_t *ps, c2_lptr_t *list) {
- c2_lptr_t *head = list;
- while (head) {
- if (!c2_tree_postprocess(ps, head->ptr))
- return false;
- head = head->next;
- }
- return true;
-}
-/**
- * Free a condition tree.
- */
-static void c2_free(c2_ptr_t p) {
- // For a branch element
- if (p.isbranch) {
- c2_b_t *const pbranch = p.b;
-
- if (!pbranch)
- return;
-
- c2_free(pbranch->opr1);
- c2_free(pbranch->opr2);
- free(pbranch);
- }
- // For a leaf element
- else {
- c2_l_t *const pleaf = p.l;
-
- if (!pleaf)
- return;
-
- free(pleaf->tgt);
- free(pleaf->ptnstr);
-#ifdef CONFIG_REGEX_PCRE
- pcre_free(pleaf->regex_pcre);
- LPCRE_FREE_STUDY(pleaf->regex_pcre_extra);
-#endif
- free(pleaf);
- }
-}
-
-/**
- * Free a condition tree in c2_lptr_t.
- */
-c2_lptr_t *c2_free_lptr(c2_lptr_t *lp) {
- if (!lp)
- return NULL;
-
- c2_lptr_t *pnext = lp->next;
- c2_free(lp->ptr);
- free(lp);
-
- return pnext;
-}
-
-/**
- * Get a string representation of a rule target.
- */
-static const char *c2h_dump_str_tgt(const c2_l_t *pleaf) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- return C2_PREDEFS[pleaf->predef].name;
- } else {
- return pleaf->tgt;
- }
-}
-
-/**
- * Get a string representation of a target.
- */
-static const char *c2h_dump_str_type(const c2_l_t *pleaf) {
- switch (pleaf->type) {
- case C2_L_TWINDOW: return "w";
- case C2_L_TDRAWABLE: return "d";
- case C2_L_TCARDINAL: return "c";
- case C2_L_TSTRING: return "s";
- case C2_L_TATOM: return "a";
- case C2_L_TUNDEFINED: break;
- }
-
- return NULL;
-}
-
-/**
- * Dump a condition tree.
- */
-static void c2_dump(c2_ptr_t p) {
- // For a branch
- if (p.isbranch) {
- const c2_b_t *const pbranch = p.b;
-
- if (!pbranch) {
- return;
- }
-
- if (pbranch->neg) {
- putchar('!');
- }
-
- printf("(");
- c2_dump(pbranch->opr1);
-
- switch (pbranch->op) {
- case C2_B_OAND: printf(" && "); break;
- case C2_B_OOR: printf(" || "); break;
- case C2_B_OXOR: printf(" XOR "); break;
- default: assert(0); break;
- }
-
- c2_dump(pbranch->opr2);
- printf(") ");
- }
- // For a leaf
- else {
- const c2_l_t *const pleaf = p.l;
-
- if (!pleaf) {
- return;
- }
-
- if (C2_L_OEXISTS == pleaf->op && pleaf->neg) {
- putchar('!');
- }
-
- // Print target name, type, and format
- {
- printf("%s", c2h_dump_str_tgt(pleaf));
- if (pleaf->tgt_onframe) {
- putchar('@');
- }
- if (pleaf->predef == C2_L_PUNDEFINED) {
- if (pleaf->index < 0) {
- printf("[*]");
- } else {
- printf("[%d]", pleaf->index);
- }
- }
- printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf));
- }
-
- // Print operator
- putchar(' ');
-
- if (C2_L_OEXISTS != pleaf->op && pleaf->neg) {
- putchar('!');
- }
-
- switch (pleaf->match) {
- case C2_L_MEXACT: break;
- case C2_L_MCONTAINS: putchar('*'); break;
- case C2_L_MSTART: putchar('^'); break;
- case C2_L_MPCRE: putchar('~'); break;
- case C2_L_MWILDCARD: putchar('%'); break;
- }
-
- if (pleaf->match_ignorecase) {
- putchar('?');
- }
-
- switch (pleaf->op) {
- case C2_L_OEXISTS: break;
- case C2_L_OEQ: fputs("=", stdout); break;
- case C2_L_OGT: fputs(">", stdout); break;
- case C2_L_OGTEQ: fputs(">=", stdout); break;
- case C2_L_OLT: fputs("<", stdout); break;
- case C2_L_OLTEQ: fputs("<=", stdout); break;
- }
-
- if (C2_L_OEXISTS == pleaf->op) {
- return;
- }
-
- // Print pattern
- putchar(' ');
- switch (pleaf->ptntype) {
- case C2_L_PTINT: printf("%ld", pleaf->ptnint); break;
- case C2_L_PTSTRING:
- // TODO(yshui) Escape string before printing out?
- printf("\"%s\"", pleaf->ptnstr);
- break;
- default: assert(0); break;
- }
- }
-}
-
-/**
- * Get the type atom of a condition.
- */
-static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf) {
- switch (pleaf->type) {
- case C2_L_TCARDINAL: return XCB_ATOM_CARDINAL;
- case C2_L_TWINDOW: return XCB_ATOM_WINDOW;
- case C2_L_TSTRING: return XCB_ATOM_STRING;
- case C2_L_TATOM: return XCB_ATOM_ATOM;
- case C2_L_TDRAWABLE: return XCB_ATOM_DRAWABLE;
- default: assert(0); break;
- }
- unreachable;
-}
-
-/**
- * Match a window against a single leaf window condition.
- *
- * For internal use.
- */
-static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w,
- const c2_l_t *pleaf, bool *pres, bool *perr) {
- assert(pleaf);
-
- const xcb_window_t wid = (pleaf->tgt_onframe ? w->client_win : w->base.id);
-
- // Return if wid is missing
- if (pleaf->predef == C2_L_PUNDEFINED && !wid) {
- return;
- }
-
- const int idx = (pleaf->index < 0 ? 0 : pleaf->index);
-
- switch (pleaf->ptntype) {
- // Deal with integer patterns
- case C2_L_PTINT: {
- long *targets = NULL;
- long *targets_free = NULL;
- size_t ntargets = 0;
-
- // Get the value
- // A predefined target
- long predef_target = 0;
- if (pleaf->predef != C2_L_PUNDEFINED) {
- *perr = false;
- switch (pleaf->predef) {
- case C2_L_PID: predef_target = wid; break;
- case C2_L_PX: predef_target = w->g.x; break;
- case C2_L_PY: predef_target = w->g.y; break;
- case C2_L_PX2: predef_target = w->g.x + w->widthb; break;
- case C2_L_PY2: predef_target = w->g.y + w->heightb; break;
- case C2_L_PWIDTH: predef_target = w->g.width; break;
- case C2_L_PHEIGHT: predef_target = w->g.height; break;
- case C2_L_PWIDTHB: predef_target = w->widthb; break;
- case C2_L_PHEIGHTB: predef_target = w->heightb; break;
- case C2_L_PBDW: predef_target = w->g.border_width; break;
- case C2_L_PFULLSCREEN:
- predef_target = win_is_fullscreen(ps, w);
- break;
- case C2_L_POVREDIR: predef_target = w->a.override_redirect; break;
- case C2_L_PARGB: predef_target = win_has_alpha(w); break;
- case C2_L_PFOCUSED:
- predef_target = win_is_focused_raw(ps, w);
- break;
- case C2_L_PWMWIN: predef_target = w->wmwin; break;
- case C2_L_PBSHAPED: predef_target = w->bounding_shaped; break;
- case C2_L_PROUNDED: predef_target = w->rounded_corners; break;
- case C2_L_PCLIENT: predef_target = w->client_win; break;
- case C2_L_PLEADER: predef_target = w->leader; break;
- default:
- *perr = true;
- assert(0);
- break;
- }
- ntargets = 1;
- targets = &predef_target;
- }
- // A raw window property
- else {
- int word_count = 1;
- if (pleaf->index < 0) {
- // Get length of property in 32-bit multiples
- auto prop_info = x_get_prop_info(ps->c, wid, pleaf->tgtatom);
- word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
- }
- winprop_t prop = x_get_prop_with_offset(
- ps->c, wid, pleaf->tgtatom, idx, word_count,
- c2_get_atom_type(pleaf), pleaf->format);
-
- ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
- if (ntargets > 0) {
- targets = targets_free = ccalloc(ntargets, long);
- *perr = false;
- for (size_t i = 0; i < ntargets; ++i) {
- targets[i] = winprop_get_int(prop, i);
- }
- }
- free_winprop(&prop);
- }
-
- if (*perr) {
- goto fail_int;
- }
-
- // Do comparison
- bool res = false;
- for (size_t i = 0; i < ntargets; ++i) {
- long tgt = targets[i];
- switch (pleaf->op) {
- case C2_L_OEXISTS:
- res = (pleaf->predef != C2_L_PUNDEFINED ? tgt : true);
- break;
- case C2_L_OEQ: res = (tgt == pleaf->ptnint); break;
- case C2_L_OGT: res = (tgt > pleaf->ptnint); break;
- case C2_L_OGTEQ: res = (tgt >= pleaf->ptnint); break;
- case C2_L_OLT: res = (tgt < pleaf->ptnint); break;
- case C2_L_OLTEQ: res = (tgt <= pleaf->ptnint); break;
- default: *perr = true; assert(0);
- }
- if (res) {
- break;
- }
- }
- *pres = res;
-
- fail_int:
- // Free property values after usage, if necessary
- if (targets_free) {
- free(targets_free);
- }
- } break;
- // String patterns
- case C2_L_PTSTRING: {
- const char **targets = NULL;
- const char **targets_free = NULL;
- const char **targets_free_inner = NULL;
- size_t ntargets = 0;
-
- // A predefined target
- const char *predef_target = NULL;
- if (pleaf->predef != C2_L_PUNDEFINED) {
- switch (pleaf->predef) {
- case C2_L_PWINDOWTYPE:
- predef_target = WINTYPES[w->window_type];
- break;
- case C2_L_PNAME: predef_target = w->name; break;
- case C2_L_PCLASSG: predef_target = w->class_general; break;
- case C2_L_PCLASSI: predef_target = w->class_instance; break;
- case C2_L_PROLE: predef_target = w->role; break;
- default: assert(0); break;
- }
- ntargets = 1;
- targets = &predef_target;
- }
- // An atom type property, convert it to string
- else if (pleaf->type == C2_L_TATOM) {
- int word_count = 1;
- if (pleaf->index < 0) {
- // Get length of property in 32-bit multiples
- auto prop_info = x_get_prop_info(ps->c, wid, pleaf->tgtatom);
- word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
- }
- winprop_t prop = x_get_prop_with_offset(
- ps->c, wid, pleaf->tgtatom, idx, word_count,
- c2_get_atom_type(pleaf), pleaf->format);
-
- ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
- targets = targets_free = (const char **)ccalloc(2 * ntargets, char *);
- targets_free_inner = targets + ntargets;
-
- for (size_t i = 0; i < ntargets; ++i) {
- xcb_atom_t atom = (xcb_atom_t)winprop_get_int(prop, i);
- if (atom) {
- xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(
- ps->c, xcb_get_atom_name(ps->c, atom), NULL);
- if (reply) {
- targets[i] = targets_free_inner[i] = strndup(
- xcb_get_atom_name_name(reply),
- (size_t)xcb_get_atom_name_name_length(reply));
- free(reply);
- }
- }
- }
- free_winprop(&prop);
- }
- // Not an atom type, just fetch the string list
- else {
- char **strlst = NULL;
- int nstr = 0;
- if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, &nstr)) {
- if (pleaf->index < 0 && nstr > 0 && strlen(strlst[0]) > 0) {
- ntargets = to_u32_checked(nstr);
- targets = (const char **)strlst;
- } else if (nstr > idx) {
- ntargets = 1;
- targets = (const char **)strlst + idx;
- }
- }
- if (strlst) {
- targets_free = (const char **)strlst;
- }
- }
-
- if (ntargets == 0) {
- goto fail_str;
- }
- for (size_t i = 0; i < ntargets; ++i) {
- if (!targets[i]) {
- goto fail_str;
- }
- }
- *perr = false;
-
- // Actual matching
- bool res = false;
- for (size_t i = 0; i < ntargets; ++i) {
- const char *tgt = targets[i];
- switch (pleaf->op) {
- case C2_L_OEXISTS: res = true; break;
- case C2_L_OEQ:
- switch (pleaf->match) {
- case C2_L_MEXACT:
- if (pleaf->match_ignorecase) {
- res = !strcasecmp(tgt, pleaf->ptnstr);
- } else {
- res = !strcmp(tgt, pleaf->ptnstr);
- }
- break;
- case C2_L_MCONTAINS:
- if (pleaf->match_ignorecase) {
- res = strcasestr(tgt, pleaf->ptnstr);
- } else {
- res = strstr(tgt, pleaf->ptnstr);
- }
- break;
- case C2_L_MSTART:
- if (pleaf->match_ignorecase) {
- res = !strncasecmp(tgt, pleaf->ptnstr,
- strlen(pleaf->ptnstr));
- } else {
- res = !strncmp(tgt, pleaf->ptnstr,
- strlen(pleaf->ptnstr));
- }
- break;
- case C2_L_MWILDCARD: {
- int flags = 0;
- if (pleaf->match_ignorecase) {
- flags |= FNM_CASEFOLD;
- }
- res = !fnmatch(pleaf->ptnstr, tgt, flags);
- } break;
- case C2_L_MPCRE:
-#ifdef CONFIG_REGEX_PCRE
- assert(strlen(tgt) <= INT_MAX);
- res = (pcre_exec(pleaf->regex_pcre,
- pleaf->regex_pcre_extra, tgt,
- (int)strlen(tgt), 0, 0, NULL, 0) >= 0);
-#else
- assert(0);
-#endif
- break;
- }
- break;
- default: *perr = true; assert(0);
- }
- if (res) {
- break;
- }
- }
- *pres = res;
-
- fail_str:
- // Free the string after usage, if necessary
- if (targets_free_inner) {
- for (size_t i = 0; i < ntargets; ++i) {
- if (targets_free_inner[i]) {
- free((void *)targets_free_inner[i]);
- }
- }
- }
- // Free property values after usage, if necessary
- if (targets_free) {
- free(targets_free);
- }
- } break;
- default: assert(0); break;
- }
-}
-
-/**
- * Match a window against a single window condition.
- *
- * @return true if matched, false otherwise.
- */
-static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_ptr_t cond) {
- bool result = false;
- bool error = true;
-
- // Handle a branch
- if (cond.isbranch) {
- const c2_b_t *pb = cond.b;
-
- if (!pb)
- return false;
-
- error = false;
-
- switch (pb->op) {
- case C2_B_OAND:
- result = (c2_match_once(ps, w, pb->opr1) &&
- c2_match_once(ps, w, pb->opr2));
- break;
- case C2_B_OOR:
- result = (c2_match_once(ps, w, pb->opr1) ||
- c2_match_once(ps, w, pb->opr2));
- break;
- case C2_B_OXOR:
- result = (c2_match_once(ps, w, pb->opr1) !=
- c2_match_once(ps, w, pb->opr2));
- break;
- default: error = true; assert(0);
- }
-
-#ifdef DEBUG_WINMATCH
- log_trace("(%#010x): branch: result = %d, pattern = ", w->base.id, result);
- c2_dump(cond);
- putchar('\n');
-#endif
- }
- // Handle a leaf
- else {
- const c2_l_t *pleaf = cond.l;
-
- if (!pleaf)
- return false;
-
- c2_match_once_leaf(ps, w, pleaf, &result, &error);
-
- // For EXISTS operator, no errors are fatal
- if (C2_L_OEXISTS == pleaf->op && error) {
- result = false;
- error = false;
- }
-
-#ifdef DEBUG_WINMATCH
- log_trace("(%#010x): leaf: result = %d, error = %d, "
- "client = %#010x, pattern = ",
- w->base.id, result, error, w->client_win);
- c2_dump(cond);
- putchar('\n');
-#endif
- }
-
- // Postprocess the result
- if (error)
- result = false;
-
- if (cond.isbranch ? cond.b->neg : cond.l->neg)
- result = !result;
-
- return result;
-}
-
-/**
- * Match a window against a condition linked list.
- *
- * @param cache a place to cache the last matched condition
- * @param pdata a place to return the data
- * @return true if matched, false otherwise.
- */
-bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condlst,
- void **pdata) {
- assert(ps->server_grabbed);
- // Then go through the whole linked list
- for (; condlst; condlst = condlst->next) {
- if (c2_match_once(ps, w, condlst->ptr)) {
- if (pdata)
- *pdata = condlst->data;
- return true;
- }
- }
-
- return false;
-}
diff --git a/src/c2.h b/src/c2.h
deleted file mode 100644
index d6b1d37..0000000
--- a/src/c2.h
+++ /dev/null
@@ -1,26 +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.
- *
- */
-
-#pragma once
-
-#include <stdbool.h>
-
-typedef struct _c2_lptr c2_lptr_t;
-typedef struct session session_t;
-struct managed_win;
-
-c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
-
-c2_lptr_t *c2_free_lptr(c2_lptr_t *lp);
-
-bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condlst, void **pdata);
-
-bool c2_list_postprocess(session_t *ps, c2_lptr_t *list);
diff --git a/src/cache.c b/src/cache.c
deleted file mode 100644
index 1ffb31c..0000000
--- a/src/cache.c
+++ /dev/null
@@ -1,95 +0,0 @@
-#include <uthash.h>
-
-#include "compiler.h"
-#include "utils.h"
-#include "cache.h"
-
-struct cache_entry {
- char *key;
- void *value;
- UT_hash_handle hh;
-};
-
-struct cache {
- cache_getter_t getter;
- cache_free_t free;
- void *user_data;
- struct cache_entry *entries;
-};
-
-void cache_set(struct cache *c, const char *key, void *data) {
- struct cache_entry *e = NULL;
- HASH_FIND_STR(c->entries, key, e);
- CHECK(!e);
-
- e = ccalloc(1, struct cache_entry);
- e->key = strdup(key);
- e->value = data;
- HASH_ADD_STR(c->entries, key, e);
-}
-
-void *cache_get(struct cache *c, const char *key, int *err) {
- struct cache_entry *e;
- HASH_FIND_STR(c->entries, key, e);
- if (e) {
- return e->value;
- }
-
- int tmperr;
- if (!err) {
- err = &tmperr;
- }
-
- *err = 0;
- e = ccalloc(1, struct cache_entry);
- e->key = strdup(key);
- e->value = c->getter(c->user_data, key, err);
- if (*err) {
- free(e->key);
- free(e);
- return NULL;
- }
-
- HASH_ADD_STR(c->entries, key, e);
- return e->value;
-}
-
-static inline void _cache_invalidate(struct cache *c, struct cache_entry *e) {
- if (c->free) {
- c->free(c->user_data, e->value);
- }
- free(e->key);
- HASH_DEL(c->entries, e);
- free(e);
-}
-
-void cache_invalidate(struct cache *c, const char *key) {
- struct cache_entry *e;
- HASH_FIND_STR(c->entries, key, e);
-
- if (e) {
- _cache_invalidate(c, e);
- }
-}
-
-void cache_invalidate_all(struct cache *c) {
- struct cache_entry *e, *tmpe;
- HASH_ITER(hh, c->entries, e, tmpe) {
- _cache_invalidate(c, e);
- }
-}
-
-void *cache_free(struct cache *c) {
- void *ret = c->user_data;
- cache_invalidate_all(c);
- free(c);
- return ret;
-}
-
-struct cache *new_cache(void *ud, cache_getter_t getter, cache_free_t f) {
- auto c = ccalloc(1, struct cache);
- c->user_data = ud;
- c->getter = getter;
- c->free = f;
- return c;
-}
diff --git a/src/cache.h b/src/cache.h
deleted file mode 100644
index 3ca054f..0000000
--- a/src/cache.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-struct cache;
-
-typedef void *(*cache_getter_t)(void *user_data, const char *key, int *err);
-typedef void (*cache_free_t)(void *user_data, void *data);
-
-/// Create a cache with `getter`, and a free function `f` which is used to free the cache
-/// value when they are invalidated.
-///
-/// `user_data` will be passed to `getter` and `f` when they are called.
-struct cache *new_cache(void *user_data, cache_getter_t getter, cache_free_t f);
-
-/// Fetch a value from the cache. If the value doesn't present in the cache yet, the
-/// getter will be called, and the returned value will be stored into the cache.
-void *cache_get(struct cache *, const char *key, int *err);
-
-/// Invalidate a value in the cache.
-void cache_invalidate(struct cache *, const char *key);
-
-/// Invalidate all values in the cache.
-void cache_invalidate_all(struct cache *);
-
-/// Invalidate all values in the cache and free it. Returns the user data passed to
-/// `new_cache`
-void *cache_free(struct cache *);
-
-/// Insert a key-value pair into the cache. Only used for internal testing. Takes
-/// ownership of `data`
-///
-/// If `key` already exists in the cache, this function will abort the program.
-void cache_set(struct cache *c, const char *key, void *data);
diff --git a/src/common.h b/src/common.h
deleted file mode 100644
index b7f2fe0..0000000
--- a/src/common.h
+++ /dev/null
@@ -1,541 +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
- * Copyright (c) 2018, Yuxuan Shui <[email protected]>
- *
- * See LICENSE-mit for more information.
- *
- */
-
-#pragma once
-
-// === Options ===
-
-// Debug options, enable them using -D in CFLAGS
-// #define DEBUG_REPAINT 1
-// #define DEBUG_EVENTS 1
-// #define DEBUG_RESTACK 1
-// #define DEBUG_WINMATCH 1
-// #define DEBUG_C2 1
-// #define DEBUG_GLX_DEBUG_CONTEXT 1
-
-#define MAX_ALPHA (255)
-
-// === Includes ===
-
-// For some special functions
-#include <assert.h>
-#include <stdbool.h>
-#include <sys/time.h>
-#include <time.h>
-
-#include <X11/Xlib.h>
-#include <ev.h>
-#include <pixman.h>
-#include <xcb/xproto.h>
-#include <xcb/render.h>
-#include <xcb/sync.h>
-
-#include "uthash_extra.h"
-#ifdef CONFIG_OPENGL
-#include "backend/gl/glx.h"
-#endif
-
-// X resource checker
-#ifdef DEBUG_XRC
-#include "xrescheck.h"
-#endif
-
-// FIXME This list of includes should get shorter
-#include "backend/backend.h"
-#include "backend/driver.h"
-#include "compiler.h"
-#include "config.h"
-#include "region.h"
-#include "types.h"
-#include "utils.h"
-#include "list.h"
-#include "render.h"
-#include "win_defs.h"
-#include "x.h"
-
-// === Constants ===0
-
-#define NS_PER_SEC 1000000000L
-#define US_PER_SEC 1000000L
-#define MS_PER_SEC 1000
-
-/// @brief Maximum OpenGL FBConfig depth.
-#define OPENGL_MAX_DEPTH 32
-
-/// @brief Maximum OpenGL buffer age.
-#define CGLX_MAX_BUFFER_AGE 5
-
-// Window flags
-
-// === Types ===
-typedef struct glx_fbconfig glx_fbconfig_t;
-struct glx_session;
-struct atom;
-struct conv;
-
-typedef struct _ignore {
- struct _ignore *next;
- unsigned long sequence;
-} ignore_t;
-
-#ifdef CONFIG_OPENGL
-#ifdef DEBUG_GLX_DEBUG_CONTEXT
-typedef GLXContext (*f_glXCreateContextAttribsARB)(Display *dpy, GLXFBConfig config,
- GLXContext share_context, Bool direct,
- const int *attrib_list);
-typedef void (*GLDEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity,
- GLsizei length, const GLchar *message, GLvoid *userParam);
-typedef void (*f_DebugMessageCallback)(GLDEBUGPROC, void *userParam);
-#endif
-
-typedef struct glx_prog_main {
- /// GLSL program.
- GLuint prog;
- /// Location of uniform "opacity" in window GLSL program.
- GLint unifm_opacity;
- /// Location of uniform "invert_color" in blur GLSL program.
- GLint unifm_invert_color;
- /// Location of uniform "tex" in window GLSL program.
- GLint unifm_tex;
- /// Location of uniform "time" in window GLSL program.
- GLint unifm_time;
-} glx_prog_main_t;
-
-#define GLX_PROG_MAIN_INIT \
- { \
- .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, \
- .unifm_tex = -1, .unifm_time = -1 \
- }
-
-#else
-struct glx_prog_main {};
-#endif
-
-#define PAINT_INIT \
- { .pixmap = XCB_NONE, .pict = XCB_NONE }
-
-/// Linked list type of atoms.
-typedef struct _latom {
- xcb_atom_t atom;
- struct _latom *next;
-} latom_t;
-
-/// Structure containing all necessary data for a session.
-typedef struct session {
- // === Event handlers ===
- /// ev_io for X connection
- ev_io xiow;
- /// Timeout for delayed unredirection.
- ev_timer unredir_timer;
- /// Timer for fading
- ev_timer fade_timer;
- /// Timer for animations
- ev_timer animation_timer;
- /// Timer for delayed drawing, right now only used by
- /// swopti
- ev_timer delayed_draw_timer;
- /// Use an ev_idle callback for drawing
- /// So we only start drawing when events are processed
- ev_idle draw_idle;
- /// Called everytime we have timeouts or new data on socket,
- /// so we can be sure if xcb read from X socket at anytime during event
- /// handling, we will not left any event unhandled in the queue
- ev_prepare event_check;
- /// Signal handler for SIGUSR1
- ev_signal usr1_signal;
- /// Signal handler for SIGINT
- ev_signal int_signal;
- /// backend data
- backend_t *backend_data;
- /// backend blur context
- void *backend_blur_context;
- /// graphic drivers used
- enum driver drivers;
- /// file watch handle
- void *file_watch_handle;
- /// libev mainloop
- struct ev_loop *loop;
-
- // === Display related ===
- /// Whether the X server is grabbed by us
- bool server_grabbed;
- /// Display in use.
- Display *dpy;
- /// Previous handler of X errors
- XErrorHandler previous_xerror_handler;
- /// Default screen.
- int scr;
- /// XCB connection.
- xcb_connection_t *c;
- /// Default visual.
- xcb_visualid_t vis;
- /// Default depth.
- int depth;
- /// Root window.
- xcb_window_t root;
- /// Height of root window.
- int root_height;
- /// Width of root window.
- int root_width;
- /// Current desktop number of root window
- int root_desktop_num;
- /// Desktop switch direction
- int root_desktop_switch_direction;
- // Damage of root window.
- // Damage root_damage;
- /// X Composite overlay window. Used if <code>--paint-on-overlay</code>.
- xcb_window_t overlay;
- /// The target window for debug mode
- xcb_window_t debug_window;
- /// Whether the root tile is filled by us.
- bool root_tile_fill;
- /// Picture of the root window background.
- paint_t root_tile_paint;
- /// The backend data the root pixmap bound to
- void *root_image;
- /// A region of the size of the screen.
- region_t screen_reg;
- /// Picture of root window. Destination of painting in no-DBE painting
- /// mode.
- xcb_render_picture_t root_picture;
- /// A Picture acting as the painting target.
- xcb_render_picture_t tgt_picture;
- /// Temporary buffer to paint to before sending to display.
- paint_t tgt_buffer;
- /// Window ID of the window we register as a symbol.
- xcb_window_t reg_win;
-#ifdef CONFIG_OPENGL
- /// Pointer to GLX data.
- struct glx_session *psglx;
- /// Custom GLX program used for painting window.
- // XXX should be in struct glx_session
- glx_prog_main_t glx_prog_win;
- struct glx_fbconfig_info *argb_fbconfig;
-#endif
- /// Sync fence to sync draw operations
- xcb_sync_fence_t sync_fence;
- /// Whether we are rendering the first frame after screen is redirected
- bool first_frame;
-
- // === Operation related ===
- /// Flags related to the root window
- uint64_t root_flags;
- /// Program options.
- options_t o;
- /// Whether we have hit unredirection timeout.
- bool tmout_unredir_hit;
- /// Whether we need to redraw the screen
- bool redraw_needed;
-
- /// Cache a xfixes region so we don't need to allocate it everytime.
- /// A workaround for yshui/picom#301
- xcb_xfixes_region_t damaged_region;
- /// The region needs to painted on next paint.
- region_t *damage;
- /// The region damaged on the last paint.
- region_t *damage_ring;
- /// Number of damage regions we track
- int ndamage;
- /// Whether all windows are currently redirected.
- bool redirected;
- /// Pre-generated alpha pictures.
- xcb_render_picture_t *alpha_picts;
- /// Time of last fading. In milliseconds.
- long fade_time;
- /// Time of last window animation step. In milliseconds.
- long animation_time;
- /// Head pointer of the error ignore linked list.
- ignore_t *ignore_head;
- /// Pointer to the <code>next</code> member of tail element of the error
- /// ignore linked list.
- ignore_t **ignore_tail;
- // Cached blur convolution kernels.
- struct x_convolution_kernel **blur_kerns_cache;
- /// If we should quit
- bool quit:1;
- // TODO(yshui) use separate flags for dfferent kinds of updates so we don't
- // waste our time.
- /// Whether there are pending updates, like window creation, etc.
- bool pending_updates:1;
-
- // === Expose event related ===
- /// Pointer to an array of <code>XRectangle</code>-s of exposed region.
- /// XXX why do we need this array?
- rect_t *expose_rects;
- /// Number of <code>XRectangle</code>-s in <code>expose_rects</code>.
- int size_expose;
- /// Index of the next free slot in <code>expose_rects</code>.
- int n_expose;
-
- // === Window related ===
- /// A hash table of all windows.
- struct win *windows;
- /// Windows in their stacking order
- struct list_node window_stack;
- /// Pointer to <code>win</code> of current active window. Used by
- /// EWMH <code>_NET_ACTIVE_WINDOW</code> focus detection. In theory,
- /// it's more reliable to store the window ID directly here, just in
- /// case the WM does something extraordinary, but caching the pointer
- /// means another layer of complexity.
- struct managed_win *active_win;
- /// Window ID of leader window of currently active window. Used for
- /// subsidiary window detection.
- xcb_window_t active_leader;
-
- // === Shadow/dimming related ===
- /// 1x1 black Picture.
- xcb_render_picture_t black_picture;
- /// 1x1 Picture of the shadow color.
- xcb_render_picture_t cshadow_picture;
- /// 1x1 white Picture.
- xcb_render_picture_t white_picture;
- /// Gaussian map of shadow.
- struct conv *gaussian_map;
- // for shadow precomputation
- /// A region in which shadow is not painted on.
- region_t shadow_exclude_reg;
-
- // === Software-optimization-related ===
- /// Currently used refresh rate.
- int refresh_rate;
- /// Interval between refresh in nanoseconds.
- long refresh_intv;
- /// Nanosecond offset of the first painting.
- long paint_tm_offset;
-
-#ifdef CONFIG_VSYNC_DRM
- // === DRM VSync related ===
- /// File descriptor of DRI device file. Used for DRM VSync.
- int drm_fd;
-#endif
-
- // === X extension related ===
- /// Event base number for X Fixes extension.
- int xfixes_event;
- /// Error base number for X Fixes extension.
- int xfixes_error;
- /// Event base number for X Damage extension.
- int damage_event;
- /// Error base number for X Damage extension.
- int damage_error;
- /// Event base number for X Render extension.
- int render_event;
- /// Error base number for X Render extension.
- int render_error;
- /// Event base number for X Composite extension.
- int composite_event;
- /// Error base number for X Composite extension.
- int composite_error;
- /// Major opcode for X Composite extension.
- int composite_opcode;
- /// Whether X Shape extension exists.
- bool shape_exists;
- /// Event base number for X Shape extension.
- int shape_event;
- /// Error base number for X Shape extension.
- int shape_error;
- /// Whether X RandR extension exists.
- bool randr_exists;
- /// Event base number for X RandR extension.
- int randr_event;
- /// Error base number for X RandR extension.
- int randr_error;
- /// Whether X Present extension exists.
- bool present_exists;
- /// Whether X GLX extension exists.
- bool glx_exists;
- /// Event base number for X GLX extension.
- int glx_event;
- /// Error base number for X GLX extension.
- int glx_error;
- /// Whether X Xinerama extension exists.
- bool xinerama_exists;
- /// Xinerama screen regions.
- region_t *xinerama_scr_regs;
- /// Number of Xinerama screens.
- int xinerama_nscrs;
- /// Whether X Sync extension exists.
- bool xsync_exists;
- /// Event base number for X Sync extension.
- int xsync_event;
- /// Error base number for X Sync extension.
- int xsync_error;
- /// Whether X Render convolution filter exists.
- bool xrfilter_convolution_exists;
-
- // === Atoms ===
- struct atom *atoms;
- /// Array of atoms of all possible window types.
- xcb_atom_t atoms_wintypes[NUM_WINTYPES];
- /// Linked list of additional atoms to track.
- latom_t *track_atom_lst;
-
-#ifdef CONFIG_DBUS
- // === DBus related ===
- void *dbus_data;
-#endif
-
- int (*vsync_wait)(session_t *);
-} session_t;
-
-/// Enumeration for window event hints.
-typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, WIN_EVMODE_CLIENT } win_evmode_t;
-
-extern const char *const WINTYPES[NUM_WINTYPES];
-extern session_t *ps_g;
-
-void ev_xcb_error(session_t *ps, xcb_generic_error_t *err);
-
-// === Functions ===
-
-/**
- * Subtracting two struct timespec values.
- *
- * Taken from glibc manual.
- *
- * Subtract the `struct timespec' values X and Y,
- * storing the result in RESULT.
- * Return 1 if the difference is negative, otherwise 0.
- */
-static inline int
-timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y) {
- /* Perform the carry for the later subtraction by updating y. */
- if (x->tv_nsec < y->tv_nsec) {
- long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1;
- y->tv_nsec -= NS_PER_SEC * nsec;
- y->tv_sec += nsec;
- }
-
- if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) {
- long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC;
- y->tv_nsec += NS_PER_SEC * nsec;
- y->tv_sec -= nsec;
- }
-
- /* Compute the time remaining to wait.
- tv_nsec is certainly positive. */
- result->tv_sec = x->tv_sec - y->tv_sec;
- result->tv_nsec = x->tv_nsec - y->tv_nsec;
-
- /* Return 1 if result is negative. */
- return x->tv_sec < y->tv_sec;
-}
-
-/**
- * Get current time in struct timeval.
- */
-static inline struct timeval get_time_timeval(void) {
- struct timeval tv = {0, 0};
-
- gettimeofday(&tv, NULL);
-
- // Return a time of all 0 if the call fails
- return tv;
-}
-
-/**
- * Get current time in struct timespec.
- *
- * Note its starting time is unspecified.
- */
-static inline struct timespec get_time_timespec(void) {
- struct timespec tm = {0, 0};
-
- clock_gettime(CLOCK_MONOTONIC, &tm);
-
- // Return a time of all 0 if the call fails
- return tm;
-}
-
-/**
- * Return the painting target window.
- */
-static inline xcb_window_t get_tgt_window(session_t *ps) {
- return ps->overlay != XCB_NONE ? ps->overlay : ps->root;
-}
-
-/**
- * Check if current backend uses GLX.
- */
-static inline bool bkend_use_glx(session_t *ps) {
- return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
-}
-
-static void set_ignore(session_t *ps, unsigned long sequence) {
- if (ps->o.show_all_xerrors)
- return;
-
- auto i = cmalloc(ignore_t);
- if (!i)
- return;
-
- i->sequence = sequence;
- i->next = 0;
- *ps->ignore_tail = i;
- ps->ignore_tail = &i->next;
-}
-
-/**
- * Ignore X errors caused by given X request.
- */
-static inline void set_ignore_cookie(session_t *ps, xcb_void_cookie_t cookie) {
- set_ignore(ps, cookie.sequence);
-}
-
-/**
- * Determine if a window has a specific property.
- *
- * @param ps current session
- * @param w window to check
- * @param atom atom of property to check
- * @return true if it has the attribute, false otherwise
- */
-static inline bool wid_has_prop(const session_t *ps, xcb_window_t w, xcb_atom_t atom) {
- auto r = xcb_get_property_reply(
- ps->c, xcb_get_property(ps->c, 0, w, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, 0), NULL);
- if (!r) {
- return false;
- }
-
- auto rtype = r->type;
- free(r);
-
- if (rtype != XCB_NONE) {
- return true;
- }
- return false;
-}
-
-void force_repaint(session_t *ps);
-
-/** @name DBus handling
- */
-///@{
-#ifdef CONFIG_DBUS
-/** @name DBus hooks
- */
-///@{
-void opts_set_no_fading_openclose(session_t *ps, bool newval);
-//!@}
-#endif
-
-/**
- * Set a <code>bool</code> array of all wintypes to true.
- */
-static inline void wintype_arr_enable(bool arr[]) {
- wintype_t i;
-
- for (i = 0; i < NUM_WINTYPES; ++i) {
- arr[i] = true;
- }
-}
diff --git a/src/compiler.h b/src/compiler.h
deleted file mode 100644
index f146bd2..0000000
--- a/src/compiler.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-#pragma once
-
-#ifdef HAS_STDC_PREDEF_H
-#include <stdc-predef.h>
-#endif
-
-// clang-format off
-#define auto __auto_type
-#define likely(x) __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#define likely_if(x) if (likely(x))
-#define unlikely_if(x) if (unlikely(x))
-
-#ifndef __has_attribute
-# if __GNUC__ >= 4
-# define __has_attribute(x) 1
-# else
-# define __has_attribute(x) 0
-# endif
-#endif
-
-#if __has_attribute(const)
-# define attr_const __attribute__((const))
-#else
-# define attr_const
-#endif
-
-#if __has_attribute(format)
-# define attr_printf(a, b) __attribute__((format(printf, a, b)))
-#else
-# define attr_printf(a, b)
-#endif
-
-#if __has_attribute(pure)
-# define attr_pure __attribute__((pure))
-#else
-# define attr_pure
-#endif
-
-#if __has_attribute(unused)
-# define attr_unused __attribute__((unused))
-#else
-# define attr_unused
-#endif
-
-#if __has_attribute(warn_unused_result)
-# define attr_warn_unused_result __attribute__((warn_unused_result))
-#else
-# define attr_warn_unused_result
-#endif
-// An alias for conveninence
-#define must_use attr_warn_unused_result
-
-#if __has_attribute(nonnull)
-# define attr_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))
-# define attr_nonnull_all __attribute__((nonnull))
-#else
-# define attr_nonnull(...)
-# define attr_nonnull_all
-#endif
-
-#if __has_attribute(returns_nonnull)
-# define attr_ret_nonnull __attribute__((returns_nonnull))
-#else
-# define attr_ret_nonnull
-#endif
-
-#if __has_attribute(deprecated)
-# define attr_deprecated __attribute__((deprecated))
-#else
-# define attr_deprecated
-#endif
-
-#if __has_attribute(malloc)
-# define attr_malloc __attribute__((malloc))
-#else
-# define attr_malloc
-#endif
-
-#if __has_attribute(fallthrough)
-# define fallthrough() __attribute__((fallthrough))
-#else
-# define fallthrough()
-#endif
-
-#if __STDC_VERSION__ >= 201112L
-# define attr_noret _Noreturn
-#else
-# if __has_attribute(noreturn)
-# define attr_noret __attribute__((noreturn))
-# else
-# define attr_noret
-# endif
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-# define unreachable __builtin_unreachable()
-#else
-# define unreachable do {} while(0)
-#endif
-
-#ifndef __has_include
-# define __has_include(x) 0
-#endif
-
-#if !defined(__STDC_NO_THREADS__) && __has_include(<threads.h>)
-# include <threads.h>
-#elif __STDC_VERSION__ >= 201112L
-# define thread_local _Thread_local
-#elif defined(__GNUC__) || defined(__clang__)
-# define thread_local __thread
-#else
-# define thread_local _Pragma("GCC error \"No thread local storage support\"") __error__
-#endif
-// clang-format on
-
-typedef unsigned long ulong;
-typedef unsigned int uint;
-
-static inline int attr_const popcntul(unsigned long a) {
- return __builtin_popcountl(a);
-}
diff --git a/src/config.c b/src/config.c
deleted file mode 100644
index d24bf64..0000000
--- a/src/config.c
+++ /dev/null
@@ -1,650 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2011-2013, Christopher Jeffrey
-// Copyright (c) 2013 Richard Grenville <[email protected]>
-
-#include <ctype.h>
-#include <limits.h>
-#include <math.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/render.h> // for xcb_render_fixed_t, XXX
-
-#include "c2.h"
-#include "common.h"
-#include "compiler.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "string_utils.h"
-#include "types.h"
-#include "utils.h"
-#include "win.h"
-
-#include "config.h"
-
-/**
- * Parse a long number.
- */
-bool parse_long(const char *s, long *dest) {
- const char *endptr = NULL;
- long val = strtol(s, (char **)&endptr, 0);
- if (!endptr || endptr == s) {
- log_error("Invalid number: %s", s);
- return false;
- }
- while (isspace((unsigned char)*endptr))
- ++endptr;
- if (*endptr) {
- log_error("Trailing characters: %s", s);
- return false;
- }
- *dest = val;
- return true;
-}
-
-/**
- * Parse an int number.
- */
-bool parse_int(const char *s, int *dest) {
- long val;
- if (!parse_long(s, &val)) {
- return false;
- }
- if (val > INT_MAX || val < INT_MIN) {
- log_error("Number exceeded int limits: %ld", val);
- return false;
- }
- *dest = (int)val;
- return true;
-}
-
-/**
- * Parse a floating-point number in from a string,
- * also strips the trailing space and comma after the number.
- *
- * @param[in] src string to parse
- * @param[out] dest return the number parsed from the string
- * @return pointer to the last character parsed
- */
-const char *parse_readnum(const char *src, double *dest) {
- const char *pc = NULL;
- double val = strtod_simple(src, &pc);
- if (!pc || pc == src) {
- log_error("No number found: %s", src);
- return src;
- }
- while (*pc && (isspace((unsigned char)*pc) || *pc == ',')) {
- ++pc;
- }
- *dest = val;
- return pc;
-}
-
-enum blur_method parse_blur_method(const char *src) {
- if (strcmp(src, "kernel") == 0) {
- return BLUR_METHOD_KERNEL;
- } else if (strcmp(src, "box") == 0) {
- return BLUR_METHOD_BOX;
- } else if (strcmp(src, "gaussian") == 0) {
- return BLUR_METHOD_GAUSSIAN;
- } else if (strcmp(src, "dual_kawase") == 0) {
- return BLUR_METHOD_DUAL_KAWASE;
- } else if (strcmp(src, "kawase") == 0) {
- log_warn("Blur method 'kawase' has been renamed to 'dual_kawase'. "
- "Interpreted as 'dual_kawase', but this will stop working "
- "soon.");
- return BLUR_METHOD_DUAL_KAWASE;
- } else if (strcmp(src, "none") == 0) {
- return BLUR_METHOD_NONE;
- }
- return BLUR_METHOD_INVALID;
-}
-
-/**
- * Parse a matrix.
- *
- * @param[in] src the blur kernel string
- * @param[out] endptr return where the end of kernel is in the string
- * @param[out] hasneg whether the kernel has negative values
- */
-conv *parse_blur_kern(const char *src, const char **endptr, bool *hasneg) {
- int width = 0, height = 0;
- *hasneg = false;
-
- const char *pc = NULL;
-
- // Get matrix width and height
- double val = 0.0;
- if (src == (pc = parse_readnum(src, &val)))
- goto err1;
- src = pc;
- width = (int)val;
- if (src == (pc = parse_readnum(src, &val)))
- goto err1;
- src = pc;
- height = (int)val;
-
- // Validate matrix width and height
- if (width <= 0 || height <= 0) {
- log_error("Blue kernel width/height can't be negative.");
- goto err1;
- }
- if (!(width % 2 && height % 2)) {
- log_error("Blur kernel width/height must be odd.");
- goto err1;
- }
- if (width > 16 || height > 16)
- log_warn("Blur kernel width/height too large, may slow down"
- "rendering, and/or consume lots of memory");
-
- // Allocate memory
- conv *matrix = cvalloc(sizeof(conv) + (size_t)(width * height) * sizeof(double));
-
- // Read elements
- int skip = height / 2 * width + width / 2;
- for (int i = 0; i < width * height; ++i) {
- // Ignore the center element
- if (i == skip) {
- matrix->data[i] = 1;
- continue;
- }
- if (src == (pc = parse_readnum(src, &val))) {
- goto err2;
- }
- src = pc;
- if (val < 0) {
- *hasneg = true;
- }
- matrix->data[i] = val;
- }
-
- // Detect trailing characters
- for (; *pc && *pc != ';'; pc++) {
- if (!isspace((unsigned char)*pc) && *pc != ',') {
- // TODO(yshui) isspace is locale aware, be careful
- log_error("Trailing characters in blur kernel string.");
- goto err2;
- }
- }
-
- // Jump over spaces after ';'
- if (*pc == ';') {
- pc++;
- while (*pc && isspace((unsigned char)*pc)) {
- ++pc;
- }
- }
-
- // Require an end of string if endptr is not provided, otherwise
- // copy end pointer to endptr
- if (endptr) {
- *endptr = pc;
- } else if (*pc) {
- log_error("Only one blur kernel expected.");
- goto err2;
- }
-
- // Fill in width and height
- matrix->w = width;
- matrix->h = height;
- return matrix;
-
-err2:
- free(matrix);
-err1:
- return NULL;
-}
-
-/**
- * Parse a list of convolution kernels.
- *
- * @param[in] src string to parse
- * @param[out] hasneg whether any of the kernels have negative values
- * @return the kernels
- */
-struct conv **parse_blur_kern_lst(const char *src, bool *hasneg, int *count) {
- // TODO(yshui) just return a predefined kernels, not parse predefined strings...
- static const struct {
- const char *name;
- const char *kern_str;
- } CONV_KERN_PREDEF[] = {
- {"3x3box", "3,3,1,1,1,1,1,1,1,1,"},
- {"5x5box", "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,"},
- {"7x7box", "7,7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,"
- "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,"},
- {"3x3gaussian", "3,3,0.243117,0.493069,0.243117,0.493069,0.493069,0.243117,0."
- "493069,0.243117,"},
- {"5x5gaussian", "5,5,0.003493,0.029143,0.059106,0.029143,0.003493,0.029143,0."
- "243117,0.493069,0.243117,0.029143,0.059106,0.493069,0."
- "493069,0.059106,0.029143,0.243117,0.493069,0.243117,0."
- "029143,0.003493,0.029143,0.059106,0.029143,0.003493,"},
- {"7x7gaussian", "7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0."
- "000003,0.000102,0.003493,0.029143,0.059106,0.029143,0."
- "003493,0.000102,0.000849,0.029143,0.243117,0.493069,0."
- "243117,0.029143,0.000849,0.001723,0.059106,0.493069,0."
- "493069,0.059106,0.001723,0.000849,0.029143,0.243117,0."
- "493069,0.243117,0.029143,0.000849,0.000102,0.003493,0."
- "029143,0.059106,0.029143,0.003493,0.000102,0.000003,0."
- "000102,0.000849,0.001723,0.000849,0.000102,0.000003,"},
- {"9x9gaussian",
- "9,9,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0."
- "000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0."
- "000102,0.000003,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0."
- "029143,0.003493,0.000102,0.000001,0.000006,0.000849,0.029143,0.243117,0."
- "493069,0.243117,0.029143,0.000849,0.000006,0.000012,0.001723,0.059106,0."
- "493069,0.493069,0.059106,0.001723,0.000012,0.000006,0.000849,0.029143,0."
- "243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000001,0.000102,0."
- "003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0."
- "000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0."
- "000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0."
- "000000,"},
- {"11x11gaussian",
- "11,11,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0."
- "000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0."
- "000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0."
- "000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0."
- "000000,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0."
- "029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000006,0.000849,0."
- "029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0."
- "000000,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0."
- "000012,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0."
- "243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000001,0.000102,0."
- "003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0."
- "000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0."
- "000003,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0."
- "000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0."
- "000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0."
- "000000,"},
- };
-
- *count = 0;
- *hasneg = false;
- for (unsigned int i = 0;
- i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) {
- if (!strcmp(CONV_KERN_PREDEF[i].name, src))
- return parse_blur_kern_lst(CONV_KERN_PREDEF[i].kern_str, hasneg, count);
- }
-
- int nkernels = 1;
- for (int i = 0; src[i]; i++) {
- if (src[i] == ';') {
- nkernels++;
- }
- }
-
- struct conv **ret = ccalloc(nkernels, struct conv *);
-
- int i = 0;
- const char *pc = src;
-
- // Continue parsing until the end of source string
- i = 0;
- while (pc && *pc) {
- bool tmp_hasneg;
- assert(i < nkernels);
- ret[i] = parse_blur_kern(pc, &pc, &tmp_hasneg);
- if (!ret[i]) {
- for (int j = 0; j < i; j++) {
- free(ret[j]);
- }
- free(ret);
- return NULL;
- }
- i++;
- *hasneg |= tmp_hasneg;
- }
-
- if (i > 1) {
- log_warn("You are seeing this message because you are using "
- "multipass blur. Please report an issue to us so we know "
- "multipass blur is actually been used. Otherwise it might be "
- "removed in future releases");
- }
-
- *count = i;
-
- return ret;
-}
-
-/**
- * Parse a X geometry.
- *
- * ps->root_width and ps->root_height must be valid
- */
-bool parse_geometry(session_t *ps, const char *src, region_t *dest) {
- pixman_region32_clear(dest);
- if (!src) {
- return true;
- }
- if (!ps->root_width || !ps->root_height) {
- return true;
- }
-
- long x = 0, y = 0;
- long width = ps->root_width, height = ps->root_height;
- long val = 0L;
- char *endptr = NULL;
-
- src = skip_space(src);
- if (!*src) {
- goto parse_geometry_end;
- }
-
- // Parse width
- // Must be base 10, because "0x0..." may appear
- if (*src != '+' && *src != '-') {
- val = strtol(src, &endptr, 10);
- assert(endptr);
- if (src != endptr) {
- if (val < 0) {
- log_error("Invalid width: %s", src);
- return false;
- }
- width = val;
- src = endptr;
- }
- src = skip_space(src);
- }
-
- // Parse height
- if (*src == 'x') {
- ++src;
- val = strtol(src, &endptr, 10);
- assert(endptr);
- if (src != endptr) {
- if (val < 0) {
- log_error("Invalid height: %s", src);
- return false;
- }
- height = val;
- src = endptr;
- }
- src = skip_space(src);
- }
-
- // Parse x
- if (*src == '+' || *src == '-') {
- val = strtol(src, &endptr, 10);
- if (endptr && src != endptr) {
- x = val;
- if (*src == '-') {
- x += ps->root_width - width;
- }
- src = endptr;
- }
- src = skip_space(src);
- }
-
- // Parse y
- if (*src == '+' || *src == '-') {
- val = strtol(src, &endptr, 10);
- if (endptr && src != endptr) {
- y = val;
- if (*src == '-') {
- y += ps->root_height - height;
- }
- src = endptr;
- }
- src = skip_space(src);
- }
-
- if (*src) {
- log_error("Trailing characters: %s", src);
- return false;
- }
-
-parse_geometry_end:
- if (x < INT_MIN || x > INT_MAX || y < INT_MIN || y > INT_MAX) {
- log_error("Geometry coordinates exceeded limits: %s", src);
- return false;
- }
- if (width > UINT_MAX || height > UINT_MAX) {
- // less than 0 is checked for earlier
- log_error("Geometry size exceeded limits: %s", src);
- return false;
- }
- pixman_region32_union_rect(dest, dest, (int)x, (int)y, (uint)width, (uint)height);
- return true;
-}
-
-/**
- * Parse a list of opacity rules.
- */
-bool parse_rule_opacity(c2_lptr_t **res, const char *src) {
- // Find opacity value
- char *endptr = NULL;
- long val = strtol(src, &endptr, 0);
- if (!endptr || endptr == src) {
- log_error("No opacity specified: %s", src);
- return false;
- }
- if (val > 100 || val < 0) {
- log_error("Opacity %ld invalid: %s", val, src);
- return false;
- }
-
- // Skip over spaces
- while (*endptr && isspace((unsigned char)*endptr))
- ++endptr;
- if (':' != *endptr) {
- log_error("Opacity terminator not found: %s", src);
- return false;
- }
- ++endptr;
-
- // Parse pattern
- // I hope 1-100 is acceptable for (void *)
- return c2_parse(res, endptr, (void *)val);
-}
-
-/**
- * Add a pattern to a condition linked list.
- */
-bool condlst_add(c2_lptr_t **pcondlst, const char *pattern) {
- if (!pattern)
- return false;
-
- if (!c2_parse(pcondlst, pattern, NULL))
- exit(1);
-
- return true;
-}
-
-void set_default_winopts(options_t *opt, win_option_mask_t *mask, bool shadow_enable,
- bool fading_enable, bool blur_enable) {
- // Apply default wintype options.
- if (!mask[WINTYPE_DESKTOP].shadow) {
- // Desktop windows are always drawn without shadow by default.
- mask[WINTYPE_DESKTOP].shadow = true;
- opt->wintype_option[WINTYPE_DESKTOP].shadow = false;
- }
-
- // Focused/unfocused state only apply to a few window types, all other windows
- // are always considered focused.
- const wintype_t nofocus_type[] = {WINTYPE_UNKNOWN, WINTYPE_NORMAL, WINTYPE_UTILITY};
- for (unsigned long i = 0; i < ARR_SIZE(nofocus_type); i++) {
- if (!mask[nofocus_type[i]].focus) {
- mask[nofocus_type[i]].focus = true;
- opt->wintype_option[nofocus_type[i]].focus = false;
- }
- }
- for (unsigned long i = 0; i < NUM_WINTYPES; i++) {
- if (!mask[i].shadow) {
- mask[i].shadow = true;
- opt->wintype_option[i].shadow = shadow_enable;
- }
- if (!mask[i].fade) {
- mask[i].fade = true;
- opt->wintype_option[i].fade = fading_enable;
- }
- if (!mask[i].focus) {
- mask[i].focus = true;
- opt->wintype_option[i].focus = true;
- }
- if (!mask[i].blur_background) {
- mask[i].blur_background = true;
- opt->wintype_option[i].blur_background = blur_enable;
- }
- if (!mask[i].full_shadow) {
- mask[i].full_shadow = true;
- opt->wintype_option[i].full_shadow = false;
- }
- if (!mask[i].redir_ignore) {
- mask[i].redir_ignore = true;
- opt->wintype_option[i].redir_ignore = false;
- }
- if (!mask[i].opacity) {
- mask[i].opacity = true;
- // Opacity is not set to a concrete number here because the
- // opacity logic is complicated, and needs an "unset" state
- opt->wintype_option[i].opacity = NAN;
- }
- if (!mask[i].animation) {
- mask[i].animation = OPEN_WINDOW_ANIMATION_INVALID;
- opt->wintype_option[i].animation = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (!mask[i].animation_unmap) {
- mask[i].animation_unmap = OPEN_WINDOW_ANIMATION_INVALID;
- opt->wintype_option[i].animation_unmap = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (!mask[i].animation_workspace_in) {
- mask[i].animation_workspace_in = OPEN_WINDOW_ANIMATION_INVALID;
- opt->wintype_option[i].animation_workspace_in = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (!mask[i].animation_workspace_out) {
- mask[i].animation_workspace_out = OPEN_WINDOW_ANIMATION_INVALID;
- opt->wintype_option[i].animation_workspace_out = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (!mask[i].clip_shadow_above) {
- mask[i].clip_shadow_above = true;
- opt->wintype_option[i].clip_shadow_above = false;
- }
- }
-}
-
-enum open_window_animation parse_open_window_animation(const char *src) {
- if (strcmp(src, "none") == 0) {
- return OPEN_WINDOW_ANIMATION_NONE;
- }else if (strcmp(src, "auto") == 0) {
- return OPEN_WINDOW_ANIMATION_AUTO;
- } else if (strcmp(src, "fly-in") == 0) {
- return OPEN_WINDOW_ANIMATION_FLYIN;
- } else if (strcmp(src, "zoom") == 0) {
- return OPEN_WINDOW_ANIMATION_ZOOM;
- } else if (strcmp(src, "slide-up") == 0) {
- return OPEN_WINDOW_ANIMATION_SLIDE_UP;
- } else if (strcmp(src, "slide-down") == 0) {
- return OPEN_WINDOW_ANIMATION_SLIDE_DOWN;
- } else if (strcmp(src, "slide-left") == 0) {
- return OPEN_WINDOW_ANIMATION_SLIDE_LEFT;
- } else if (strcmp(src, "slide-right") == 0) {
- return OPEN_WINDOW_ANIMATION_SLIDE_RIGHT;
- }
- return OPEN_WINDOW_ANIMATION_INVALID;
-}
-
-char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
- bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask) {
- // clang-format off
- *opt = (struct options){
- .backend = BKEND_XRENDER,
- .glx_no_stencil = false,
- .mark_wmwin_focused = false,
- .mark_ovredir_focused = false,
- .detect_rounded_corners = false,
- .resize_damage = 0,
- .unredir_if_possible = false,
- .unredir_if_possible_blacklist = NULL,
- .unredir_if_possible_delay = 0,
- .redirected_force = UNSET,
- .stoppaint_force = UNSET,
- .dbus = false,
- .benchmark = 0,
- .benchmark_wid = XCB_NONE,
- .logpath = NULL,
-
- .refresh_rate = 0,
- .sw_opti = false,
- .use_damage = true,
-
- .shadow_red = 0.0,
- .shadow_green = 0.0,
- .shadow_blue = 0.0,
- .shadow_radius = 18,
- .shadow_offset_x = -15,
- .shadow_offset_y = -15,
- .shadow_opacity = .75,
- .shadow_blacklist = NULL,
- .shadow_ignore_shaped = false,
- .xinerama_shadow_crop = false,
- .shadow_clip_list = NULL,
-
- .corner_radius = 0,
-
- .fade_in_step = 0.028,
- .fade_out_step = 0.03,
- .fade_delta = 10,
- .no_fading_openclose = false,
- .no_fading_destroyed_argb = false,
- .fade_blacklist = NULL,
-
- .animations = false,
- .animation_for_open_window = OPEN_WINDOW_ANIMATION_NONE,
- .animation_for_transient_window = OPEN_WINDOW_ANIMATION_NONE,
- .animation_for_unmap_window = OPEN_WINDOW_ANIMATION_AUTO,
- .animation_for_workspace_switch_in = OPEN_WINDOW_ANIMATION_AUTO,
- .animation_for_workspace_switch_out = OPEN_WINDOW_ANIMATION_AUTO,
- .animation_stiffness = 200.0,
- .animation_window_mass = 1.0,
- .animation_dampening = 25,
- .animation_delta = 10,
- .animation_force_steps = false,
- .animation_clamping = true,
-
- .inactive_opacity = 1.0,
- .inactive_opacity_override = false,
- .active_opacity = 1.0,
- .frame_opacity = 1.0,
- .detect_client_opacity = false,
-
- .blur_method = BLUR_METHOD_NONE,
- .blur_radius = 3,
- .blur_deviation = 0.84089642,
- .blur_strength = 5,
- .blur_background_frame = false,
- .blur_background_fixed = false,
- .blur_background_blacklist = NULL,
- .blur_kerns = NULL,
- .blur_kernel_count = 0,
- .inactive_dim = 0.0,
- .inactive_dim_fixed = false,
- .invert_color_list = NULL,
- .opacity_rules = NULL,
- .max_brightness = 1.0,
-
- .use_ewmh_active_win = false,
- .focus_blacklist = NULL,
- .detect_transient = false,
- .detect_client_leader = false,
- .no_ewmh_fullscreen = false,
-
- .track_leader = false,
-
- .rounded_corners_blacklist = NULL
- };
- // clang-format on
-
- char *ret = NULL;
-#ifdef CONFIG_LIBCONFIG
- ret = parse_config_libconfig(opt, config_file, shadow_enable, fading_enable,
- hasneg, winopt_mask);
-#else
- (void)config_file;
- (void)shadow_enable;
- (void)fading_enable;
- (void)hasneg;
- (void)winopt_mask;
-#endif
- return ret;
-}
diff --git a/src/config.h b/src/config.h
deleted file mode 100644
index a22d222..0000000
--- a/src/config.h
+++ /dev/null
@@ -1,380 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2011-2013, Christopher Jeffrey
-// Copyright (c) 2013 Richard Grenville <[email protected]>
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-#pragma once
-
-/// Common functions and definitions for configuration parsing
-/// Used for command line arguments and config files
-
-#include <ctype.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/render.h> // for xcb_render_fixed_t, XXX
-#include <xcb/xcb.h>
-#include <xcb/xfixes.h>
-
-#ifdef CONFIG_LIBCONFIG
-#include <libconfig.h>
-#endif
-
-#include "compiler.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "types.h"
-#include "win_defs.h"
-
-typedef struct session session_t;
-
-/// @brief Possible backends
-enum backend {
- BKEND_XRENDER,
- BKEND_GLX,
- BKEND_XR_GLX_HYBRID,
- BKEND_DUMMY,
- NUM_BKEND,
-};
-
-enum open_window_animation {
- OPEN_WINDOW_ANIMATION_NONE = 0,
- OPEN_WINDOW_ANIMATION_AUTO,
- OPEN_WINDOW_ANIMATION_FLYIN,
- OPEN_WINDOW_ANIMATION_ZOOM,
- OPEN_WINDOW_ANIMATION_SLIDE_UP,
- OPEN_WINDOW_ANIMATION_SLIDE_DOWN,
- OPEN_WINDOW_ANIMATION_SLIDE_LEFT,
- OPEN_WINDOW_ANIMATION_SLIDE_RIGHT,
- OPEN_WINDOW_ANIMATION_SLIDE_IN,
- OPEN_WINDOW_ANIMATION_SLIDE_OUT,
- OPEN_WINDOW_ANIMATION_INVALID,
-};
-
-typedef struct win_option_mask {
- bool shadow : 1;
- bool fade : 1;
- bool focus : 1;
- bool blur_background : 1;
- bool full_shadow : 1;
- bool redir_ignore : 1;
- bool opacity : 1;
- bool clip_shadow_above : 1;
- enum open_window_animation animation;
- enum open_window_animation animation_unmap;
- enum open_window_animation animation_workspace_in;
- enum open_window_animation animation_workspace_out;
-} win_option_mask_t;
-
-typedef struct win_option {
- bool shadow;
- bool fade;
- bool focus;
- bool blur_background;
- bool full_shadow;
- bool redir_ignore;
- double opacity;
- bool clip_shadow_above;
- enum open_window_animation animation;
- enum open_window_animation animation_unmap;
- enum open_window_animation animation_workspace_in;
- enum open_window_animation animation_workspace_out;
-} win_option_t;
-
-enum blur_method {
- BLUR_METHOD_NONE = 0,
- BLUR_METHOD_KERNEL,
- BLUR_METHOD_BOX,
- BLUR_METHOD_GAUSSIAN,
- BLUR_METHOD_DUAL_KAWASE,
- BLUR_METHOD_INVALID,
-};
-
-typedef struct _c2_lptr c2_lptr_t;
-
-/// Structure representing all options.
-typedef struct options {
- // === Debugging ===
- bool monitor_repaint;
- bool print_diagnostics;
- /// Render to a separate window instead of taking over the screen
- bool debug_mode;
- // === General ===
- /// Use the experimental new backends?
- bool experimental_backends;
- /// Path to write PID to.
- char *write_pid_path;
- /// The backend in use.
- enum backend backend;
- /// Whether to sync X drawing with X Sync fence to avoid certain delay
- /// issues with GLX backend.
- bool xrender_sync_fence;
- /// Whether to avoid using stencil buffer under GLX backend. Might be
- /// unsafe.
- bool glx_no_stencil;
- /// Whether to avoid rebinding pixmap on window damage.
- bool glx_no_rebind_pixmap;
- /// Custom fragment shader for painting windows, as a string.
- char *glx_fshader_win_str;
- /// Whether to detect rounded corners.
- bool detect_rounded_corners;
- /// Force painting of window content with blending.
- bool force_win_blend;
- /// Resize damage for a specific number of pixels.
- int resize_damage;
- /// Whether to unredirect all windows if a full-screen opaque window
- /// is detected.
- bool unredir_if_possible;
- /// List of conditions of windows to ignore as a full-screen window
- /// when determining if a window could be unredirected.
- c2_lptr_t *unredir_if_possible_blacklist;
- /// Delay before unredirecting screen, in milliseconds.
- long unredir_if_possible_delay;
- /// Forced redirection setting through D-Bus.
- switch_t redirected_force;
- /// Whether to stop painting. Controlled through D-Bus.
- switch_t stoppaint_force;
- /// Whether to enable D-Bus support.
- bool dbus;
- /// Path to log file.
- char *logpath;
- /// Number of cycles to paint in benchmark mode. 0 for disabled.
- int benchmark;
- /// Window to constantly repaint in benchmark mode. 0 for full-screen.
- xcb_window_t benchmark_wid;
- /// A list of conditions of windows not to paint.
- c2_lptr_t *paint_blacklist;
- /// Whether to show all X errors.
- bool show_all_xerrors;
- /// Whether to avoid acquiring X Selection.
- bool no_x_selection;
- /// Window type option override.
- win_option_t wintype_option[NUM_WINTYPES];
-
- // === VSync & software optimization ===
- /// User-specified refresh rate.
- int refresh_rate;
- /// Whether to enable refresh-rate-based software optimization.
- bool sw_opti;
- /// VSync method to use;
- bool vsync;
- /// Whether to use glFinish() instead of glFlush() for (possibly) better
- /// VSync yet probably higher CPU usage.
- bool vsync_use_glfinish;
- /// Whether use damage information to help limit the area to paint
- bool use_damage;
-
- // === Shadow ===
- /// Red, green and blue tone of the shadow.
- double shadow_red, shadow_green, shadow_blue;
- int shadow_radius;
- int shadow_offset_x, shadow_offset_y;
- double shadow_opacity;
- /// argument string to shadow-exclude-reg option
- char *shadow_exclude_reg_str;
- /// Shadow blacklist. A linked list of conditions.
- c2_lptr_t *shadow_blacklist;
- /// Whether bounding-shaped window should be ignored.
- bool shadow_ignore_shaped;
- /// Whether to crop shadow to the very Xinerama screen.
- bool xinerama_shadow_crop;
- /// Don't draw shadow over these windows. A linked list of conditions.
- c2_lptr_t *shadow_clip_list;
-
- // === Fading ===
- /// How much to fade in in a single fading step.
- double fade_in_step;
- /// How much to fade out in a single fading step.
- double fade_out_step;
- /// Fading time delta. In milliseconds.
- int fade_delta;
- /// Whether to disable fading on window open/close.
- bool no_fading_openclose;
- /// Whether to disable fading on ARGB managed destroyed windows.
- bool no_fading_destroyed_argb;
- /// Fading blacklist. A linked list of conditions.
- c2_lptr_t *fade_blacklist;
-
- // === Animations ===
- /// Whether to do window animations
- bool animations;
- /// Which animation to run when opening a window
- enum open_window_animation animation_for_open_window;
- /// Which animation to run when opening a transient window
- enum open_window_animation animation_for_transient_window;
- /// Which animation to run when unmapping (e.g. minimizing) a window
- enum open_window_animation animation_for_unmap_window;
- /// Which animation to run when switching workspace
- /// IMPORTANT: will only work if window manager updates
- /// _NET_CURRENT_DESKTOP before doing the hide/show of windows
- enum open_window_animation animation_for_workspace_switch_in;
- enum open_window_animation animation_for_workspace_switch_out;
- /// Spring stiffness for animation
- double animation_stiffness;
- /// Window mass for animation
- double animation_window_mass;
- /// Animation dampening
- double animation_dampening;
- /// Animation delta. In milliseconds.
- double animation_delta;
- /// Whether to force animations to not miss a beat
- bool animation_force_steps;
- /// Whether to clamp animations
- bool animation_clamping;
- /// TODO: window animation blacklist
- /// TODO: open/close animations
-
- // === Opacity ===
- /// Default opacity for inactive windows.
- /// 32-bit integer with the format of _NET_WM_OPACITY.
- double inactive_opacity;
- /// Default opacity for inactive windows.
- double active_opacity;
- /// Whether inactive_opacity overrides the opacity set by window
- /// attributes.
- bool inactive_opacity_override;
- /// Frame opacity. Relative to window opacity, also affects shadow
- /// opacity.
- double frame_opacity;
- /// Whether to detect _NET_WM_OPACITY on client windows. Used on window
- /// managers that don't pass _NET_WM_OPACITY to frame windows.
- bool detect_client_opacity;
-
- // === Other window processing ===
- /// Blur method for background of semi-transparent windows
- enum blur_method blur_method;
- // Size of the blur kernel
- int blur_radius;
- // Standard deviation for the gaussian blur
- double blur_deviation;
- // Strength of the dual_kawase blur
- int blur_strength;
- /// Whether to blur background when the window frame is not opaque.
- /// Implies blur_background.
- bool blur_background_frame;
- /// Whether to use fixed blur strength instead of adjusting according
- /// to window opacity.
- bool blur_background_fixed;
- /// Background blur blacklist. A linked list of conditions.
- c2_lptr_t *blur_background_blacklist;
- /// Blur convolution kernel.
- struct conv **blur_kerns;
- /// Number of convolution kernels
- int blur_kernel_count;
- /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable.
- double inactive_dim;
- /// Whether to use fixed inactive dim opacity, instead of deciding
- /// based on window opacity.
- bool inactive_dim_fixed;
- /// Conditions of windows to have inverted colors.
- c2_lptr_t *invert_color_list;
- /// Rules to change window opacity.
- c2_lptr_t *opacity_rules;
- /// Limit window brightness
- double max_brightness;
- // Radius of rounded window corners
- int corner_radius;
- /// Rounded corners blacklist. A linked list of conditions.
- c2_lptr_t *rounded_corners_blacklist;
-
- // === Focus related ===
- /// Whether to try to detect WM windows and mark them as focused.
- bool mark_wmwin_focused;
- /// Whether to mark override-redirect windows as focused.
- bool mark_ovredir_focused;
- /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window.
- bool use_ewmh_active_win;
- /// A list of windows always to be considered focused.
- c2_lptr_t *focus_blacklist;
- /// Whether to do window grouping with <code>WM_TRANSIENT_FOR</code>.
- bool detect_transient;
- /// Whether to do window grouping with <code>WM_CLIENT_LEADER</code>.
- bool detect_client_leader;
-
- // === Calculated ===
- /// Whether we need to track window leaders.
- bool track_leader;
-
- // Don't use EWMH to detect fullscreen applications
- bool no_ewmh_fullscreen;
-
- // Make transparent windows clip other windows, instead of blending on top of
- // them
- bool transparent_clipping;
-} options_t;
-
-extern const char *const BACKEND_STRS[NUM_BKEND + 1];
-
-bool must_use parse_long(const char *, long *);
-bool must_use parse_int(const char *, int *);
-struct conv **must_use parse_blur_kern_lst(const char *, bool *hasneg, int *count);
-bool must_use parse_geometry(session_t *, const char *, region_t *);
-bool must_use parse_rule_opacity(c2_lptr_t **, const char *);
-enum blur_method must_use parse_blur_method(const char *src);
-enum open_window_animation must_use parse_open_window_animation(const char *src);
-
-/**
- * Add a pattern to a condition linked list.
- */
-bool condlst_add(c2_lptr_t **, const char *);
-
-#ifdef CONFIG_LIBCONFIG
-/// Parse a configuration file
-/// Returns the actually config_file name used, allocated on heap
-/// Outputs:
-/// shadow_enable = whether shaodw is enabled globally
-/// fading_enable = whether fading is enabled globally
-/// win_option_mask = whether option overrides for specific window type is set for given
-/// options
-/// hasneg = whether the convolution kernel has negative values
-char *
-parse_config_libconfig(options_t *, const char *config_file, bool *shadow_enable,
- bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
-#endif
-
-void set_default_winopts(options_t *, win_option_mask_t *, bool shadow_enable,
- bool fading_enable, bool blur_enable);
-/// Parse a configuration file is that is enabled, also initialize the winopt_mask with
-/// default values
-/// Outputs and returns:
-/// same as parse_config_libconfig
-char *parse_config(options_t *, const char *config_file, bool *shadow_enable,
- bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
-
-/**
- * Parse a backend option argument.
- */
-static inline attr_pure enum backend parse_backend(const char *str) {
- for (enum backend i = 0; BACKEND_STRS[i]; ++i) {
- if (!strcasecmp(str, BACKEND_STRS[i])) {
- return i;
- }
- }
- // Keep compatibility with an old revision containing a spelling mistake...
- if (!strcasecmp(str, "xr_glx_hybird")) {
- log_warn("backend xr_glx_hybird should be xr_glx_hybrid, the misspelt "
- "version will be removed soon.");
- return BKEND_XR_GLX_HYBRID;
- }
- // cju wants to use dashes
- if (!strcasecmp(str, "xr-glx-hybrid")) {
- log_warn("backend xr-glx-hybrid should be xr_glx_hybrid, the alternative "
- "version will be removed soon.");
- return BKEND_XR_GLX_HYBRID;
- }
- log_error("Invalid backend argument: %s", str);
- return NUM_BKEND;
-}
-
-/**
- * Parse a VSync option argument.
- */
-static inline bool parse_vsync(const char *str) {
- if (strcmp(str, "no") == 0 || strcmp(str, "none") == 0 ||
- strcmp(str, "false") == 0 || strcmp(str, "nah") == 0) {
- return false;
- }
- return true;
-}
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/config_libconfig.c b/src/config_libconfig.c
deleted file mode 100644
index 7d266cd..0000000
--- a/src/config_libconfig.c
+++ /dev/null
@@ -1,784 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2012-2014 Richard Grenville <[email protected]>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <libconfig.h>
-#include <libgen.h>
-
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "err.h"
-#include "log.h"
-#include "options.h"
-#include "string_utils.h"
-#include "utils.h"
-#include "win.h"
-
-#pragma GCC diagnostic error "-Wunused-parameter"
-
-/**
- * Wrapper of libconfig's <code>config_lookup_int</code>.
- *
- * So it takes a pointer to bool.
- */
-static inline int lcfg_lookup_bool(const config_t *config, const char *path, bool *value) {
- int ival;
-
- int ret = config_lookup_bool(config, path, &ival);
- if (ret)
- *value = ival;
-
- return ret;
-}
-
-const char *xdg_config_home(void) {
- char *xdgh = getenv("XDG_CONFIG_HOME");
- char *home = getenv("HOME");
- const char *default_dir = "/.config";
-
- if (!xdgh) {
- if (!home) {
- return NULL;
- }
-
- xdgh = cvalloc(strlen(home) + strlen(default_dir) + 1);
-
- strcpy(xdgh, home);
- strcat(xdgh, default_dir);
- } else {
- xdgh = strdup(xdgh);
- }
-
- return xdgh;
-}
-
-char **xdg_config_dirs(void) {
- char *xdgd = getenv("XDG_CONFIG_DIRS");
- size_t count = 0;
-
- if (!xdgd) {
- xdgd = "/etc/xdg";
- }
-
- for (int i = 0; xdgd[i]; i++) {
- if (xdgd[i] == ':') {
- count++;
- }
- }
-
- // Store the string and the result pointers together so they can be
- // freed together
- char **dir_list = cvalloc(sizeof(char *) * (count + 2) + strlen(xdgd) + 1);
- auto dirs = strcpy((char *)dir_list + sizeof(char *) * (count + 2), xdgd);
- auto path = dirs;
-
- for (size_t i = 0; i < count; i++) {
- dir_list[i] = path;
- path = strchr(path, ':');
- *path = '\0';
- path++;
- }
- dir_list[count] = path;
-
- size_t fill = 0;
- for (size_t i = 0; i <= count; i++) {
- if (dir_list[i][0] == '/') {
- dir_list[fill] = dir_list[i];
- fill++;
- }
- }
-
- dir_list[fill] = NULL;
-
- return dir_list;
-}
-
-TEST_CASE(xdg_config_dirs) {
- auto old_var = getenv("XDG_CONFIG_DIRS");
- if (old_var) {
- old_var = strdup(old_var);
- }
- unsetenv("XDG_CONFIG_DIRS");
-
- auto result = xdg_config_dirs();
- TEST_STREQUAL(result[0], "/etc/xdg");
- TEST_EQUAL(result[1], NULL);
- free(result);
-
- setenv("XDG_CONFIG_DIRS", ".:.:/etc/xdg:.:/:", 1);
- result = xdg_config_dirs();
- TEST_STREQUAL(result[0], "/etc/xdg");
- TEST_STREQUAL(result[1], "/");
- TEST_EQUAL(result[2], NULL);
- free(result);
-
- setenv("XDG_CONFIG_DIRS", ":", 1);
- result = xdg_config_dirs();
- TEST_EQUAL(result[0], NULL);
- free(result);
-
- if (old_var) {
- setenv("XDG_CONFIG_DIRS", old_var, 1);
- free(old_var);
- }
-}
-
-/// Search for config file under a base directory
-FILE *open_config_file_at(const char *base, char **out_path) {
- static const char *config_paths[] = {"/picom.conf", "/picom/picom.conf",
- "/compton.conf", "/compton/compton.conf"};
- for (size_t i = 0; i < ARR_SIZE(config_paths); i++) {
- char *path = mstrjoin(base, config_paths[i]);
- FILE *ret = fopen(path, "r");
- if (ret && out_path) {
- *out_path = path;
- } else {
- free(path);
- }
- if (ret) {
- if (strstr(config_paths[i], "compton")) {
- log_warn("This compositor has been renamed to \"picom\", "
- "the old config file paths is deprecated. "
- "Please replace the \"compton\"s in the path "
- "with \"picom\"");
- }
- return ret;
- }
- }
- return NULL;
-}
-
-/**
- * Get a file stream of the configuration file to read.
- *
- * Follows the XDG specification to search for the configuration file.
- */
-FILE *open_config_file(const char *cpath, char **ppath) {
- static const char config_filename_legacy[] = "/.compton.conf";
-
- if (cpath) {
- FILE *ret = fopen(cpath, "r");
- if (ret && ppath)
- *ppath = strdup(cpath);
- return ret;
- }
-
- // First search for config file in user config directory
- auto config_home = xdg_config_home();
- auto ret = open_config_file_at(config_home, ppath);
- free((void *)config_home);
- if (ret) {
- return ret;
- }
-
- // Fall back to legacy config file in user home directory
- const char *home = getenv("HOME");
- if (home && strlen(home)) {
- auto path = mstrjoin(home, config_filename_legacy);
- ret = fopen(path, "r");
- if (ret && ppath) {
- *ppath = path;
- } else {
- free(path);
- }
- if (ret) {
- return ret;
- }
- }
-
- // Fall back to config file in system config directory
- auto config_dirs = xdg_config_dirs();
- for (int i = 0; config_dirs[i]; i++) {
- ret = open_config_file_at(config_dirs[i], ppath);
- if (ret) {
- free(config_dirs);
- return ret;
- }
- }
- free(config_dirs);
-
- return NULL;
-}
-
-/**
- * Parse a condition list in configuration file.
- */
-void parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst, const char *name) {
- config_setting_t *setting = config_lookup(pcfg, name);
- if (setting) {
- // Parse an array of options
- if (config_setting_is_array(setting)) {
- int i = config_setting_length(setting);
- while (i--)
- condlst_add(pcondlst,
- config_setting_get_string_elem(setting, i));
- }
- // Treat it as a single pattern if it's a string
- else if (CONFIG_TYPE_STRING == config_setting_type(setting)) {
- condlst_add(pcondlst, config_setting_get_string(setting));
- }
- }
-}
-
-/**
- * Parse an opacity rule list in configuration file.
- */
-static inline void
-parse_cfg_condlst_opct(options_t *opt, const config_t *pcfg, const char *name) {
- config_setting_t *setting = config_lookup(pcfg, name);
- if (setting) {
- // Parse an array of options
- if (config_setting_is_array(setting)) {
- int i = config_setting_length(setting);
- while (i--)
- if (!parse_rule_opacity(
- &opt->opacity_rules,
- config_setting_get_string_elem(setting, i)))
- exit(1);
- }
- // Treat it as a single pattern if it's a string
- else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
- if (!parse_rule_opacity(&opt->opacity_rules,
- config_setting_get_string(setting)))
- exit(1);
- }
- }
-}
-
-static inline void parse_wintype_config(const config_t *cfg, const char *member_name,
- win_option_t *o, win_option_mask_t *mask) {
- char *str = mstrjoin("wintypes.", member_name);
- const config_setting_t *setting = config_lookup(cfg, str);
- free(str);
-
- int ival = 0;
- const char *sval = NULL;
-
- if (setting) {
- if (config_setting_lookup_bool(setting, "shadow", &ival)) {
- o->shadow = ival;
- mask->shadow = true;
- }
- if (config_setting_lookup_bool(setting, "fade", &ival)) {
- o->fade = ival;
- mask->fade = true;
- }
- if (config_setting_lookup_bool(setting, "focus", &ival)) {
- o->focus = ival;
- mask->focus = true;
- }
- if (config_setting_lookup_bool(setting, "blur-background", &ival)) {
- o->blur_background = ival;
- mask->blur_background = true;
- }
- if (config_setting_lookup_bool(setting, "full-shadow", &ival)) {
- o->full_shadow = ival;
- mask->full_shadow = true;
- }
- if (config_setting_lookup_bool(setting, "redir-ignore", &ival)) {
- o->redir_ignore = ival;
- mask->redir_ignore = true;
- }
- if (config_setting_lookup_bool(setting, "clip-shadow-above", &ival)) {
- o->clip_shadow_above = ival;
- mask->clip_shadow_above = true;
- }
- if (config_setting_lookup_string(setting, "animation", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID)
- animation = OPEN_WINDOW_ANIMATION_NONE;
-
- o->animation = animation;
- mask->animation = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (config_setting_lookup_string(setting, "animation-unmap", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID)
- animation = OPEN_WINDOW_ANIMATION_NONE;
-
- o->animation_unmap = animation;
- mask->animation_unmap = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (config_setting_lookup_string(setting, "animation-workspace-in", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID)
- animation = OPEN_WINDOW_ANIMATION_NONE;
-
- o->animation_workspace_in = animation;
- mask->animation_workspace_in = OPEN_WINDOW_ANIMATION_INVALID;
- }
- if (config_setting_lookup_string(setting, "animation-workspace-out", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID)
- animation = OPEN_WINDOW_ANIMATION_NONE;
-
- o->animation_workspace_out = animation;
- mask->animation_workspace_out = OPEN_WINDOW_ANIMATION_INVALID;
- }
-
- double fval;
- if (config_setting_lookup_float(setting, "opacity", &fval)) {
- o->opacity = normalize_d(fval);
- mask->opacity = true;
- }
- }
-}
-
-/**
- * Parse a configuration file from default location.
- *
- * Returns the actually config_file name
- */
-char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shadow_enable,
- bool *fading_enable, bool *conv_kern_hasneg,
- win_option_mask_t *winopt_mask) {
- char *path = NULL;
- FILE *f;
- config_t cfg;
- int ival = 0;
- bool bval;
- double dval = 0.0;
- // libconfig manages string memory itself, so no need to manually free
- // anything
- const char *sval = NULL;
-
- f = open_config_file(config_file, &path);
- if (!f) {
- free(path);
- if (config_file) {
- log_fatal("Failed to read configuration file \"%s\".", config_file);
- return ERR_PTR(-1);
- }
- return NULL;
- }
-
- config_init(&cfg);
-#ifdef CONFIG_OPTION_ALLOW_OVERRIDES
- config_set_options(&cfg, CONFIG_OPTION_ALLOW_OVERRIDES);
-#endif
- {
- // dirname() could modify the original string, thus we must pass a
- // copy
- char *path2 = strdup(path);
- char *parent = dirname(path2);
-
- if (parent)
- config_set_include_dir(&cfg, parent);
-
- free(path2);
- }
-
- {
- int read_result = config_read(&cfg, f);
- fclose(f);
- f = NULL;
- if (read_result == CONFIG_FALSE) {
- log_fatal("Error when reading configuration file \"%s\", line "
- "%d: %s",
- path, config_error_line(&cfg), config_error_text(&cfg));
- goto err;
- }
- }
- config_set_auto_convert(&cfg, 1);
-
- // Get options from the configuration file. We don't do range checking
- // right now. It will be done later
-
- // -D (fade_delta)
- if (config_lookup_int(&cfg, "fade-delta", &ival))
- opt->fade_delta = ival;
- // -I (fade_in_step)
- if (config_lookup_float(&cfg, "fade-in-step", &dval))
- opt->fade_in_step = normalize_d(dval);
- // -O (fade_out_step)
- if (config_lookup_float(&cfg, "fade-out-step", &dval))
- opt->fade_out_step = normalize_d(dval);
- // -r (shadow_radius)
- config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
- // -o (shadow_opacity)
- config_lookup_float(&cfg, "shadow-opacity", &opt->shadow_opacity);
- // -l (shadow_offset_x)
- config_lookup_int(&cfg, "shadow-offset-x", &opt->shadow_offset_x);
- // -t (shadow_offset_y)
- config_lookup_int(&cfg, "shadow-offset-y", &opt->shadow_offset_y);
- // -i (inactive_opacity)
- if (config_lookup_float(&cfg, "inactive-opacity", &dval))
- opt->inactive_opacity = normalize_d(dval);
- // --active_opacity
- if (config_lookup_float(&cfg, "active-opacity", &dval))
- opt->active_opacity = normalize_d(dval);
- // --corner-radius
- config_lookup_int(&cfg, "corner-radius", &opt->corner_radius);
- // --rounded-corners-exclude
- parse_cfg_condlst(&cfg, &opt->rounded_corners_blacklist, "rounded-corners-exclude");
- // -e (frame_opacity)
- config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
- // -c (shadow_enable)
- if (config_lookup_bool(&cfg, "shadow", &ival))
- *shadow_enable = ival;
- // -C (no_dock_shadow)
- if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) {
- log_error("Option `no-dock-shadow` has been removed. Please use the "
- "wintype option `shadow` of `dock` instead.");
- goto err;
- }
- // -G (no_dnd_shadow)
- if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) {
- log_error("Option `no-dnd-shadow` has been removed. Please use the "
- "wintype option `shadow` of `dnd` instead.");
- goto err;
- };
- // -m (menu_opacity)
- if (config_lookup_float(&cfg, "menu-opacity", &dval)) {
- log_warn("Option `menu-opacity` is deprecated, and will be "
- "removed.Please use the "
- "wintype option `opacity` of `popup_menu` and `dropdown_menu` "
- "instead.");
- opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = dval;
- opt->wintype_option[WINTYPE_POPUP_MENU].opacity = dval;
- winopt_mask[WINTYPE_DROPDOWN_MENU].opacity = true;
- winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
- }
- // -f (fading_enable)
- if (config_lookup_bool(&cfg, "fading", &ival))
- *fading_enable = ival;
- // --no-fading-open-close
- lcfg_lookup_bool(&cfg, "no-fading-openclose", &opt->no_fading_openclose);
- // --no-fading-destroyed-argb
- lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb", &opt->no_fading_destroyed_argb);
- // --shadow-red
- config_lookup_float(&cfg, "shadow-red", &opt->shadow_red);
- // --shadow-green
- config_lookup_float(&cfg, "shadow-green", &opt->shadow_green);
- // --shadow-blue
- config_lookup_float(&cfg, "shadow-blue", &opt->shadow_blue);
- // --shadow-color
- if (config_lookup_string(&cfg, "shadow-color", &sval)) {
- struct color rgb;
- rgb = hex_to_rgb(sval);
- opt->shadow_red = rgb.red;
- opt->shadow_green = rgb.green;
- opt->shadow_blue = rgb.blue;
- }
- // --shadow-exclude-reg
- if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval))
- opt->shadow_exclude_reg_str = strdup(sval);
- // --inactive-opacity-override
- lcfg_lookup_bool(&cfg, "inactive-opacity-override", &opt->inactive_opacity_override);
- // --inactive-dim
- config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim);
- // --mark-wmwin-focused
- lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused);
- // --mark-ovredir-focused
- lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &opt->mark_ovredir_focused);
- // --shadow-ignore-shaped
- lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", &opt->shadow_ignore_shaped);
- // --detect-rounded-corners
- lcfg_lookup_bool(&cfg, "detect-rounded-corners", &opt->detect_rounded_corners);
- // --xinerama-shadow-crop
- lcfg_lookup_bool(&cfg, "xinerama-shadow-crop", &opt->xinerama_shadow_crop);
- // --detect-client-opacity
- lcfg_lookup_bool(&cfg, "detect-client-opacity", &opt->detect_client_opacity);
- // --refresh-rate
- if (config_lookup_int(&cfg, "refresh-rate", &opt->refresh_rate)) {
- if (opt->refresh_rate < 0) {
- log_warn("Invalid refresh rate %d, fallback to 0", opt->refresh_rate);
- opt->refresh_rate = 0;
- }
- }
- // --vsync
- if (config_lookup_string(&cfg, "vsync", &sval)) {
- opt->vsync = parse_vsync(sval);
- log_warn("vsync option will take a boolean from now on. \"%s\" is "
- "interpreted as \"%s\" for compatibility, but this will stop "
- "working soon",
- sval, opt->vsync ? "true" : "false");
- }
- lcfg_lookup_bool(&cfg, "vsync", &opt->vsync);
- // --backend
- if (config_lookup_string(&cfg, "backend", &sval)) {
- opt->backend = parse_backend(sval);
- if (opt->backend >= NUM_BKEND) {
- log_fatal("Cannot parse backend");
- goto err;
- }
- }
- // --log-level
- if (config_lookup_string(&cfg, "log-level", &sval)) {
- auto level = string_to_log_level(sval);
- if (level == LOG_LEVEL_INVALID) {
- log_warn("Invalid log level, defaults to WARN");
- } else {
- log_set_level_tls(level);
- }
- }
- // --log-file
- if (config_lookup_string(&cfg, "log-file", &sval)) {
- if (*sval != '/') {
- log_warn("The log-file in your configuration file is not an "
- "absolute path");
- }
- opt->logpath = strdup(sval);
- }
- // --sw-opti
- lcfg_lookup_bool(&cfg, "sw-opti", &opt->sw_opti);
- // --use-ewmh-active-win
- lcfg_lookup_bool(&cfg, "use-ewmh-active-win", &opt->use_ewmh_active_win);
- // --unredir-if-possible
- lcfg_lookup_bool(&cfg, "unredir-if-possible", &opt->unredir_if_possible);
- // --unredir-if-possible-delay
- if (config_lookup_int(&cfg, "unredir-if-possible-delay", &ival)) {
- if (ival < 0) {
- log_warn("Invalid unredir-if-possible-delay %d", ival);
- } else {
- opt->unredir_if_possible_delay = ival;
- }
- }
- // --inactive-dim-fixed
- lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &opt->inactive_dim_fixed);
- // --detect-transient
- lcfg_lookup_bool(&cfg, "detect-transient", &opt->detect_transient);
- // --detect-client-leader
- lcfg_lookup_bool(&cfg, "detect-client-leader", &opt->detect_client_leader);
- // --no-ewmh-fullscreen
- lcfg_lookup_bool(&cfg, "no-ewmh-fullscreen", &opt->no_ewmh_fullscreen);
- // --transparent-clipping
- lcfg_lookup_bool(&cfg, "transparent-clipping", &opt->transparent_clipping);
- // --shadow-exclude
- parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude");
- // --clip-shadow-above
- parse_cfg_condlst(&cfg, &opt->shadow_clip_list, "clip-shadow-above");
- // --fade-exclude
- parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude");
- // --animations
- lcfg_lookup_bool(&cfg, "animations", &opt->animations);
- // --animation-for-open-window
- if (config_lookup_string(&cfg, "animation-for-open-window", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_fatal("Invalid open-window animation %s", sval);
- goto err;
- }
- opt->animation_for_open_window = animation;
- }
- // --animation-for-transient-window
- if (config_lookup_string(&cfg, "animation-for-transient-window", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_fatal("Invalid transient-window animation %s", sval);
- goto err;
- }
- opt->animation_for_transient_window = animation;
- }
- // --animation-for-unmap-window
- if (config_lookup_string(&cfg, "animation-for-unmap-window", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_fatal("Invalid unmap-window animation %s", sval);
- goto err;
- }
- opt->animation_for_unmap_window = animation;
- }
- // --animation-for-workspace-switch-in
- if (config_lookup_string(&cfg, "animation-for-workspace-switch-in", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_fatal("Invalid workspace-switch-in animation %s", sval);
- goto err;
- }
- opt->animation_for_workspace_switch_in = animation;
- }
- // --animation-for-workspace-switch-out
- if (config_lookup_string(&cfg, "animation-for-workspace-switch-out", &sval)) {
- enum open_window_animation animation = parse_open_window_animation(sval);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_fatal("Invalid workspace-switch-out animation %s", sval);
- goto err;
- }
- opt->animation_for_workspace_switch_out = animation;
- }
- // --animation-stiffness
- config_lookup_float(&cfg, "animation-stiffness", &opt->animation_stiffness);
- // --animation-window-mass
- config_lookup_float(&cfg, "animation-window-mass", &opt->animation_window_mass);
- // --animation-dampening
- config_lookup_float(&cfg, "animation-dampening", &opt->animation_dampening);
- // --animation-delta
- config_lookup_float(&cfg, "animation-delta", &opt->animation_delta);
- // --animation-force-steps
- lcfg_lookup_bool(&cfg, "animation-force-steps", &opt->animation_force_steps);
- // --animation-clamping
- lcfg_lookup_bool(&cfg, "animation-clamping", &opt->animation_clamping);
- // --focus-exclude
- parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
- // --invert-color-include
- parse_cfg_condlst(&cfg, &opt->invert_color_list, "invert-color-include");
- // --blur-background-exclude
- parse_cfg_condlst(&cfg, &opt->blur_background_blacklist, "blur-background-exclude");
- // --opacity-rule
- parse_cfg_condlst_opct(opt, &cfg, "opacity-rule");
- // --unredir-if-possible-exclude
- parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist,
- "unredir-if-possible-exclude");
- // --blur-method
- if (config_lookup_string(&cfg, "blur-method", &sval)) {
- enum blur_method method = parse_blur_method(sval);
- if (method >= BLUR_METHOD_INVALID) {
- log_fatal("Invalid blur method %s", sval);
- goto err;
- }
- opt->blur_method = method;
- }
- // --blur-size
- config_lookup_int(&cfg, "blur-size", &opt->blur_radius);
- // --blur-deviation
- config_lookup_float(&cfg, "blur-deviation", &opt->blur_deviation);
- // --blur-strength
- config_lookup_int(&cfg, "blur-strength", &opt->blur_strength);
- // --blur-background
- if (config_lookup_bool(&cfg, "blur-background", &ival) && ival) {
- if (opt->blur_method == BLUR_METHOD_NONE) {
- opt->blur_method = BLUR_METHOD_KERNEL;
- }
- }
- // --blur-background-frame
- lcfg_lookup_bool(&cfg, "blur-background-frame", &opt->blur_background_frame);
- // --blur-background-fixed
- lcfg_lookup_bool(&cfg, "blur-background-fixed", &opt->blur_background_fixed);
- // --blur-kern
- if (config_lookup_string(&cfg, "blur-kern", &sval)) {
- opt->blur_kerns =
- parse_blur_kern_lst(sval, conv_kern_hasneg, &opt->blur_kernel_count);
- if (!opt->blur_kerns) {
- log_fatal("Cannot parse \"blur-kern\"");
- goto err;
- }
- }
- // --resize-damage
- config_lookup_int(&cfg, "resize-damage", &opt->resize_damage);
- // --glx-no-stencil
- lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil);
- // --glx-no-rebind-pixmap
- lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap);
- lcfg_lookup_bool(&cfg, "force-win-blend", &opt->force_win_blend);
- // --glx-swap-method
- if (config_lookup_string(&cfg, "glx-swap-method", &sval)) {
- char *endptr;
- long val = strtol(sval, &endptr, 10);
- if (*endptr || !(*sval)) {
- // sval is not a number, or an empty string
- val = -1;
- }
- if (strcmp(sval, "undefined") != 0 && val != 0) {
- // If not undefined, we will use damage and buffer-age to limit
- // the rendering area.
- opt->use_damage = true;
- }
- log_warn("glx-swap-method has been deprecated since v6, your setting "
- "\"%s\" should be %s.",
- sval,
- opt->use_damage ? "replaced by `use-damage = true`" : "removed");
- }
- // --use-damage
- lcfg_lookup_bool(&cfg, "use-damage", &opt->use_damage);
-
- // --max-brightness
- if (config_lookup_float(&cfg, "max-brightness", &opt->max_brightness) &&
- opt->use_damage && opt->max_brightness < 1) {
- log_warn("max-brightness requires use-damage = false. Falling back to "
- "1.0");
- opt->max_brightness = 1.0;
- }
-
- // --glx-use-gpushader4
- if (config_lookup_bool(&cfg, "glx-use-gpushader4", &ival) && ival) {
- log_warn("glx-use-gpushader4 is deprecated since v6, please remove it "
- "from"
- "your config file");
- }
- // --xrender-sync
- if (config_lookup_bool(&cfg, "xrender-sync", &ival) && ival) {
- log_error("Please use xrender-sync-fence instead of xrender-sync.");
- goto err;
- }
- // --xrender-sync-fence
- lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
-
- if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
- log_warn("\"clear-shadow\" is removed as an option, and is always"
- " enabled now. Consider removing it from your config file");
- if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval)) {
- log_error("\"paint-on-overlay\" has been removed as an option, and "
- "the feature is enabled whenever possible");
- goto err;
- }
-
- if (config_lookup_float(&cfg, "alpha-step", &dval)) {
- log_error("\"alpha-step\" has been removed, compton now tries to make use"
- " of all alpha values");
- goto err;
- }
-
- const char *deprecation_message attr_unused =
- "has been removed. If you encounter problems "
- "without this feature, please feel free to open a bug report";
-
- config_setting_t *blur_cfg = config_lookup(&cfg, "blur");
- if (blur_cfg) {
- if (config_setting_lookup_string(blur_cfg, "method", &sval)) {
- enum blur_method method = parse_blur_method(sval);
- if (method >= BLUR_METHOD_INVALID) {
- log_warn("Invalid blur method %s, ignoring.", sval);
- } else {
- opt->blur_method = method;
- }
- }
-
- config_setting_lookup_int(blur_cfg, "size", &opt->blur_radius);
-
- if (config_setting_lookup_string(blur_cfg, "kernel", &sval)) {
- opt->blur_kerns = parse_blur_kern_lst(sval, conv_kern_hasneg,
- &opt->blur_kernel_count);
- if (!opt->blur_kerns) {
- log_warn("Failed to parse blur kernel: %s", sval);
- }
- }
-
- config_setting_lookup_float(blur_cfg, "deviation", &opt->blur_deviation);
- config_setting_lookup_int(blur_cfg, "strength", &opt->blur_strength);
- }
-
- // --write-pid-path
- if (config_lookup_string(&cfg, "write-pid-path", &sval)) {
- if (*sval != '/') {
- log_warn("The write-pid-path in your configuration file is not"
- " an absolute path");
- }
- opt->write_pid_path = strdup(sval);
- }
-
- // Wintype settings
-
- // XXX ! Refactor all the wintype_* arrays into a struct
- for (wintype_t i = 0; i < NUM_WINTYPES; ++i) {
- parse_wintype_config(&cfg, WINTYPES[i], &opt->wintype_option[i],
- &winopt_mask[i]);
- }
-
- // Compatibility with the old name for notification windows.
- parse_wintype_config(&cfg, "notify", &opt->wintype_option[WINTYPE_NOTIFICATION],
- &winopt_mask[WINTYPE_NOTIFICATION]);
-
- config_destroy(&cfg);
- return path;
-
-err:
- config_destroy(&cfg);
- free(path);
- return ERR_PTR(-1);
-}
diff --git a/src/dbus.c b/src/dbus.c
deleted file mode 100644
index 8d6094e..0000000
--- a/src/dbus.c
+++ /dev/null
@@ -1,1340 +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.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <xcb/xcb.h>
-
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "list.h"
-#include "log.h"
-#include "string_utils.h"
-#include "types.h"
-#include "uthash_extra.h"
-#include "utils.h"
-#include "win.h"
-
-#include "dbus.h"
-
-struct cdbus_data {
- /// DBus connection.
- DBusConnection *dbus_conn;
- /// DBus service name.
- char *dbus_service;
-};
-
-// Window type
-typedef uint32_t cdbus_window_t;
-#define CDBUS_TYPE_WINDOW DBUS_TYPE_UINT32
-#define CDBUS_TYPE_WINDOW_STR DBUS_TYPE_UINT32_AS_STRING
-
-typedef uint32_t cdbus_enum_t;
-#define CDBUS_TYPE_ENUM DBUS_TYPE_UINT32
-#define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT32_AS_STRING
-
-#define CDBUS_SERVICE_NAME "com.github.chjj.compton"
-#define CDBUS_INTERFACE_NAME CDBUS_SERVICE_NAME
-#define CDBUS_OBJECT_NAME "/com/github/chjj/compton"
-#define CDBUS_ERROR_PREFIX CDBUS_INTERFACE_NAME ".error"
-#define CDBUS_ERROR_UNKNOWN CDBUS_ERROR_PREFIX ".unknown"
-#define CDBUS_ERROR_UNKNOWN_S "Well, I don't know what happened. Do you?"
-#define CDBUS_ERROR_BADMSG CDBUS_ERROR_PREFIX ".bad_message"
-#define CDBUS_ERROR_BADMSG_S \
- "Unrecognized command. Beware compton " \
- "cannot make you a sandwich."
-#define CDBUS_ERROR_BADARG CDBUS_ERROR_PREFIX ".bad_argument"
-#define CDBUS_ERROR_BADARG_S "Failed to parse argument %d: %s"
-#define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window"
-#define CDBUS_ERROR_BADWIN_S "Requested window %#010x not found."
-#define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target"
-#define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found."
-#define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden"
-#define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied."
-#define CDBUS_ERROR_CUSTOM CDBUS_ERROR_PREFIX ".custom"
-#define CDBUS_ERROR_CUSTOM_S "%s"
-
-#define cdbus_reply_err(ps, srcmsg, err_name, err_format, ...) \
- cdbus_reply_errm((ps), dbus_message_new_error_printf( \
- (srcmsg), (err_name), (err_format), ##__VA_ARGS__))
-
-static DBusHandlerResult cdbus_process(DBusConnection *conn, DBusMessage *m, void *);
-
-static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data);
-
-static void cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data);
-
-static void cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data);
-
-static dbus_bool_t cdbus_callback_add_watch(DBusWatch *watch, void *data);
-
-static void cdbus_callback_remove_watch(DBusWatch *watch, void *data);
-
-static void cdbus_callback_watch_toggled(DBusWatch *watch, void *data);
-
-/**
- * Initialize D-Bus connection.
- */
-bool cdbus_init(session_t *ps, const char *uniq) {
- auto cd = cmalloc(struct cdbus_data);
- cd->dbus_service = NULL;
-
- // Set ps->dbus_data here because add_watch functions need it
- ps->dbus_data = cd;
-
- DBusError err = {};
-
- // Initialize
- dbus_error_init(&err);
-
- // Connect to D-Bus
- // Use dbus_bus_get_private() so we can fully recycle it ourselves
- cd->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
- if (dbus_error_is_set(&err)) {
- log_error("D-Bus connection failed (%s).", err.message);
- dbus_error_free(&err);
- goto fail;
- }
-
- if (!cd->dbus_conn) {
- log_error("D-Bus connection failed for unknown reason.");
- goto fail;
- }
-
- // Avoid exiting on disconnect
- dbus_connection_set_exit_on_disconnect(cd->dbus_conn, false);
-
- // Request service name
- {
- // Build service name
- size_t service_len = strlen(CDBUS_SERVICE_NAME) + strlen(uniq) + 2;
- char *service = ccalloc(service_len, char);
- snprintf(service, service_len, "%s.%s", CDBUS_SERVICE_NAME, uniq);
-
- // Make a valid dbus name by converting non alphanumeric characters to
- // underscore
- char *tmp = service + strlen(CDBUS_SERVICE_NAME) + 1;
- while (*tmp) {
- if (!isalnum((unsigned char)*tmp)) {
- *tmp = '_';
- }
- tmp++;
- }
- cd->dbus_service = service;
-
- // Request for the name
- int ret = dbus_bus_request_name(cd->dbus_conn, service,
- DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
-
- if (dbus_error_is_set(&err)) {
- log_error("Failed to obtain D-Bus name (%s).", err.message);
- dbus_error_free(&err);
- goto fail;
- }
-
- if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret &&
- DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) {
- log_error("Failed to become the primary owner of requested D-Bus "
- "name (%d).",
- ret);
- goto fail;
- }
- }
-
- // Add watch handlers
- if (!dbus_connection_set_watch_functions(cd->dbus_conn, cdbus_callback_add_watch,
- cdbus_callback_remove_watch,
- cdbus_callback_watch_toggled, ps, NULL)) {
- log_error("Failed to add D-Bus watch functions.");
- goto fail;
- }
-
- // Add timeout handlers
- if (!dbus_connection_set_timeout_functions(
- cd->dbus_conn, cdbus_callback_add_timeout, cdbus_callback_remove_timeout,
- cdbus_callback_timeout_toggled, ps, NULL)) {
- log_error("Failed to add D-Bus timeout functions.");
- goto fail;
- }
-
- // Add match
- dbus_bus_add_match(cd->dbus_conn,
- "type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err);
- if (dbus_error_is_set(&err)) {
- log_error("Failed to add D-Bus match.");
- dbus_error_free(&err);
- goto fail;
- }
- dbus_connection_add_filter(cd->dbus_conn, cdbus_process, ps, NULL);
- return true;
-fail:
- ps->dbus_data = NULL;
- free(cd->dbus_service);
- free(cd);
- return false;
-}
-
-/**
- * Destroy D-Bus connection.
- */
-void cdbus_destroy(session_t *ps) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn) {
- // Release DBus name firstly
- if (cd->dbus_service) {
- DBusError err = {};
- dbus_error_init(&err);
-
- dbus_bus_release_name(cd->dbus_conn, cd->dbus_service, &err);
- if (dbus_error_is_set(&err)) {
- log_error("Failed to release DBus name (%s).", err.message);
- dbus_error_free(&err);
- }
- free(cd->dbus_service);
- }
-
- // Close and unref the connection
- dbus_connection_close(cd->dbus_conn);
- dbus_connection_unref(cd->dbus_conn);
- }
- free(cd);
-}
-
-/** @name DBusTimeout handling
- */
-///@{
-
-typedef struct ev_dbus_timer {
- ev_timer w;
- DBusTimeout *t;
-} ev_dbus_timer;
-
-/**
- * Callback for handling a D-Bus timeout.
- */
-static void
-cdbus_callback_handle_timeout(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
- ev_dbus_timer *t = (void *)w;
- dbus_timeout_handle(t->t);
-}
-
-/**
- * Callback for adding D-Bus timeout.
- */
-static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) {
- session_t *ps = data;
-
- auto t = ccalloc(1, ev_dbus_timer);
- double i = dbus_timeout_get_interval(timeout) / 1000.0;
- ev_timer_init(&t->w, cdbus_callback_handle_timeout, i, i);
- t->t = timeout;
- dbus_timeout_set_data(timeout, t, NULL);
-
- if (dbus_timeout_get_enabled(timeout))
- ev_timer_start(ps->loop, &t->w);
-
- return true;
-}
-
-/**
- * Callback for removing D-Bus timeout.
- */
-static void cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) {
- session_t *ps = data;
-
- ev_dbus_timer *t = dbus_timeout_get_data(timeout);
- assert(t);
- ev_timer_stop(ps->loop, &t->w);
- free(t);
-}
-
-/**
- * Callback for toggling a D-Bus timeout.
- */
-static void cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) {
- session_t *ps = data;
- ev_dbus_timer *t = dbus_timeout_get_data(timeout);
-
- assert(t);
- ev_timer_stop(ps->loop, &t->w);
- if (dbus_timeout_get_enabled(timeout)) {
- double i = dbus_timeout_get_interval(timeout) / 1000.0;
- ev_timer_set(&t->w, i, i);
- ev_timer_start(ps->loop, &t->w);
- }
-}
-
-///@}
-
-/** @name DBusWatch handling
- */
-///@{
-
-typedef struct ev_dbus_io {
- ev_io w;
- struct cdbus_data *cd;
- DBusWatch *dw;
-} ev_dbus_io;
-
-void cdbus_io_callback(EV_P attr_unused, ev_io *w, int revents) {
- ev_dbus_io *dw = (void *)w;
- DBusWatchFlags flags = 0;
- if (revents & EV_READ)
- flags |= DBUS_WATCH_READABLE;
- if (revents & EV_WRITE)
- flags |= DBUS_WATCH_WRITABLE;
- dbus_watch_handle(dw->dw, flags);
- while (dbus_connection_dispatch(dw->cd->dbus_conn) != DBUS_DISPATCH_COMPLETE)
- ;
-}
-
-/**
- * Determine the poll condition of a DBusWatch.
- */
-static inline int cdbus_get_watch_cond(DBusWatch *watch) {
- const unsigned flags = dbus_watch_get_flags(watch);
- int condition = 0;
- if (flags & DBUS_WATCH_READABLE)
- condition |= EV_READ;
- if (flags & DBUS_WATCH_WRITABLE)
- condition |= EV_WRITE;
-
- return condition;
-}
-
-/**
- * Callback for adding D-Bus watch.
- */
-static dbus_bool_t cdbus_callback_add_watch(DBusWatch *watch, void *data) {
- session_t *ps = data;
-
- auto w = ccalloc(1, ev_dbus_io);
- w->dw = watch;
- w->cd = ps->dbus_data;
- ev_io_init(&w->w, cdbus_io_callback, dbus_watch_get_unix_fd(watch),
- cdbus_get_watch_cond(watch));
-
- // Leave disabled watches alone
- if (dbus_watch_get_enabled(watch))
- ev_io_start(ps->loop, &w->w);
-
- dbus_watch_set_data(watch, w, NULL);
-
- // Always return true
- return true;
-}
-
-/**
- * Callback for removing D-Bus watch.
- */
-static void cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
- session_t *ps = data;
- ev_dbus_io *w = dbus_watch_get_data(watch);
- ev_io_stop(ps->loop, &w->w);
- free(w);
-}
-
-/**
- * Callback for toggling D-Bus watch status.
- */
-static void cdbus_callback_watch_toggled(DBusWatch *watch, void *data) {
- session_t *ps = data;
- ev_io *w = dbus_watch_get_data(watch);
- if (dbus_watch_get_enabled(watch))
- ev_io_start(ps->loop, w);
- else
- ev_io_stop(ps->loop, w);
-}
-
-///@}
-
-/** @name Message argument appending callbacks
- */
-///@{
-
-/**
- * Callback to append a bool argument to a message.
- */
-static bool cdbus_apdarg_bool(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- assert(data);
-
- dbus_bool_t val = *(const bool *)data;
-
- if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append an int32 argument to a message.
- */
-static bool cdbus_apdarg_int32(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- if (!dbus_message_append_args(msg, DBUS_TYPE_INT32, data, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append an uint32 argument to a message.
- */
-static bool
-cdbus_apdarg_uint32(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, data, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append a double argument to a message.
- */
-static bool
-cdbus_apdarg_double(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- if (!dbus_message_append_args(msg, DBUS_TYPE_DOUBLE, data, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append a Window argument to a message.
- */
-static bool cdbus_apdarg_wid(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- assert(data);
- cdbus_window_t val = *(const xcb_window_t *)data;
-
- if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append an cdbus_enum_t argument to a message.
- */
-static bool cdbus_apdarg_enum(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- assert(data);
- if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append a string argument to a message.
- */
-static bool
-cdbus_apdarg_string(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
- const char *str = data;
- if (!str)
- str = "";
-
- if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Callback to append all window IDs to a message.
- */
-static bool cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data attr_unused) {
- // Get the number of wids we are to include
- unsigned count = 0;
- HASH_ITER2(ps->windows, w) {
- assert(!w->destroyed);
- ++count;
- }
-
- if (!count) {
- // Nothing to append
- return true;
- }
-
- // Allocate memory for an array of window IDs
- auto arr = ccalloc(count, cdbus_window_t);
-
- // Build the array
- cdbus_window_t *pcur = arr;
- HASH_ITER2(ps->windows, w) {
- assert(!w->destroyed);
- *pcur = w->id;
- ++pcur;
- }
- assert(pcur == arr + count);
-
- // Append arguments
- if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, &arr,
- count, DBUS_TYPE_INVALID)) {
- log_error("Failed to append argument.");
- free(arr);
- return false;
- }
-
- free(arr);
- return true;
-}
-///@}
-
-/**
- * Send a D-Bus signal.
- *
- * @param ps current session
- * @param name signal name
- * @param func a function that modifies the built message, to, for example,
- * add an argument
- * @param data data pointer to pass to the function
- */
-static bool cdbus_signal(session_t *ps, const char *name,
- bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
- const void *data) {
- struct cdbus_data *cd = ps->dbus_data;
- DBusMessage *msg = NULL;
-
- // Create a signal
- msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, CDBUS_INTERFACE_NAME, name);
- if (!msg) {
- log_error("Failed to create D-Bus signal.");
- return false;
- }
-
- // Append arguments onto message
- if (func && !func(ps, msg, data)) {
- dbus_message_unref(msg);
- return false;
- }
-
- // Send the message and flush the connection
- if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
- log_error("Failed to send D-Bus signal.");
- dbus_message_unref(msg);
- return false;
- }
- dbus_connection_flush(cd->dbus_conn);
-
- // Free the message
- dbus_message_unref(msg);
-
- return true;
-}
-
-/**
- * Send a signal with a Window ID as argument.
- */
-static inline bool cdbus_signal_wid(session_t *ps, const char *name, xcb_window_t wid) {
- return cdbus_signal(ps, name, cdbus_apdarg_wid, &wid);
-}
-
-/**
- * Send a D-Bus reply.
- *
- * @param ps current session
- * @param srcmsg original message
- * @param func a function that modifies the built message, to, for example,
- * add an argument
- * @param data data pointer to pass to the function
- */
-static bool cdbus_reply(session_t *ps, DBusMessage *srcmsg,
- bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
- const void *data) {
- struct cdbus_data *cd = ps->dbus_data;
- DBusMessage *msg = NULL;
-
- // Create a reply
- msg = dbus_message_new_method_return(srcmsg);
- if (!msg) {
- log_error("Failed to create D-Bus reply.");
- return false;
- }
-
- // Append arguments onto message
- if (func && !func(ps, msg, data)) {
- dbus_message_unref(msg);
- return false;
- }
-
- // Send the message and flush the connection
- if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
- log_error("Failed to send D-Bus reply.");
- dbus_message_unref(msg);
- return false;
- }
- dbus_connection_flush(cd->dbus_conn);
-
- // Free the message
- dbus_message_unref(msg);
-
- return true;
-}
-
-/**
- * Send a reply with a bool argument.
- */
-static inline bool cdbus_reply_bool(session_t *ps, DBusMessage *srcmsg, bool bval) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_bool, &bval);
-}
-
-/**
- * Send a reply with an int32 argument.
- */
-static inline bool cdbus_reply_int32(session_t *ps, DBusMessage *srcmsg, int32_t val) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &val);
-}
-
-/**
- * Send a reply with an int32 argument, cast from a long.
- */
-static inline bool cdbus_reply_int32l(session_t *ps, DBusMessage *srcmsg, long val) {
- int32_t tmp = (int32_t)val;
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &tmp);
-}
-
-/**
- * Send a reply with an uint32 argument.
- */
-static inline bool cdbus_reply_uint32(session_t *ps, DBusMessage *srcmsg, uint32_t val) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_uint32, &val);
-}
-
-/**
- * Send a reply with a double argument.
- */
-static inline bool cdbus_reply_double(session_t *ps, DBusMessage *srcmsg, double val) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_double, &val);
-}
-
-/**
- * Send a reply with a wid argument.
- */
-static inline bool cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, xcb_window_t wid) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_wid, &wid);
-}
-
-/**
- * Send a reply with a string argument.
- */
-static inline bool cdbus_reply_string(session_t *ps, DBusMessage *srcmsg, const char *str) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_string, str);
-}
-
-/**
- * Send a reply with a enum argument.
- */
-static inline bool cdbus_reply_enum(session_t *ps, DBusMessage *srcmsg, cdbus_enum_t eval) {
- return cdbus_reply(ps, srcmsg, cdbus_apdarg_enum, &eval);
-}
-
-/**
- * Send a D-Bus error reply.
- *
- * @param ps current session
- * @param msg the new error DBusMessage
- */
-static bool cdbus_reply_errm(session_t *ps, DBusMessage *msg) {
- struct cdbus_data *cd = ps->dbus_data;
- if (!msg) {
- log_error("Failed to create D-Bus reply.");
- return false;
- }
-
- // Send the message and flush the connection
- if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
- log_error("Failed to send D-Bus reply.");
- dbus_message_unref(msg);
- return false;
- }
- dbus_connection_flush(cd->dbus_conn);
-
- // Free the message
- dbus_message_unref(msg);
-
- return true;
-}
-
-/**
- * Get n-th argument of a D-Bus message.
- *
- * @param count the position of the argument to get, starting from 0
- * @param type libdbus type number of the type
- * @param pdest pointer to the target
- * @return true if successful, false otherwise.
- */
-static bool cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) {
- assert(count >= 0);
-
- DBusMessageIter iter = {};
- if (!dbus_message_iter_init(msg, &iter)) {
- log_error("Message has no argument.");
- return false;
- }
-
- {
- const int oldcount = count;
- while (count) {
- if (!dbus_message_iter_next(&iter)) {
- log_error("Failed to find argument %d.", oldcount);
- return false;
- }
- --count;
- }
- }
-
- if (type != dbus_message_iter_get_arg_type(&iter)) {
- log_error("Argument has incorrect type.");
- return false;
- }
-
- dbus_message_iter_get_basic(&iter, pdest);
-
- return true;
-}
-
-/** @name Message processing
- */
-///@{
-
-/**
- * Process a list_win D-Bus request.
- */
-static bool cdbus_process_list_win(session_t *ps, DBusMessage *msg) {
- cdbus_reply(ps, msg, cdbus_apdarg_wids, NULL);
-
- return true;
-}
-
-/**
- * Process a win_get D-Bus request.
- */
-static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
- cdbus_window_t wid = XCB_NONE;
- const char *target = NULL;
- DBusError err = {};
-
- if (!dbus_message_get_args(msg, &err, CDBUS_TYPE_WINDOW, &wid, DBUS_TYPE_STRING,
- &target, DBUS_TYPE_INVALID)) {
- log_error("Failed to parse argument of \"win_get\" (%s).", err.message);
- dbus_error_free(&err);
- return false;
- }
-
- auto w = find_managed_win(ps, wid);
-
- if (!w) {
- log_error("Window %#010x not found.", wid);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
- return true;
- }
-
-#define cdbus_m_win_get_do(tgt, apdarg_func) \
- if (!strcmp(#tgt, target)) { \
- apdarg_func(ps, msg, w->tgt); \
- return true; \
- }
-
- cdbus_m_win_get_do(base.id, cdbus_reply_wid);
-
- // next
- if (!strcmp("next", target)) {
- cdbus_reply_wid(
- ps, msg,
- (list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)
- ? 0
- : list_entry(w->base.stack_neighbour.next, struct win, stack_neighbour)
- ->id));
- return true;
- }
-
- // map_state
- if (!strcmp("map_state", target)) {
- cdbus_reply_bool(ps, msg, w->a.map_state);
- return true;
- }
-
- cdbus_m_win_get_do(mode, cdbus_reply_enum);
- cdbus_m_win_get_do(client_win, cdbus_reply_wid);
- cdbus_m_win_get_do(ever_damaged, cdbus_reply_bool);
- cdbus_m_win_get_do(window_type, cdbus_reply_enum);
- cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
- cdbus_m_win_get_do(leader, cdbus_reply_wid);
- if (!strcmp("focused_raw", target)) {
- cdbus_reply_bool(ps, msg, win_is_focused_raw(ps, w));
- return true;
- }
- cdbus_m_win_get_do(fade_force, cdbus_reply_enum);
- cdbus_m_win_get_do(shadow_force, cdbus_reply_enum);
- cdbus_m_win_get_do(focused_force, cdbus_reply_enum);
- cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum);
- cdbus_m_win_get_do(name, cdbus_reply_string);
- cdbus_m_win_get_do(class_instance, cdbus_reply_string);
- cdbus_m_win_get_do(class_general, cdbus_reply_string);
- cdbus_m_win_get_do(role, cdbus_reply_string);
-
- cdbus_m_win_get_do(opacity, cdbus_reply_double);
- cdbus_m_win_get_do(opacity_target, cdbus_reply_double);
- cdbus_m_win_get_do(has_opacity_prop, cdbus_reply_bool);
- cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32);
- cdbus_m_win_get_do(opacity_is_set, cdbus_reply_bool);
- cdbus_m_win_get_do(opacity_set, cdbus_reply_double);
-
- cdbus_m_win_get_do(frame_opacity, cdbus_reply_double);
- if (!strcmp("left_width", target)) {
- cdbus_reply_int32(ps, msg, w->frame_extents.left);
- return true;
- }
- if (!strcmp("right_width", target)) {
- cdbus_reply_int32(ps, msg, w->frame_extents.right);
- return true;
- }
- if (!strcmp("top_width", target)) {
- cdbus_reply_int32(ps, msg, w->frame_extents.top);
- return true;
- }
- if (!strcmp("bottom_width", target)) {
- cdbus_reply_int32(ps, msg, w->frame_extents.bottom);
- return true;
- }
-
- cdbus_m_win_get_do(shadow, cdbus_reply_bool);
- cdbus_m_win_get_do(invert_color, cdbus_reply_bool);
- cdbus_m_win_get_do(blur_background, cdbus_reply_bool);
-#undef cdbus_m_win_get_do
-
- log_error(CDBUS_ERROR_BADTGT_S, target);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
-
- return true;
-}
-
-/**
- * Process a win_set D-Bus request.
- */
-static bool cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
- cdbus_window_t wid = XCB_NONE;
- const char *target = NULL;
- DBusError err = {};
-
- if (!dbus_message_get_args(msg, &err, CDBUS_TYPE_WINDOW, &wid, DBUS_TYPE_STRING,
- &target, DBUS_TYPE_INVALID)) {
- log_error("(): Failed to parse argument of \"win_set\" (%s).", err.message);
- dbus_error_free(&err);
- return false;
- }
-
- auto w = find_managed_win(ps, wid);
-
- if (!w) {
- log_error("Window %#010x not found.", wid);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
- return true;
- }
-
-#define cdbus_m_win_set_do(tgt, type, real_type) \
- if (!strcmp(MSTR(tgt), target)) { \
- real_type val; \
- if (!cdbus_msg_get_arg(msg, 2, type, &val)) \
- return false; \
- w->tgt = val; \
- goto cdbus_process_win_set_success; \
- }
-
- if (!strcmp("shadow_force", target)) {
- cdbus_enum_t val = UNSET;
- if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
- return false;
- win_set_shadow_force(ps, w, val);
- goto cdbus_process_win_set_success;
- }
-
- if (!strcmp("fade_force", target)) {
- cdbus_enum_t val = UNSET;
- if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
- return false;
- win_set_fade_force(w, val);
- goto cdbus_process_win_set_success;
- }
-
- if (!strcmp("focused_force", target)) {
- cdbus_enum_t val = UNSET;
- if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
- return false;
- win_set_focused_force(ps, w, val);
- goto cdbus_process_win_set_success;
- }
-
- if (!strcmp("invert_color_force", target)) {
- cdbus_enum_t val = UNSET;
- if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
- return false;
- win_set_invert_color_force(ps, w, val);
- goto cdbus_process_win_set_success;
- }
-#undef cdbus_m_win_set_do
-
- log_error(CDBUS_ERROR_BADTGT_S, target);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
-
- return true;
-
-cdbus_process_win_set_success:
- if (!dbus_message_get_no_reply(msg))
- cdbus_reply_bool(ps, msg, true);
- return true;
-}
-
-/**
- * Process a find_win D-Bus request.
- */
-static bool cdbus_process_find_win(session_t *ps, DBusMessage *msg) {
- const char *target = NULL;
-
- if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
- return false;
-
- xcb_window_t wid = XCB_NONE;
-
- // Find window by client window
- if (!strcmp("client", target)) {
- cdbus_window_t client = XCB_NONE;
- if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client))
- return false;
- auto w = find_toplevel(ps, client);
- if (w) {
- wid = w->base.id;
- }
- }
- // Find focused window
- else if (!strcmp("focused", target)) {
- if (ps->active_win && ps->active_win->state != WSTATE_UNMAPPED) {
- wid = ps->active_win->base.id;
- }
- } else {
- log_error(CDBUS_ERROR_BADTGT_S, target);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
-
- return true;
- }
-
- cdbus_reply_wid(ps, msg, wid);
-
- return true;
-}
-
-/**
- * Process a opts_get D-Bus request.
- */
-static bool cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
- const char *target = NULL;
-
- if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
- return false;
-
-#define cdbus_m_opts_get_do(tgt, apdarg_func) \
- if (!strcmp(#tgt, target)) { \
- apdarg_func(ps, msg, ps->o.tgt); \
- return true; \
- }
-
-#define cdbus_m_opts_get_stub(tgt, apdarg_func, ret) \
- if (!strcmp(#tgt, target)) { \
- apdarg_func(ps, msg, ret); \
- return true; \
- }
-
- // version
- if (!strcmp("version", target)) {
- cdbus_reply_string(ps, msg, COMPTON_VERSION);
- return true;
- }
-
- // pid
- if (!strcmp("pid", target)) {
- cdbus_reply_int32(ps, msg, getpid());
- return true;
- }
-
- // display
- if (!strcmp("display", target)) {
- cdbus_reply_string(ps, msg, DisplayString(ps->dpy));
- return true;
- }
-
- cdbus_m_opts_get_stub(config_file, cdbus_reply_string, "Unknown");
- cdbus_m_opts_get_do(write_pid_path, cdbus_reply_string);
- cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool);
- cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool);
- cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool);
- cdbus_m_opts_get_stub(paint_on_overlay, cdbus_reply_bool, ps->overlay != XCB_NONE);
- // paint_on_overlay_id: Get ID of the X composite overlay window
- if (!strcmp("paint_on_overlay_id", target)) {
- cdbus_reply_uint32(ps, msg, ps->overlay);
- return true;
- }
- cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool);
- cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32l);
- cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum);
- cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum);
- cdbus_m_opts_get_do(logpath, cdbus_reply_string);
-
- cdbus_m_opts_get_do(refresh_rate, cdbus_reply_int32);
- cdbus_m_opts_get_do(sw_opti, cdbus_reply_bool);
- cdbus_m_opts_get_do(vsync, cdbus_reply_bool);
- if (!strcmp("backend", target)) {
- assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0]));
- cdbus_reply_string(ps, msg, BACKEND_STRS[ps->o.backend]);
- return true;
- }
-
- cdbus_m_opts_get_do(shadow_red, cdbus_reply_double);
- cdbus_m_opts_get_do(shadow_green, cdbus_reply_double);
- cdbus_m_opts_get_do(shadow_blue, cdbus_reply_double);
- cdbus_m_opts_get_do(shadow_radius, cdbus_reply_int32);
- cdbus_m_opts_get_do(shadow_offset_x, cdbus_reply_int32);
- cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32);
- cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double);
- cdbus_m_opts_get_do(xinerama_shadow_crop, cdbus_reply_bool);
-
- cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32);
- cdbus_m_opts_get_do(fade_in_step, cdbus_reply_double);
- cdbus_m_opts_get_do(fade_out_step, cdbus_reply_double);
- cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool);
-
- cdbus_m_opts_get_do(blur_method, cdbus_reply_bool);
- cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool);
- cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool);
-
- cdbus_m_opts_get_do(inactive_dim, cdbus_reply_double);
- cdbus_m_opts_get_do(inactive_dim_fixed, cdbus_reply_bool);
-
- cdbus_m_opts_get_do(max_brightness, cdbus_reply_double);
-
- cdbus_m_opts_get_do(use_ewmh_active_win, cdbus_reply_bool);
- cdbus_m_opts_get_do(detect_transient, cdbus_reply_bool);
- cdbus_m_opts_get_do(detect_client_leader, cdbus_reply_bool);
- cdbus_m_opts_get_do(use_damage, cdbus_reply_bool);
-
-#ifdef CONFIG_OPENGL
- cdbus_m_opts_get_do(glx_no_stencil, cdbus_reply_bool);
- cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool);
-#endif
-
-#undef cdbus_m_opts_get_do
-#undef cdbus_m_opts_get_stub
-
- log_error(CDBUS_ERROR_BADTGT_S, target);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
-
- return true;
-}
-
-// XXX Remove this after header clean up
-void queue_redraw(session_t *ps);
-
-/**
- * Process a opts_set D-Bus request.
- */
-static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
- const char *target = NULL;
-
- if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
- return false;
-
-#define cdbus_m_opts_set_do(tgt, type, real_type) \
- if (!strcmp(#tgt, target)) { \
- real_type val; \
- if (!cdbus_msg_get_arg(msg, 1, type, &val)) \
- return false; \
- ps->o.tgt = val; \
- goto cdbus_process_opts_set_success; \
- }
-
- // fade_delta
- if (!strcmp("fade_delta", target)) {
- int32_t val = 0;
- if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) {
- return false;
- }
- if (val <= 0) {
- return false;
- }
- ps->o.fade_delta = max2(val, 1);
- goto cdbus_process_opts_set_success;
- }
-
- // fade_in_step
- if (!strcmp("fade_in_step", target)) {
- double val = 0.0;
- if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
- return false;
- ps->o.fade_in_step = normalize_d(val);
- goto cdbus_process_opts_set_success;
- }
-
- // fade_out_step
- if (!strcmp("fade_out_step", target)) {
- double val = 0.0;
- if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
- return false;
- ps->o.fade_out_step = normalize_d(val);
- goto cdbus_process_opts_set_success;
- }
-
- // no_fading_openclose
- if (!strcmp("no_fading_openclose", target)) {
- dbus_bool_t val = FALSE;
- if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
- return false;
- opts_set_no_fading_openclose(ps, val);
- goto cdbus_process_opts_set_success;
- }
-
- // unredir_if_possible
- if (!strcmp("unredir_if_possible", target)) {
- dbus_bool_t val = FALSE;
- if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
- return false;
- if (ps->o.unredir_if_possible != val) {
- ps->o.unredir_if_possible = val;
- queue_redraw(ps);
- }
- goto cdbus_process_opts_set_success;
- }
-
- // clear_shadow
- if (!strcmp("clear_shadow", target)) {
- goto cdbus_process_opts_set_success;
- }
-
- // track_focus
- if (!strcmp("track_focus", target)) {
- goto cdbus_process_opts_set_success;
- }
-
- // redirected_force
- if (!strcmp("redirected_force", target)) {
- cdbus_enum_t val = UNSET;
- if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val))
- return false;
- ps->o.redirected_force = val;
- force_repaint(ps);
- goto cdbus_process_opts_set_success;
- }
-
- // stoppaint_force
- cdbus_m_opts_set_do(stoppaint_force, CDBUS_TYPE_ENUM, cdbus_enum_t);
-
-#undef cdbus_m_opts_set_do
-
- log_error(CDBUS_ERROR_BADTGT_S, target);
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
-
- return true;
-
-cdbus_process_opts_set_success:
- if (!dbus_message_get_no_reply(msg))
- cdbus_reply_bool(ps, msg, true);
- return true;
-}
-
-/**
- * Process an Introspect D-Bus request.
- */
-static bool cdbus_process_introspect(session_t *ps, DBusMessage *msg) {
- static const char *str_introspect =
- "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection "
- "1.0//EN\"\n"
- " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
- "<node name='" CDBUS_OBJECT_NAME "'>\n"
- " <interface name='org.freedesktop.DBus.Introspectable'>\n"
- " <method name='Introspect'>\n"
- " <arg name='data' direction='out' type='s' />\n"
- " </method>\n"
- " </interface>\n"
- " <interface name='org.freedesktop.DBus.Peer'>\n"
- " <method name='Ping' />\n"
- " <method name='GetMachineId'>\n"
- " <arg name='machine_uuid' direction='out' type='s' />\n"
- " </method>\n"
- " </interface>\n"
- " <interface name='" CDBUS_INTERFACE_NAME "'>\n"
- " <signal name='win_added'>\n"
- " <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
- " </signal>\n"
- " <signal name='win_destroyed'>\n"
- " <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
- " </signal>\n"
- " <signal name='win_mapped'>\n"
- " <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
- " </signal>\n"
- " <signal name='win_unmapped'>\n"
- " <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
- " </signal>\n"
- " <signal name='win_focusin'>\n"
- " <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
- " </signal>\n"
- " <signal name='win_focusout'>\n"
- " <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
- " </signal>\n"
- " <method name='reset' />\n"
- " <method name='repaint' />\n"
- " </interface>\n"
- "</node>\n";
-
- cdbus_reply_string(ps, msg, str_introspect);
-
- return true;
-}
-///@}
-
-/**
- * Process a message from D-Bus.
- */
-static DBusHandlerResult
-cdbus_process(DBusConnection *c attr_unused, DBusMessage *msg, void *ud) {
- session_t *ps = ud;
- bool handled = false;
-
-#define cdbus_m_ismethod(method) \
- dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method)
-
- if (cdbus_m_ismethod("reset")) {
- log_info("picom is resetting...");
- ev_break(ps->loop, EVBREAK_ALL);
- if (!dbus_message_get_no_reply(msg))
- cdbus_reply_bool(ps, msg, true);
- handled = true;
- } else if (cdbus_m_ismethod("repaint")) {
- force_repaint(ps);
- if (!dbus_message_get_no_reply(msg))
- cdbus_reply_bool(ps, msg, true);
- handled = true;
- } else if (cdbus_m_ismethod("list_win")) {
- handled = cdbus_process_list_win(ps, msg);
- } else if (cdbus_m_ismethod("win_get")) {
- handled = cdbus_process_win_get(ps, msg);
- } else if (cdbus_m_ismethod("win_set")) {
- handled = cdbus_process_win_set(ps, msg);
- } else if (cdbus_m_ismethod("find_win")) {
- handled = cdbus_process_find_win(ps, msg);
- } else if (cdbus_m_ismethod("opts_get")) {
- handled = cdbus_process_opts_get(ps, msg);
- } else if (cdbus_m_ismethod("opts_set")) {
- handled = cdbus_process_opts_set(ps, msg);
- }
-#undef cdbus_m_ismethod
- else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable",
- "Introspect")) {
- handled = cdbus_process_introspect(ps, msg);
- } else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Peer", "Ping")) {
- cdbus_reply(ps, msg, NULL, NULL);
- handled = true;
- } else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Peer",
- "GetMachineId")) {
- char *uuid = dbus_get_local_machine_id();
- if (uuid) {
- cdbus_reply_string(ps, msg, uuid);
- dbus_free(uuid);
- handled = true;
- }
- } else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") ||
- dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) {
- handled = true;
- } else {
- if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) {
- log_error(
- "Error message of path \"%s\" "
- "interface \"%s\", member \"%s\", error \"%s\"",
- dbus_message_get_path(msg), dbus_message_get_interface(msg),
- dbus_message_get_member(msg), dbus_message_get_error_name(msg));
- } else {
- log_error("Illegal message of type \"%s\", path \"%s\" "
- "interface \"%s\", member \"%s\"",
- cdbus_repr_msgtype(msg), dbus_message_get_path(msg),
- dbus_message_get_interface(msg),
- dbus_message_get_member(msg));
- }
- if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) &&
- !dbus_message_get_no_reply(msg))
- cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S);
- handled = true;
- }
-
- // If the message could not be processed, and an reply is expected, return
- // an empty reply.
- if (!handled && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) &&
- !dbus_message_get_no_reply(msg)) {
- cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S);
- handled = true;
- }
-
- return handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-/** @name Core callbacks
- */
-///@{
-void cdbus_ev_win_added(session_t *ps, struct win *w) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn)
- cdbus_signal_wid(ps, "win_added", w->id);
-}
-
-void cdbus_ev_win_destroyed(session_t *ps, struct win *w) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn)
- cdbus_signal_wid(ps, "win_destroyed", w->id);
-}
-
-void cdbus_ev_win_mapped(session_t *ps, struct win *w) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn)
- cdbus_signal_wid(ps, "win_mapped", w->id);
-}
-
-void cdbus_ev_win_unmapped(session_t *ps, struct win *w) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn)
- cdbus_signal_wid(ps, "win_unmapped", w->id);
-}
-
-void cdbus_ev_win_focusout(session_t *ps, struct win *w) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn)
- cdbus_signal_wid(ps, "win_focusout", w->id);
-}
-
-void cdbus_ev_win_focusin(session_t *ps, struct win *w) {
- struct cdbus_data *cd = ps->dbus_data;
- if (cd->dbus_conn)
- cdbus_signal_wid(ps, "win_focusin", w->id);
-}
-//!@}
diff --git a/src/dbus.h b/src/dbus.h
deleted file mode 100644
index 54a58af..0000000
--- a/src/dbus.h
+++ /dev/null
@@ -1,54 +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 <stdbool.h>
-
-#include <dbus/dbus.h>
-
-typedef struct session session_t;
-struct win;
-
-/**
- * Return a string representation of a D-Bus message type.
- */
-static inline const char *cdbus_repr_msgtype(DBusMessage *msg) {
- return dbus_message_type_to_string(dbus_message_get_type(msg));
-}
-
-/**
- * Initialize D-Bus connection.
- */
-bool cdbus_init(session_t *ps, const char *uniq_name);
-
-/**
- * Destroy D-Bus connection.
- */
-void cdbus_destroy(session_t *ps);
-
-/// Generate dbus win_added signal
-void cdbus_ev_win_added(session_t *ps, struct win *w);
-
-/// Generate dbus win_destroyed signal
-void cdbus_ev_win_destroyed(session_t *ps, struct win *w);
-
-/// Generate dbus win_mapped signal
-void cdbus_ev_win_mapped(session_t *ps, struct win *w);
-
-/// Generate dbus win_unmapped signal
-void cdbus_ev_win_unmapped(session_t *ps, struct win *w);
-
-/// Generate dbus win_focusout signal
-void cdbus_ev_win_focusout(session_t *ps, struct win *w);
-
-/// Generate dbus win_focusin signal
-void cdbus_ev_win_focusin(session_t *ps, struct win *w);
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/diagnostic.c b/src/diagnostic.c
deleted file mode 100644
index d275b1a..0000000
--- a/src/diagnostic.c
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-
-#include <stdio.h>
-#include <xcb/xcb.h>
-#include <xcb/composite.h>
-
-#include "backend/driver.h"
-#include "diagnostic.h"
-#include "config.h"
-#include "picom.h"
-#include "common.h"
-
-void print_diagnostics(session_t *ps, const char *config_file, bool compositor_running) {
- printf("**Version:** " COMPTON_VERSION "\n");
- //printf("**CFLAGS:** %s\n", "??");
- printf("\n### Extensions:\n\n");
- printf("* Shape: %s\n", ps->shape_exists ? "Yes" : "No");
- printf("* XRandR: %s\n", ps->randr_exists ? "Yes" : "No");
- printf("* Present: %s\n", ps->present_exists ? "Present" : "Not Present");
- printf("\n### Misc:\n\n");
- printf("* Use Overlay: %s\n", ps->overlay != XCB_NONE ? "Yes" : "No");
- if (ps->overlay == XCB_NONE) {
- if (compositor_running) {
- printf(" (Another compositor is already running)\n");
- } else if (session_redirection_mode(ps) != XCB_COMPOSITE_REDIRECT_MANUAL) {
- printf(" (Not in manual redirection mode)\n");
- } else {
- printf("\n");
- }
- }
-#ifdef __FAST_MATH__
- printf("* Fast Math: Yes\n");
-#endif
- printf("* Config file used: %s\n", config_file ?: "None");
- printf("\n### Drivers (inaccurate):\n\n");
- print_drivers(ps->drivers);
-
- for (int i = 0; i < NUM_BKEND; i++) {
- if (backend_list[i] && backend_list[i]->diagnostics) {
- printf("\n### Backend: %s\n\n", BACKEND_STRS[i]);
- auto data = backend_list[i]->init(ps);
- if (!data) {
- printf(" Cannot initialize this backend\n");
- } else {
- backend_list[i]->diagnostics(data);
- backend_list[i]->deinit(data);
- }
- }
- }
-}
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/diagnostic.h b/src/diagnostic.h
deleted file mode 100644
index c958589..0000000
--- a/src/diagnostic.h
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-
-#pragma once
-#include <stdbool.h>
-
-typedef struct session session_t;
-
-void print_diagnostics(session_t *, const char *config_file, bool compositor_running);
diff --git a/src/err.h b/src/err.h
deleted file mode 100644
index f989bf9..0000000
--- a/src/err.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2019 Yuxuan Shui <[email protected]>
-
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-#include "compiler.h"
-
-// Functions for error reporting, adopted from Linux
-
-// INFO in user space we can probably be more liberal about what pointer we consider
-// error. e.g. In x86_64 Linux, all addresses with the highest bit set is invalid in user
-// space.
-#define MAX_ERRNO 4095
-
-static inline void *must_use ERR_PTR(intptr_t err) {
- return (void *)err;
-}
-
-static inline intptr_t must_use PTR_ERR(void *ptr) {
- return (intptr_t)ptr;
-}
-
-static inline bool must_use IS_ERR(void *ptr) {
- return unlikely((uintptr_t)ptr > (uintptr_t)-MAX_ERRNO);
-}
-
-static inline bool must_use IS_ERR_OR_NULL(void *ptr) {
- return unlikely(!ptr) || IS_ERR(ptr);
-}
-
-static inline intptr_t must_use PTR_ERR_OR_ZERO(void *ptr) {
- if (IS_ERR(ptr)) {
- return PTR_ERR(ptr);
- }
- return 0;
-}
diff --git a/src/event.c b/src/event.c
deleted file mode 100644
index 5e4017f..0000000
--- a/src/event.c
+++ /dev/null
@@ -1,757 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2019, Yuxuan Shui <[email protected]>
-
-#include <stdio.h>
-
-#include <X11/Xlibint.h>
-#include <X11/extensions/sync.h>
-#include <xcb/damage.h>
-#include <xcb/randr.h>
-
-#include "atom.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "event.h"
-#include "log.h"
-#include "picom.h"
-#include "region.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-/// Event handling with X is complicated. Handling events with other events possibly
-/// in-flight is no good. Because your internal state won't be up to date. Also, querying
-/// the server while events are in-flight is not good. Because events later in the queue
-/// might container information you are querying. Thus those events will cause you to do
-/// unnecessary updates even when you already have the latest information (remember, you
-/// made the query when those events were already in the queue. so the reply you got is
-/// more up-to-date than the events). Also, handling events when other client are making
-/// concurrent requests is not good. Because the server states are changing without you
-/// knowning them. This is super racy, and can cause lots of potential problems.
-///
-/// All of above mandates we do these things:
-/// 1. Grab server when handling events
-/// 2. Make sure the event queue is empty before we make any query to the server
-///
-/// Notice (2) has a dependency circle. To handle events, you sometimes need to make
-/// queries. But to make queries you have to first handle events.
-///
-/// To break that circle, we split all event handling into top and bottom halves. The
-/// bottom half will just look at the event itself, update as much state as they can
-/// without making queries, then queue up necessary works need to be done by the top half.
-/// The top half will do all the other necessary updates. Before entering the top half, we
-/// grab the server and make sure the event queue is empty.
-///
-/// When top half finished, we enter the render stage, where no server state should be
-/// queried. All rendering should be done with our internal knowledge of the server state.
-///
-
-// TODO(yshui) the things described above
-
-/**
- * Get a window's name from window ID.
- */
-static inline const char *ev_window_name(session_t *ps, xcb_window_t wid) {
- char *name = "";
- if (wid) {
- name = "(Failed to get title)";
- if (ps->root == wid) {
- name = "(Root window)";
- } else if (ps->overlay == wid) {
- name = "(Overlay)";
- } else {
- auto w = find_managed_win(ps, wid);
- if (!w) {
- w = find_toplevel(ps, wid);
- }
-
- if (w && w->name) {
- name = w->name;
- }
- }
- }
- return name;
-}
-
-static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_t *ev) {
- switch (ev->response_type) {
- case FocusIn:
- case FocusOut: return ((xcb_focus_in_event_t *)ev)->event;
- case CreateNotify: return ((xcb_create_notify_event_t *)ev)->window;
- case ConfigureNotify: return ((xcb_configure_notify_event_t *)ev)->window;
- case DestroyNotify: return ((xcb_destroy_notify_event_t *)ev)->window;
- case MapNotify: return ((xcb_map_notify_event_t *)ev)->window;
- case UnmapNotify: return ((xcb_unmap_notify_event_t *)ev)->window;
- case ReparentNotify: return ((xcb_reparent_notify_event_t *)ev)->window;
- case CirculateNotify: return ((xcb_circulate_notify_event_t *)ev)->window;
- case Expose: return ((xcb_expose_event_t *)ev)->window;
- case PropertyNotify: return ((xcb_property_notify_event_t *)ev)->window;
- case ClientMessage: return ((xcb_client_message_event_t *)ev)->window;
- default:
- if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
- return ((xcb_damage_notify_event_t *)ev)->drawable;
- }
-
- if (ps->shape_exists && ev->response_type == ps->shape_event) {
- return ((xcb_shape_notify_event_t *)ev)->affected_window;
- }
-
- return 0;
- }
-}
-
-#define CASESTRRET(s) \
- case s: return #s;
-
-static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) {
- static char buf[128];
- switch (ev->response_type & 0x7f) {
- CASESTRRET(FocusIn);
- CASESTRRET(FocusOut);
- CASESTRRET(CreateNotify);
- CASESTRRET(ConfigureNotify);
- CASESTRRET(DestroyNotify);
- CASESTRRET(MapNotify);
- CASESTRRET(UnmapNotify);
- CASESTRRET(ReparentNotify);
- CASESTRRET(CirculateNotify);
- CASESTRRET(Expose);
- CASESTRRET(PropertyNotify);
- CASESTRRET(ClientMessage);
- }
-
- if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type)
- return "Damage";
-
- if (ps->shape_exists && ev->response_type == ps->shape_event)
- return "ShapeNotify";
-
- if (ps->xsync_exists) {
- int o = ev->response_type - ps->xsync_event;
- switch (o) {
- CASESTRRET(XSyncCounterNotify);
- CASESTRRET(XSyncAlarmNotify);
- }
- }
-
- sprintf(buf, "Event %d", ev->response_type);
-
- return buf;
-}
-
-static inline const char *attr_pure ev_focus_mode_name(xcb_focus_in_event_t *ev) {
- switch (ev->mode) {
- CASESTRRET(NotifyNormal);
- CASESTRRET(NotifyWhileGrabbed);
- CASESTRRET(NotifyGrab);
- CASESTRRET(NotifyUngrab);
- }
-
- return "Unknown";
-}
-
-static inline const char *attr_pure ev_focus_detail_name(xcb_focus_in_event_t *ev) {
- switch (ev->detail) {
- CASESTRRET(NotifyAncestor);
- CASESTRRET(NotifyVirtual);
- CASESTRRET(NotifyInferior);
- CASESTRRET(NotifyNonlinear);
- CASESTRRET(NotifyNonlinearVirtual);
- CASESTRRET(NotifyPointer);
- CASESTRRET(NotifyPointerRoot);
- CASESTRRET(NotifyDetailNone);
- }
-
- return "Unknown";
-}
-
-#undef CASESTRRET
-
-static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) {
- log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
- ev_focus_detail_name(ev));
- ps->pending_updates = true;
-}
-
-static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
- log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
- ev_focus_detail_name(ev));
- ps->pending_updates = true;
-}
-
-static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
- if (ev->parent == ps->root) {
- add_win_top(ps, ev->window);
- }
-}
-
-/// Handle configure event of a regular window
-static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
- auto w = find_win(ps, ce->window);
-
- if (!w) {
- return;
- }
-
- if (!w->managed) {
- restack_above(ps, w, ce->above_sibling);
- return;
- }
-
- auto mw = (struct managed_win *)w;
-
- restack_above(ps, w, ce->above_sibling);
-
- // We check against pending_g here, because there might have been multiple
- // configure notifies in this cycle, or the window could receive multiple updates
- // while it's unmapped.
- bool position_changed = mw->pending_g.x != ce->x || mw->pending_g.y != ce->y;
- bool size_changed = mw->pending_g.width != ce->width ||
- mw->pending_g.height != ce->height ||
- mw->pending_g.border_width != ce->border_width;
- if (position_changed || size_changed) {
- // Queue pending updates
- win_set_flags(mw, WIN_FLAGS_FACTOR_CHANGED);
- // TODO(yshui) don't set pending_updates if the window is not
- // visible/mapped
- ps->pending_updates = true;
-
- // At least one of the following if's is true
- if (position_changed) {
- log_trace("Window position changed, %dx%d -> %dx%d", mw->g.x,
- mw->g.y, ce->x, ce->y);
- mw->pending_g.x = ce->x;
- mw->pending_g.y = ce->y;
- win_set_flags(mw, WIN_FLAGS_POSITION_STALE);
- }
-
- if (size_changed) {
- log_trace("Window size changed, %dx%d -> %dx%d", mw->g.width,
- mw->g.height, ce->width, ce->height);
- mw->pending_g.width = ce->width;
- mw->pending_g.height = ce->height;
- mw->pending_g.border_width = ce->border_width;
- win_set_flags(mw, WIN_FLAGS_SIZE_STALE);
- }
-
- // Recalculate which screen this window is on
- win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, mw);
- }
-
- // override_redirect flag cannot be changed after window creation, as far
- // as I know, so there's no point to re-match windows here.
- mw->a.override_redirect = ce->override_redirect;
-}
-
-static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) {
- log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
- ev->event, ev->window, ev->above_sibling, ev->override_redirect);
- if (ev->window == ps->root) {
- set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
- } else {
- configure_win(ps, ev);
- }
-}
-
-static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
- auto w = find_win(ps, ev->window);
- auto mw = find_toplevel(ps, ev->window);
- if (mw && mw->client_win == mw->base.id) {
- // We only want _real_ frame window
- assert(&mw->base == w);
- mw = NULL;
- }
- assert(w == NULL || mw == NULL);
-
- if (w != NULL) {
- auto _ attr_unused = destroy_win_start(ps, w);
- } else if (mw != NULL) {
- win_unmark_client(ps, mw);
- win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
- ps->pending_updates = true;
- } else {
- log_debug("Received a destroy notify from an unknown window, %#010x",
- ev->window);
- }
-}
-
-static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
- // Unmap overlay window if it got mapped but we are currently not
- // in redirected state.
- if (ps->overlay && ev->window == ps->overlay && !ps->redirected) {
- log_debug("Overlay is mapped while we are not redirected");
- auto e = xcb_request_check(ps->c, xcb_unmap_window(ps->c, ps->overlay));
- if (e) {
- log_error("Failed to unmap the overlay window");
- free(e);
- }
- // We don't track the overlay window, so we can return
- return;
- }
-
- auto w = find_managed_win(ps, ev->window);
- if (!w) {
- return;
- }
-
- win_set_flags(w, WIN_FLAGS_MAPPED);
-
- // FocusIn/Out may be ignored when the window is unmapped, so we must
- // recheck focus here
- ps->pending_updates = true; // to update focus
-}
-
-static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) {
- auto w = find_managed_win(ps, ev->window);
- if (w) {
- unmap_win_start(ps, w);
- }
-}
-
-static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
- log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
- ev->window, ev->parent, ev->override_redirect);
- auto w_top = find_toplevel(ps, ev->window);
- if (w_top) {
- win_unmark_client(ps, w_top);
- win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
- ps->pending_updates = true;
- }
-
- if (ev->parent == ps->root) {
- // X will generate reparent notifiy even if the parent didn't actually
- // change (i.e. reparent again to current parent). So we check if that's
- // the case
- auto w = find_win(ps, ev->window);
- if (w) {
- // This window has already been reparented to root before,
- // so we don't need to create a new window for it, we just need to
- // move it to the top
- restack_top(ps, w);
- } else {
- add_win_top(ps, ev->window);
- }
- } else {
- // otherwise, find and destroy the window first
- {
- auto w = find_win(ps, ev->window);
- if (w) {
- auto ret = destroy_win_start(ps, w);
- if (!ret && w->managed) {
- auto mw = (struct managed_win *)w;
- CHECK(win_skip_fading(ps, mw));
- }
- }
- }
-
- // Reset event mask in case something wrong happens
- xcb_change_window_attributes(
- ps->c, ev->window, XCB_CW_EVENT_MASK,
- (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
-
- if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
- log_debug("Window %#010x doesn't have WM_STATE property, it is "
- "probably not a client window. But we will listen for "
- "property change in case it gains one.",
- ev->window);
- xcb_change_window_attributes(
- ps->c, ev->window, XCB_CW_EVENT_MASK,
- (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
- XCB_EVENT_MASK_PROPERTY_CHANGE});
- } else {
- auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
- if (w_real_top && w_real_top->state != WSTATE_UNMAPPED &&
- w_real_top->state != WSTATE_UNMAPPING) {
- log_debug("Mark window %#010x (%s) as having a stale "
- "client",
- w_real_top->base.id, w_real_top->name);
- win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
- ps->pending_updates = true;
- } else {
- if (!w_real_top)
- log_debug("parent %#010x not found", ev->parent);
- else {
- // Window is not currently mapped, unmark its
- // client to trigger a client recheck when it is
- // mapped later.
- win_unmark_client(ps, w_real_top);
- log_debug("parent %#010x (%s) is in state %d",
- w_real_top->base.id, w_real_top->name,
- w_real_top->state);
- }
- }
- }
- }
-}
-
-static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
- auto w = find_win(ps, ev->window);
-
- if (!w)
- return;
-
- if (ev->place == PlaceOnTop) {
- restack_top(ps, w);
- } else {
- restack_bottom(ps, w);
- }
-}
-
-static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) {
- region_t region;
- pixman_region32_init_rects(&region, rects, nrects);
- add_damage(ps, &region);
- pixman_region32_fini(&region);
-}
-
-static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
- if (ev->window == ps->root || (ps->overlay && ev->window == ps->overlay)) {
- int more = ev->count + 1;
- if (ps->n_expose == ps->size_expose) {
- if (ps->expose_rects) {
- ps->expose_rects =
- crealloc(ps->expose_rects, ps->size_expose + more);
- ps->size_expose += more;
- } else {
- ps->expose_rects = ccalloc(more, rect_t);
- ps->size_expose = more;
- }
- }
-
- ps->expose_rects[ps->n_expose].x1 = ev->x;
- ps->expose_rects[ps->n_expose].y1 = ev->y;
- ps->expose_rects[ps->n_expose].x2 = ev->x + ev->width;
- ps->expose_rects[ps->n_expose].y2 = ev->y + ev->height;
- ps->n_expose++;
-
- if (ev->count == 0) {
- expose_root(ps, ps->expose_rects, ps->n_expose);
- ps->n_expose = 0;
- }
- }
-}
-
-static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) {
- if (unlikely(log_get_level_tls() <= LOG_LEVEL_TRACE)) {
- // Print out changed atom
- xcb_get_atom_name_reply_t *reply =
- xcb_get_atom_name_reply(ps->c, xcb_get_atom_name(ps->c, ev->atom), NULL);
- const char *name = "?";
- int name_len = 1;
- if (reply) {
- name = xcb_get_atom_name_name(reply);
- name_len = xcb_get_atom_name_name_length(reply);
- }
-
- log_debug("{ atom = %.*s }", name_len, name);
- free(reply);
- }
-
- if (ps->root == ev->window) {
- // If desktop number property changes
- if (ev->atom == ps->atoms->a_NET_CURRENT_DESKTOP) {
- auto prop = x_get_prop(ps->c, ps->root, ps->atoms->a_NET_CURRENT_DESKTOP,
- 1L, XCB_ATOM_CARDINAL, 32);
-
- if (prop.nitems) {
- ps->root_desktop_switch_direction = ((int)*prop.c32) - ps->root_desktop_num;
- ps->root_desktop_num = (int)*prop.c32;
- }
- }
-
- if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
- // to update focus
- ps->pending_updates = true;
- } else {
- // Destroy the root "image" if the wallpaper probably changed
- if (x_is_root_back_pixmap_atom(ps->atoms, ev->atom)) {
- root_damaged(ps);
- }
- }
-
- // Unconcerned about any other proprties on root window
- return;
- }
-
- ps->pending_updates = true;
- // If WM_STATE changes
- if (ev->atom == ps->atoms->aWM_STATE) {
- // Check whether it could be a client window
- if (!find_toplevel(ps, ev->window)) {
- // Reset event mask anyway
- xcb_change_window_attributes(ps->c, ev->window, XCB_CW_EVENT_MASK,
- (const uint32_t[]){determine_evmask(
- ps, ev->window, WIN_EVMODE_UNKNOWN)});
-
- auto w_top = find_managed_window_or_parent(ps, ev->window);
- // ev->window might have not been managed yet, in that case w_top
- // would be NULL.
- if (w_top) {
- win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
- }
- }
- return;
- }
-
- // If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
- // there are always some stupid applications. (#144)
- if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) {
- struct managed_win *w = NULL;
- if ((w = find_toplevel(ps, ev->window))) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
- // Unnecessay until we remove the queue_redraw in ev_handle
- queue_redraw(ps);
- }
-
- // If _NET_WM_OPACITY changes
- if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY) {
- auto w = find_managed_win(ps, ev->window) ?: find_toplevel(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // If frame extents property changes
- if (ev->atom == ps->atoms->a_NET_FRAME_EXTENTS) {
- auto w = find_toplevel(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // If name changes
- if (ps->atoms->aWM_NAME == ev->atom || ps->atoms->a_NET_WM_NAME == ev->atom) {
- auto w = find_toplevel(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // If class changes
- if (ps->atoms->aWM_CLASS == ev->atom) {
- auto w = find_toplevel(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // If role changes
- if (ps->atoms->aWM_WINDOW_ROLE == ev->atom) {
- auto w = find_toplevel(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // If _COMPTON_SHADOW changes
- if (ps->atoms->a_COMPTON_SHADOW == ev->atom) {
- auto w = find_managed_win(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // If a leader property changes
- if ((ps->o.detect_transient && ps->atoms->aWM_TRANSIENT_FOR == ev->atom) ||
- (ps->o.detect_client_leader && ps->atoms->aWM_CLIENT_LEADER == ev->atom)) {
- auto w = find_toplevel(ps, ev->window);
- if (w) {
- win_set_property_stale(w, ev->atom);
- }
- }
-
- // Check for other atoms we are tracking
- for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
- if (platom->atom == ev->atom) {
- auto w = find_managed_win(ps, ev->window);
- if (!w) {
- w = find_toplevel(ps, ev->window);
- }
- if (w) {
- // Set FACTOR_CHANGED so rules based on properties will be
- // re-evaluated.
- // Don't need to set property stale here, since that only
- // concerns properties we explicitly check.
- win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
- }
- break;
- }
- }
-}
-
-static inline void repair_win(session_t *ps, struct managed_win *w) {
- // Only mapped window can receive damages
- assert(win_is_mapped_in_x(w));
-
- region_t parts;
- pixman_region32_init(&parts);
-
- if (!w->ever_damaged) {
- win_extents(w, &parts);
- set_ignore_cookie(
- ps, xcb_damage_subtract(ps->c, w->damage, XCB_NONE, XCB_NONE));
- } else {
- set_ignore_cookie(
- ps, xcb_damage_subtract(ps->c, w->damage, XCB_NONE, ps->damaged_region));
- x_fetch_region(ps->c, ps->damaged_region, &parts);
- pixman_region32_translate(&parts, w->g.x + w->g.border_width,
- w->g.y + w->g.border_width);
- }
-
- log_trace("Mark window %#010x (%s) as having received damage", w->base.id, w->name);
- w->ever_damaged = true;
- w->pixmap_damaged = true;
-
- // Why care about damage when screen is unredirected?
- // We will force full-screen repaint on redirection.
- if (!ps->redirected) {
- pixman_region32_fini(&parts);
- return;
- }
-
- // Remove the part in the damage area that could be ignored
- if (w->reg_ignore && win_is_region_ignore_valid(ps, w)) {
- pixman_region32_subtract(&parts, &parts, w->reg_ignore);
- }
-
- add_damage(ps, &parts);
- pixman_region32_fini(&parts);
-}
-
-static inline void ev_damage_notify(session_t *ps, xcb_damage_notify_event_t *de) {
- /*
- if (ps->root == de->drawable) {
- root_damaged();
- return;
- } */
-
- auto w = find_managed_win(ps, de->drawable);
-
- if (!w) {
- return;
- }
-
- repair_win(ps, w);
-}
-
-static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev) {
- auto w = find_managed_win(ps, ev->affected_window);
- if (!w || w->a.map_state == XCB_MAP_STATE_UNMAPPED) {
- return;
- }
-
- /*
- * Empty bounding_shape may indicated an
- * unmapped/destroyed window, in which case
- * seemingly BadRegion errors would be triggered
- * if we attempt to rebuild border_size
- */
- // Mark the old bounding shape as damaged
- if (!win_check_flags_any(w, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
- region_t tmp = win_get_bounding_shape_global_by_val(w);
- add_damage(ps, &tmp);
- pixman_region32_fini(&tmp);
- }
- w->reg_ignore_valid = false;
-
- win_set_flags(w, WIN_FLAGS_SIZE_STALE);
- ps->pending_updates = true;
-}
-
-static inline void
-ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) {
- // The only selection we own is the _NET_WM_CM_Sn selection.
- // If we lose that one, we should exit.
- log_fatal("Another composite manager started and took the _NET_WM_CM_Sn "
- "selection.");
- quit(ps);
-}
-
-void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
- if ((ev->response_type & 0x7f) != KeymapNotify) {
- discard_ignore(ps, ev->full_sequence);
- }
-
- xcb_window_t wid = ev_window(ps, ev);
- if (ev->response_type != ps->damage_event + XCB_DAMAGE_NOTIFY) {
- log_debug("event %10.10s serial %#010x window %#010x \"%s\"",
- ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid));
- } else {
- log_trace("event %10.10s serial %#010x window %#010x \"%s\"",
- ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid));
- }
-
- // Check if a custom XEvent constructor was registered in xlib for this event
- // type, and call it discarding the constructed XEvent if any. XESetWireToEvent
- // might be used by libraries to intercept messages from the X server e.g. the
- // OpenGL lib waiting for DRI2 events.
-
- // XXX This exists to workaround compton issue #33, #34, #47
- // For even more details, see:
- // https://bugs.freedesktop.org/show_bug.cgi?id=35945
- // https://lists.freedesktop.org/archives/xcb/2011-November/007337.html
- auto proc = XESetWireToEvent(ps->dpy, ev->response_type, 0);
- if (proc) {
- XESetWireToEvent(ps->dpy, ev->response_type, proc);
- XEvent dummy;
-
- // Stop Xlib from complaining about lost sequence numbers.
- // proc might also just be Xlib internal event processing functions, and
- // because they probably won't see all X replies, they will complain about
- // missing sequence numbers.
- //
- // We only need the low 16 bits
- ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->dpy) & 0xffff);
- proc(ps->dpy, &dummy, (xEvent *)ev);
- }
-
- // XXX redraw needs to be more fine grained
- queue_redraw(ps);
-
- switch (ev->response_type) {
- case FocusIn: ev_focus_in(ps, (xcb_focus_in_event_t *)ev); break;
- case FocusOut: ev_focus_out(ps, (xcb_focus_out_event_t *)ev); break;
- case CreateNotify: ev_create_notify(ps, (xcb_create_notify_event_t *)ev); break;
- case ConfigureNotify:
- ev_configure_notify(ps, (xcb_configure_notify_event_t *)ev);
- break;
- case DestroyNotify:
- ev_destroy_notify(ps, (xcb_destroy_notify_event_t *)ev);
- break;
- case MapNotify: ev_map_notify(ps, (xcb_map_notify_event_t *)ev); break;
- case UnmapNotify: ev_unmap_notify(ps, (xcb_unmap_notify_event_t *)ev); break;
- case ReparentNotify:
- ev_reparent_notify(ps, (xcb_reparent_notify_event_t *)ev);
- break;
- case CirculateNotify:
- ev_circulate_notify(ps, (xcb_circulate_notify_event_t *)ev);
- break;
- case Expose: ev_expose(ps, (xcb_expose_event_t *)ev); break;
- case PropertyNotify:
- ev_property_notify(ps, (xcb_property_notify_event_t *)ev);
- break;
- case SelectionClear:
- ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev);
- break;
- case 0: ev_xcb_error(ps, (xcb_generic_error_t *)ev); break;
- default:
- if (ps->shape_exists && ev->response_type == ps->shape_event) {
- ev_shape_notify(ps, (xcb_shape_notify_event_t *)ev);
- break;
- }
- if (ps->randr_exists &&
- ev->response_type == (ps->randr_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
- set_root_flags(ps, ROOT_FLAGS_SCREEN_CHANGE);
- break;
- }
- if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
- ev_damage_notify(ps, (xcb_damage_notify_event_t *)ev);
- break;
- }
- }
-}
diff --git a/src/event.h b/src/event.h
deleted file mode 100644
index 629dec0..0000000
--- a/src/event.h
+++ /dev/null
@@ -1,8 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2019, Yuxuan Shui <[email protected]>
-
-#include <xcb/xcb.h>
-
-#include "common.h"
-
-void ev_handle(session_t *ps, xcb_generic_event_t *ev);
diff --git a/src/file_watch.c b/src/file_watch.c
deleted file mode 100644
index faa8f68..0000000
--- a/src/file_watch.c
+++ /dev/null
@@ -1,188 +0,0 @@
-#include <errno.h>
-#include <string.h>
-#ifdef HAS_INOTIFY
-#include <sys/inotify.h>
-#elif HAS_KQUEUE
-// clang-format off
-#include <sys/types.h>
-// clang-format on
-#include <sys/event.h>
-#undef EV_ERROR // Avoid clashing with libev's EV_ERROR
-#include <fcntl.h> // For O_RDONLY
-#include <sys/time.h> // For struct timespec
-#include <unistd.h> // For open
-#endif
-
-#include <ev.h>
-#include <uthash.h>
-
-#include "file_watch.h"
-#include "list.h"
-#include "log.h"
-#include "utils.h"
-
-struct watched_file {
- int wd;
- void *ud;
- file_watch_cb_t cb;
-
- UT_hash_handle hh;
-};
-
-struct file_watch_registry {
- struct ev_io w;
-
- struct watched_file *reg;
-};
-
-static void file_watch_ev_cb(EV_P attr_unused, struct ev_io *w, int revent attr_unused) {
- auto fwr = (struct file_watch_registry *)w;
-
- while (true) {
- int wd = -1;
-#ifdef HAS_INOTIFY
- struct inotify_event inotify_event;
- auto ret = read(w->fd, &inotify_event, sizeof(struct inotify_event));
- if (ret < 0) {
- if (errno != EAGAIN) {
- log_error_errno("Failed to read from inotify fd");
- }
- break;
- }
- wd = inotify_event.wd;
-#elif HAS_KQUEUE
- struct kevent ev;
- struct timespec timeout = {0};
- int ret = kevent(fwr->w.fd, NULL, 0, &ev, 1, &timeout);
- if (ret <= 0) {
- if (ret < 0) {
- log_error_errno("Failed to get kevent");
- }
- break;
- }
- wd = (int)ev.ident;
-#else
- assert(false);
-#endif
-
- struct watched_file *wf = NULL;
- HASH_FIND_INT(fwr->reg, &wd, wf);
- if (!wf) {
- log_warn("Got notification for a file I didn't watch.");
- continue;
- }
- wf->cb(wf->ud);
- }
-}
-
-void *file_watch_init(EV_P) {
- log_debug("Starting watching for file changes");
- int fd = -1;
-#ifdef HAS_INOTIFY
- fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
- if (fd < 0) {
- log_error_errno("inotify_init1 failed");
- return NULL;
- }
-#elif HAS_KQUEUE
- fd = kqueue();
- if (fd < 0) {
- log_error_errno("Failed to create kqueue");
- return NULL;
- }
-#else
- log_info("No file watching support found on the host system.");
- return NULL;
-#endif
- auto fwr = ccalloc(1, struct file_watch_registry);
- ev_io_init(&fwr->w, file_watch_ev_cb, fd, EV_READ);
- ev_io_start(EV_A_ & fwr->w);
-
- return fwr;
-}
-
-void file_watch_destroy(EV_P_ void *_fwr) {
- log_debug("Stopping watching for file changes");
- auto fwr = (struct file_watch_registry *)_fwr;
- struct watched_file *i, *tmp;
-
- HASH_ITER(hh, fwr->reg, i, tmp) {
- HASH_DEL(fwr->reg, i);
-#ifdef HAS_KQUEUE
- // kqueue watch descriptors are file descriptors of
- // the files we are watching, so we need to close
- // them
- close(i->wd);
-#endif
- free(i);
- }
-
- ev_io_stop(EV_A_ & fwr->w);
- close(fwr->w.fd);
- free(fwr);
-}
-
-bool file_watch_add(void *_fwr, const char *filename, file_watch_cb_t cb, void *ud) {
- log_debug("Adding \"%s\" to watched files", filename);
- auto fwr = (struct file_watch_registry *)_fwr;
- int wd = -1;
-
- struct stat statbuf;
- int ret = stat(filename, &statbuf);
- if (ret < 0) {
- log_error_errno("Failed to retrieve information about file \"%s\"", filename);
- return false;
- }
- if (!S_ISREG(statbuf.st_mode)) {
- log_info("\"%s\" is not a regular file, not watching it.", filename);
- return false;
- }
-
-#ifdef HAS_INOTIFY
- wd = inotify_add_watch(fwr->w.fd, filename,
- IN_CLOSE_WRITE | IN_MOVE_SELF | IN_DELETE_SELF);
- if (wd < 0) {
- log_error_errno("Failed to watch file \"%s\"", filename);
- return false;
- }
-#elif HAS_KQUEUE
- wd = open(filename, O_RDONLY);
- if (wd < 0) {
- log_error_errno("Cannot open file \"%s\" for watching", filename);
- return false;
- }
-
- uint32_t fflags = NOTE_DELETE | NOTE_RENAME | NOTE_REVOKE | NOTE_ATTRIB;
- // NOTE_CLOSE_WRITE is relatively new, so we cannot just use it
-#ifdef NOTE_CLOSE_WRITE
- fflags |= NOTE_CLOSE_WRITE;
-#else
- // NOTE_WRITE will receive notification more frequent than necessary, so is less
- // preferrable
- fflags |= NOTE_WRITE;
-#endif
- struct kevent ev = {
- .ident = (unsigned int)wd, // the wd < 0 case is checked above
- .filter = EVFILT_VNODE,
- .flags = EV_ADD | EV_CLEAR,
- .fflags = fflags,
- .data = 0,
- .udata = NULL,
- };
- if (kevent(fwr->w.fd, &ev, 1, NULL, 0, NULL) < 0) {
- log_error_errno("Failed to register kevent");
- close(wd);
- return false;
- }
-#else
- assert(false);
-#endif // HAS_KQUEUE
-
- auto w = ccalloc(1, struct watched_file);
- w->wd = wd;
- w->cb = cb;
- w->ud = ud;
-
- HASH_ADD_INT(fwr->reg, wd, w);
- return true;
-}
diff --git a/src/file_watch.h b/src/file_watch.h
deleted file mode 100644
index c249cd2..0000000
--- a/src/file_watch.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-#include <stdbool.h>
-
-#include <ev.h>
-
-typedef void (*file_watch_cb_t)(void *);
-
-void *file_watch_init(EV_P);
-bool file_watch_add(void *, const char *, file_watch_cb_t, void *);
-void file_watch_destroy(EV_P_ void *);
diff --git a/src/kernel.c b/src/kernel.c
deleted file mode 100644
index 5151045..0000000
--- a/src/kernel.c
+++ /dev/null
@@ -1,160 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-#include <assert.h>
-#include <math.h>
-
-#include "compiler.h"
-#include "kernel.h"
-#include "log.h"
-#include "utils.h"
-
-/// Sum a region convolution kernel. Region is defined by a width x height rectangle whose
-/// top left corner is at (x, y)
-double sum_kernel(const conv *map, int x, int y, int width, int height) {
- double ret = 0;
-
- // Compute sum of values which are "in range"
- int xstart = normalize_i_range(x, 0, map->w),
- xend = normalize_i_range(width + x, 0, map->w);
- int ystart = normalize_i_range(y, 0, map->h),
- yend = normalize_i_range(height + y, 0, map->h);
- assert(yend >= ystart && xend >= xstart);
-
- int d = map->w;
- if (map->rsum) {
- // See sum_kernel_preprocess
- double v1 = xstart ? map->rsum[(yend - 1) * d + xstart - 1] : 0;
- double v2 = ystart ? map->rsum[(ystart - 1) * d + xend - 1] : 0;
- double v3 = (xstart && ystart) ? map->rsum[(ystart - 1) * d + xstart - 1] : 0;
- return map->rsum[(yend - 1) * d + xend - 1] - v1 - v2 + v3;
- }
-
- for (int yi = ystart; yi < yend; yi++) {
- for (int xi = xstart; xi < xend; xi++) {
- ret += map->data[yi * d + xi];
- }
- }
-
- return ret;
-}
-
-double sum_kernel_normalized(const conv *map, int x, int y, int width, int height) {
- double ret = sum_kernel(map, x, y, width, height);
- if (ret < 0) {
- ret = 0;
- }
- if (ret > 1) {
- ret = 1;
- }
- return ret;
-}
-
-static inline double attr_const gaussian(double r, double x, double y) {
- // Formula can be found here:
- // https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
- // Except a special case for r == 0 to produce sharp shadows
- if (r == 0)
- return 1;
- return exp(-0.5 * (x * x + y * y) / (r * r)) / (2 * M_PI * r * r);
-}
-
-conv *gaussian_kernel(double r, int size) {
- conv *c;
- int center = size / 2;
- double t;
- assert(size % 2 == 1);
-
- c = cvalloc(sizeof(conv) + (size_t)(size * size) * sizeof(double));
- c->w = c->h = size;
- c->rsum = NULL;
- t = 0.0;
-
- for (int y = 0; y < size; y++) {
- for (int x = 0; x < size; x++) {
- double g = gaussian(r, x - center, y - center);
- t += g;
- c->data[y * size + x] = g;
- }
- }
-
- for (int y = 0; y < size; y++) {
- for (int x = 0; x < size; x++) {
- c->data[y * size + x] /= t;
- }
- }
-
- return c;
-}
-
-/// Estimate the element of the sum of the first row in a gaussian kernel with standard
-/// deviation `r` and size `size`,
-static inline double estimate_first_row_sum(double size, double r) {
- double factor = erf(size / r / sqrt(2));
- double a = exp(-0.5 * size * size / (r * r)) / sqrt(2 * M_PI) / r;
- return a / factor;
-}
-
-/// Pick a suitable gaussian kernel radius for a given kernel size. The returned radius
-/// is the maximum possible radius (<= size*2) that satisfies no sum of the rows in
-/// the kernel are less than `row_limit` (up to certain precision).
-static inline double gaussian_kernel_std_for_size(int size, double row_limit) {
- assert(size > 0);
- if (row_limit >= 1.0 / 2.0 / size) {
- return size * 2;
- }
- double l = 0, r = size * 2;
- while (r - l > 1e-2) {
- double mid = (l + r) / 2.0;
- double vmid = estimate_first_row_sum(size, mid);
- if (vmid > row_limit) {
- r = mid;
- } else {
- l = mid;
- }
- }
- return (l + r) / 2.0;
-}
-
-/// Create a gaussian kernel with auto detected standard deviation. The choosen standard
-/// deviation tries to make sure the outer most pixels of the shadow are completely
-/// transparent, so the transition from shadow to the background is smooth.
-///
-/// @param[in] shadow_radius the radius of the shadow
-conv *gaussian_kernel_autodetect_deviation(int shadow_radius) {
- assert(shadow_radius >= 0);
- int size = shadow_radius * 2 + 1;
-
- if (shadow_radius == 0) {
- return gaussian_kernel(0, size);
- }
- double std = gaussian_kernel_std_for_size(shadow_radius, 1.0 / 256.0);
- return gaussian_kernel(std, size);
-}
-
-/// preprocess kernels to make shadow generation faster
-/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
-void sum_kernel_preprocess(conv *map) {
- if (map->rsum) {
- free(map->rsum);
- }
-
- auto sum = map->rsum = ccalloc(map->w * map->h, double);
- sum[0] = map->data[0];
-
- for (int x = 1; x < map->w; x++) {
- sum[x] = sum[x - 1] + map->data[x];
- }
-
- const int d = map->w;
- for (int y = 1; y < map->h; y++) {
- sum[y * d] = sum[(y - 1) * d] + map->data[y * d];
- for (int x = 1; x < map->w; x++) {
- double tmp = sum[(y - 1) * d + x] + sum[y * d + x - 1] -
- sum[(y - 1) * d + x - 1];
- sum[y * d + x] = tmp + map->data[y * d + x];
- }
- }
-}
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/kernel.h b/src/kernel.h
deleted file mode 100644
index 251d127..0000000
--- a/src/kernel.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-#pragma once
-#include <stdlib.h>
-#include "compiler.h"
-
-/// Code for generating convolution kernels
-
-typedef struct conv {
- int w, h;
- double *rsum;
- double data[];
-} conv;
-
-/// Calculate the sum of a rectangle part of the convolution kernel
-/// the rectangle is defined by top left (x, y), and a size (width x height)
-double attr_pure sum_kernel(const conv *map, int x, int y, int width, int height);
-double attr_pure sum_kernel_normalized(const conv *map, int x, int y, int width, int height);
-
-/// Create a kernel with gaussian distribution with standard deviation `r`, and size
-/// `size`.
-conv *gaussian_kernel(double r, int size);
-
-/// Create a gaussian kernel with auto detected standard deviation. The choosen standard
-/// deviation tries to make sure the outer most pixels of the shadow are completely
-/// transparent.
-///
-/// @param[in] shadow_radius the radius of the shadow
-conv *gaussian_kernel_autodetect_deviation(int shadow_radius);
-
-/// preprocess kernels to make shadow generation faster
-/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
-void sum_kernel_preprocess(conv *map);
-
-static inline void free_conv(conv *k) {
- free(k->rsum);
- free(k);
-}
diff --git a/src/list.h b/src/list.h
deleted file mode 100644
index 19e2c2c..0000000
--- a/src/list.h
+++ /dev/null
@@ -1,108 +0,0 @@
-#pragma once
-#include <stdbool.h>
-#include <stddef.h>
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) \
- ({ \
- const __typeof__(((type *)0)->member) *__mptr = (ptr); \
- (type *)((char *)__mptr - offsetof(type, member)); \
- })
-
-struct list_node {
- struct list_node *next, *prev;
-};
-
-#define list_entry(ptr, type, node) container_of(ptr, type, node)
-#define list_next_entry(ptr, node) list_entry((ptr)->node.next, __typeof__(*(ptr)), node)
-#define list_prev_entry(ptr, node) list_entry((ptr)->node.prev, __typeof__(*(ptr)), node)
-
-/// Insert a new node between two adjacent nodes in the list
-static inline void __list_insert_between(struct list_node *prev, struct list_node *next,
- struct list_node *new_) {
- new_->prev = prev;
- new_->next = next;
- next->prev = new_;
- prev->next = new_;
-}
-
-/// Insert a new node after `curr`
-static inline void list_insert_after(struct list_node *curr, struct list_node *new_) {
- __list_insert_between(curr, curr->next, new_);
-}
-
-/// Insert a new node before `curr`
-static inline void list_insert_before(struct list_node *curr, struct list_node *new_) {
- __list_insert_between(curr->prev, curr, new_);
-}
-
-/// Link two nodes in the list, so `next` becomes the successor node of `prev`
-static inline void __list_link(struct list_node *prev, struct list_node *next) {
- next->prev = prev;
- prev->next = next;
-}
-
-/// Remove a node from the list
-static inline void list_remove(struct list_node *to_remove) {
- __list_link(to_remove->prev, to_remove->next);
- to_remove->prev = (void *)-1;
- to_remove->next = (void *)-2;
-}
-
-/// Move `to_move` so that it's before `new_next`
-static inline void list_move_before(struct list_node *to_move, struct list_node *new_next) {
- list_remove(to_move);
- list_insert_before(new_next, to_move);
-}
-
-/// Move `to_move` so that it's after `new_prev`
-static inline void list_move_after(struct list_node *to_move, struct list_node *new_prev) {
- list_remove(to_move);
- list_insert_after(new_prev, to_move);
-}
-
-/// Initialize a list node that's intended to be the head node
-static inline void list_init_head(struct list_node *head) {
- head->next = head->prev = head;
-}
-
-/// Replace list node `old` with `n`
-static inline void list_replace(struct list_node *old, struct list_node *n) {
- __list_insert_between(old->prev, old->next, n);
- old->prev = (void *)-1;
- old->next = (void *)-2;
-}
-
-/// Return true if head is the only node in the list. Under usual circumstances this means
-/// the list is empty
-static inline bool list_is_empty(const struct list_node *head) {
- return head->prev == head;
-}
-
-/// Return true if `to_check` is the first node in list headed by `head`
-static inline bool
-list_node_is_first(const struct list_node *head, const struct list_node *to_check) {
- return head->next == to_check;
-}
-
-/// Return true if `to_check` is the last node in list headed by `head`
-static inline bool
-list_node_is_last(const struct list_node *head, const struct list_node *to_check) {
- return head->prev == to_check;
-}
-
-#define list_foreach(type, i, head, member) \
- for (type *i = list_entry((head)->next, type, member); &i->member != (head); \
- i = list_next_entry(i, member))
-
-/// Like list_for_each, but it's safe to remove the current list node from the list
-#define list_foreach_safe(type, i, head, member) \
- for (type *i = list_entry((head)->next, type, member), \
- *__tmp = list_next_entry(i, member); \
- &i->member != (head); i = __tmp, __tmp = list_next_entry(i, member))
diff --git a/src/log.c b/src/log.c
deleted file mode 100644
index 0b663e7..0000000
--- a/src/log.c
+++ /dev/null
@@ -1,376 +0,0 @@
-#include <assert.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifdef CONFIG_OPENGL
-#include <GL/gl.h>
-#include "backend/gl/gl_common.h"
-#include "backend/gl/glx.h"
-#endif
-
-#include "compiler.h"
-#include "log.h"
-#include "utils.h"
-
-thread_local struct log *tls_logger;
-
-struct log_target;
-
-struct log {
- struct log_target *head;
-
- int log_level;
-};
-
-struct log_target {
- const struct log_ops *ops;
- struct log_target *next;
-};
-
-struct log_ops {
- void (*write)(struct log_target *, const char *, size_t);
- void (*writev)(struct log_target *, const struct iovec *, int vcnt);
- void (*destroy)(struct log_target *);
-
- /// Additional strings to print around the log_level string
- const char *(*colorize_begin)(enum log_level);
- const char *(*colorize_end)(enum log_level);
-};
-
-/// Fallback writev for targets don't implement it
-static attr_unused void
-log_default_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) {
- size_t total = 0;
- for (int i = 0; i < vcnt; i++) {
- total += vec[i].iov_len;
- }
-
- if (!total) {
- // Nothing to write
- return;
- }
- char *buf = ccalloc(total, char);
- total = 0;
- for (int i = 0; i < vcnt; i++) {
- memcpy(buf + total, vec[i].iov_base, vec[i].iov_len);
- total += vec[i].iov_len;
- }
- tgt->ops->write(tgt, buf, total);
- free(buf);
-}
-
-static attr_const const char *log_level_to_string(enum log_level level) {
- switch (level) {
- case LOG_LEVEL_TRACE: return "TRACE";
- case LOG_LEVEL_DEBUG: return "DEBUG";
- case LOG_LEVEL_INFO: return "INFO";
- case LOG_LEVEL_WARN: return "WARN";
- case LOG_LEVEL_ERROR: return "ERROR";
- case LOG_LEVEL_FATAL: return "FATAL ERROR";
- default: return "????";
- }
-}
-
-enum log_level string_to_log_level(const char *str) {
- if (strcasecmp(str, "TRACE") == 0)
- return LOG_LEVEL_TRACE;
- else if (strcasecmp(str, "DEBUG") == 0)
- return LOG_LEVEL_DEBUG;
- else if (strcasecmp(str, "INFO") == 0)
- return LOG_LEVEL_INFO;
- else if (strcasecmp(str, "WARN") == 0)
- return LOG_LEVEL_WARN;
- else if (strcasecmp(str, "ERROR") == 0)
- return LOG_LEVEL_ERROR;
- return LOG_LEVEL_INVALID;
-}
-
-struct log *log_new(void) {
- auto ret = cmalloc(struct log);
- ret->log_level = LOG_LEVEL_WARN;
- ret->head = NULL;
- return ret;
-}
-
-void log_add_target(struct log *l, struct log_target *tgt) {
- assert(tgt->ops->writev);
- tgt->next = l->head;
- l->head = tgt;
-}
-
-/// Remove a previously added log target for a log struct, and destroy it. If the log
-/// target was never added, nothing happens.
-void log_remove_target(struct log *l, struct log_target *tgt) {
- struct log_target *now = l->head, **prev = &l->head;
- while (now) {
- if (now == tgt) {
- *prev = now->next;
- tgt->ops->destroy(tgt);
- break;
- }
- prev = &now->next;
- now = now->next;
- }
-}
-
-/// Destroy a log struct and every log target added to it
-void log_destroy(struct log *l) {
- // free all tgt
- struct log_target *head = l->head;
- while (head) {
- auto next = head->next;
- head->ops->destroy(head);
- head = next;
- }
- free(l);
-}
-
-void log_set_level(struct log *l, int level) {
- assert(level <= LOG_LEVEL_FATAL && level >= 0);
- l->log_level = level;
-}
-
-enum log_level log_get_level(const struct log *l) {
- return l->log_level;
-}
-
-attr_printf(4, 5) void log_printf(struct log *l, int level, const char *func,
- const char *fmt, ...) {
- assert(level <= LOG_LEVEL_FATAL && level >= 0);
- if (level < l->log_level)
- return;
-
- char *buf = NULL;
- va_list args;
-
- va_start(args, fmt);
- int blen = vasprintf(&buf, fmt, args);
- va_end(args);
-
- if (blen < 0 || !buf) {
- free(buf);
- return;
- }
-
- struct timespec ts;
- timespec_get(&ts, TIME_UTC);
- struct tm now;
- localtime_r(&ts.tv_sec, &now);
- char time_buf[100];
- strftime(time_buf, sizeof time_buf, "%x %T", &now);
-
- char *time = NULL;
- int tlen = asprintf(&time, "%s.%03ld", time_buf, ts.tv_nsec / 1000000);
- if (tlen < 0 || !time) {
- free(buf);
- free(time);
- return;
- }
-
- const char *log_level_str = log_level_to_string(level);
- size_t llen = strlen(log_level_str);
- size_t flen = strlen(func);
-
- struct log_target *head = l->head;
- while (head) {
- const char *p = "", *s = "";
- size_t plen = 0, slen = 0;
-
- if (head->ops->colorize_begin) {
- // construct target specific prefix
- p = head->ops->colorize_begin(level);
- plen = strlen(p);
- if (head->ops->colorize_end) {
- s = head->ops->colorize_end(level);
- slen = strlen(s);
- }
- }
- head->ops->writev(
- head,
- (struct iovec[]){{.iov_base = "[ ", .iov_len = 2},
- {.iov_base = time, .iov_len = (size_t)tlen},
- {.iov_base = " ", .iov_len = 1},
- {.iov_base = (void *)func, .iov_len = flen},
- {.iov_base = " ", .iov_len = 1},
- {.iov_base = (void *)p, .iov_len = plen},
- {.iov_base = (void *)log_level_str, .iov_len = llen},
- {.iov_base = (void *)s, .iov_len = slen},
- {.iov_base = " ] ", .iov_len = 3},
- {.iov_base = buf, .iov_len = (size_t)blen},
- {.iov_base = "\n", .iov_len = 1}},
- 11);
- head = head->next;
- }
- free(time);
- free(buf);
-}
-
-/// A trivial deinitializer that simply frees the memory
-static attr_unused void logger_trivial_destroy(struct log_target *tgt) {
- free(tgt);
-}
-
-/// A null log target that does nothing
-static const struct log_ops null_logger_ops;
-static struct log_target null_logger_target = {
- .ops = &null_logger_ops,
-};
-
-struct log_target *null_logger_new(void) {
- return &null_logger_target;
-}
-
-static void null_logger_write(struct log_target *tgt attr_unused,
- const char *str attr_unused, size_t len attr_unused) {
- return;
-}
-
-static void null_logger_writev(struct log_target *tgt attr_unused,
- const struct iovec *vec attr_unused, int vcnt attr_unused) {
- return;
-}
-
-static const struct log_ops null_logger_ops = {
- .write = null_logger_write,
- .writev = null_logger_writev,
-};
-
-/// A file based logger that writes to file (or stdout/stderr)
-struct file_logger {
- struct log_target tgt;
- FILE *f;
- struct log_ops ops;
-};
-
-static void file_logger_write(struct log_target *tgt, const char *str, size_t len) {
- auto f = (struct file_logger *)tgt;
- fwrite(str, 1, len, f->f);
-}
-
-static void file_logger_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) {
- auto f = (struct file_logger *)tgt;
- fflush(f->f);
- writev(fileno(f->f), vec, vcnt);
-}
-
-static void file_logger_destroy(struct log_target *tgt) {
- auto f = (struct file_logger *)tgt;
- fclose(f->f);
- free(tgt);
-}
-
-#define ANSI(x) "\033[" x "m"
-static const char *terminal_colorize_begin(enum log_level level) {
- switch (level) {
- case LOG_LEVEL_TRACE: return ANSI("30;2");
- case LOG_LEVEL_DEBUG: return ANSI("37;2");
- case LOG_LEVEL_INFO: return ANSI("92");
- case LOG_LEVEL_WARN: return ANSI("33");
- case LOG_LEVEL_ERROR: return ANSI("31;1");
- case LOG_LEVEL_FATAL: return ANSI("30;103;1");
- default: return "";
- }
-}
-
-static const char *terminal_colorize_end(enum log_level level attr_unused) {
- return ANSI("0");
-}
-#undef PREFIX
-
-static const struct log_ops file_logger_ops = {
- .write = file_logger_write,
- .writev = file_logger_writev,
- .destroy = file_logger_destroy,
-};
-
-struct log_target *file_logger_new(const char *filename) {
- FILE *f = fopen(filename, "a");
- if (!f) {
- return NULL;
- }
-
- auto ret = cmalloc(struct file_logger);
- ret->tgt.ops = &ret->ops;
- ret->f = f;
-
- // Always assume a file is not a terminal
- ret->ops = file_logger_ops;
-
- return &ret->tgt;
-}
-
-struct log_target *stderr_logger_new(void) {
- int fd = dup(STDERR_FILENO);
- if (fd < 0) {
- return NULL;
- }
-
- FILE *f = fdopen(fd, "w");
- if (!f) {
- return NULL;
- }
-
- auto ret = cmalloc(struct file_logger);
- ret->tgt.ops = &ret->ops;
- ret->f = f;
- ret->ops = file_logger_ops;
-
- if (isatty(fd)) {
- ret->ops.colorize_begin = terminal_colorize_begin;
- ret->ops.colorize_end = terminal_colorize_end;
- }
- return &ret->tgt;
-}
-
-#ifdef CONFIG_OPENGL
-/// An opengl logger that can be used for logging into opengl debugging tools,
-/// such as apitrace
-struct gl_string_marker_logger {
- struct log_target tgt;
- PFNGLSTRINGMARKERGREMEDYPROC gl_string_marker;
-};
-
-static void
-gl_string_marker_logger_write(struct log_target *tgt, const char *str, size_t len) {
- auto g = (struct gl_string_marker_logger *)tgt;
- // strip newlines at the end of the string
- while (len > 0 && str[len-1] == '\n') {
- len--;
- }
- g->gl_string_marker((GLsizei)len, str);
-}
-
-static const struct log_ops gl_string_marker_logger_ops = {
- .write = gl_string_marker_logger_write,
- .writev = log_default_writev,
- .destroy = logger_trivial_destroy,
-};
-
-struct log_target *gl_string_marker_logger_new(void) {
- if (!gl_has_extension("GL_GREMEDY_string_marker")) {
- return NULL;
- }
-
- void *fnptr = glXGetProcAddress((GLubyte *)"glStringMarkerGREMEDY");
- if (!fnptr)
- return NULL;
-
- auto ret = cmalloc(struct gl_string_marker_logger);
- ret->tgt.ops = &gl_string_marker_logger_ops;
- ret->gl_string_marker = fnptr;
- return &ret->tgt;
-}
-
-#else
-struct log_target *gl_string_marker_logger_new(void) {
- return NULL;
-}
-#endif
-
-// vim: set noet sw=8 ts=8:
diff --git a/src/log.h b/src/log.h
deleted file mode 100644
index e40fe3c..0000000
--- a/src/log.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-
-#pragma once
-#include <assert.h>
-#include <stdio.h>
-
-#include "compiler.h"
-
-enum log_level {
- LOG_LEVEL_INVALID = -1,
- LOG_LEVEL_TRACE = 0,
- LOG_LEVEL_DEBUG,
- LOG_LEVEL_INFO,
- LOG_LEVEL_WARN,
- LOG_LEVEL_ERROR,
- LOG_LEVEL_FATAL,
-};
-
-#define LOG_UNLIKELY(level, x, ...) \
- do { \
- if (unlikely(LOG_LEVEL_##level >= log_get_level_tls())) { \
- log_printf(tls_logger, LOG_LEVEL_##level, __func__, x, ##__VA_ARGS__); \
- } \
- } while (0)
-
-#define LOG(level, x, ...) \
- do { \
- if (LOG_LEVEL_##level >= log_get_level_tls()) { \
- log_printf(tls_logger, LOG_LEVEL_##level, __func__, x, ##__VA_ARGS__); \
- } \
- } while (0)
-#define log_trace(x, ...) LOG_UNLIKELY(TRACE, x, ##__VA_ARGS__)
-#define log_debug(x, ...) LOG_UNLIKELY(DEBUG, x, ##__VA_ARGS__)
-#define log_info(x, ...) LOG(INFO, x, ##__VA_ARGS__)
-#define log_warn(x, ...) LOG(WARN, x, ##__VA_ARGS__)
-#define log_error(x, ...) LOG(ERROR, x, ##__VA_ARGS__)
-#define log_fatal(x, ...) LOG(FATAL, x, ##__VA_ARGS__)
-
-#define log_error_errno(x, ...) LOG(ERROR, x ": %s", ##__VA_ARGS__, strerror(errno))
-
-struct log;
-struct log_target;
-
-attr_printf(4, 5) void log_printf(struct log *, int level, const char *func,
- const char *fmt, ...);
-
-attr_malloc struct log *log_new(void);
-/// Destroy a log struct and every log target added to it
-attr_nonnull_all void log_destroy(struct log *);
-attr_nonnull(1) void log_set_level(struct log *l, int level);
-attr_pure enum log_level log_get_level(const struct log *l);
-attr_nonnull_all void log_add_target(struct log *, struct log_target *);
-attr_pure enum log_level string_to_log_level(const char *);
-/// Remove a previously added log target for a log struct, and destroy it. If the log
-/// target was never added, nothing happens.
-void log_remove_target(struct log *l, struct log_target *tgt);
-
-extern thread_local struct log *tls_logger;
-
-/// Create a thread local logger
-static inline void log_init_tls(void) {
- tls_logger = log_new();
-}
-/// Set thread local logger log level
-static inline void log_set_level_tls(int level) {
- assert(tls_logger);
- log_set_level(tls_logger, level);
-}
-static inline attr_nonnull_all void log_add_target_tls(struct log_target *tgt) {
- assert(tls_logger);
- log_add_target(tls_logger, tgt);
-}
-
-static inline attr_nonnull_all void log_remove_target_tls(struct log_target *tgt) {
- assert(tls_logger);
- log_remove_target(tls_logger, tgt);
-}
-
-static inline attr_pure enum log_level log_get_level_tls(void) {
- assert(tls_logger);
- return log_get_level(tls_logger);
-}
-
-static inline void log_deinit_tls(void) {
- assert(tls_logger);
- log_destroy(tls_logger);
- tls_logger = NULL;
-}
-
-attr_malloc struct log_target *stderr_logger_new(void);
-attr_malloc struct log_target *file_logger_new(const char *file);
-attr_malloc struct log_target *null_logger_new(void);
-attr_malloc struct log_target *gl_string_marker_logger_new(void);
-
-// vim: set noet sw=8 ts=8:
diff --git a/src/meson.build b/src/meson.build
deleted file mode 100644
index 0a882f9..0000000
--- a/src/meson.build
+++ /dev/null
@@ -1,97 +0,0 @@
-libev = dependency('libev', required: false)
-if not libev.found()
- libev = cc.find_library('ev')
-endif
-base_deps = [
- cc.find_library('m'),
- libev
-]
-
-srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
- 'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
- 'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c') ]
-picom_inc = include_directories('.')
-
-cflags = []
-
-required_xcb_packages = [
- 'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync', 'xcb-composite',
- 'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb-glx', 'xcb'
-]
-
-required_packages = [
- 'x11', 'x11-xcb', 'xcb-renderutil', 'xcb-image', 'xext', 'pixman-1'
-]
-
-foreach i : required_packages
- base_deps += [dependency(i, required: true)]
-endforeach
-
-foreach i : required_xcb_packages
- base_deps += [dependency(i, version: '>=1.12.0', required: true)]
-endforeach
-
-if not cc.has_header('uthash.h')
- error('Dependency uthash not found')
-endif
-
-deps = []
-
-if get_option('config_file')
- deps += [dependency('libconfig', version: '>=1.4', required: true)]
-
- cflags += ['-DCONFIG_LIBCONFIG']
- srcs += [ 'config_libconfig.c' ]
-endif
-if get_option('regex')
- pcre = dependency('libpcre', required: true)
- cflags += ['-DCONFIG_REGEX_PCRE']
- if pcre.version().version_compare('>=8.20')
- cflags += ['-DCONFIG_REGEX_PCRE_JIT']
- endif
- deps += [pcre]
-endif
-
-if get_option('vsync_drm')
- cflags += ['-DCONFIG_VSYNC_DRM']
- deps += [dependency('libdrm', required: true)]
-endif
-
-if get_option('opengl')
- cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES']
- deps += [dependency('gl', required: true)]
- srcs += [ 'opengl.c' ]
-endif
-
-if get_option('dbus')
- cflags += ['-DCONFIG_DBUS']
- deps += [dependency('dbus-1', required: true)]
- srcs += [ 'dbus.c' ]
-endif
-
-if get_option('xrescheck')
- cflags += ['-DDEBUG_XRC']
- srcs += [ 'xrescheck.c' ]
-endif
-
-if get_option('unittest')
- cflags += ['-DUNIT_TEST']
-endif
-
-host_system = host_machine.system()
-if host_system == 'linux'
- cflags += ['-DHAS_INOTIFY']
-elif (host_system == 'freebsd' or host_system == 'netbsd' or
- host_system == 'dragonfly' or host_system == 'openbsd')
- cflags += ['-DHAS_KQUEUE']
-endif
-
-subdir('backend')
-
-picom = executable('picom', srcs, c_args: cflags,
- dependencies: [ base_deps, deps, test_h_dep ],
- install: true, include_directories: picom_inc)
-
-if get_option('unittest')
- test('picom unittest', picom, args: [ '--unittest' ])
-endif
diff --git a/src/meta.h b/src/meta.h
deleted file mode 100644
index 4314356..0000000
--- a/src/meta.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2019, Yuxuan Shui <[email protected]>
-
-#pragma once
-
-/// Macro metaprogramming
-
-#define _APPLY1(a, ...) a(__VA_ARGS__)
-#define _APPLY2(a, ...) a(__VA_ARGS__)
-#define _APPLY3(a, ...) a(__VA_ARGS__)
-#define _APPLY4(a, ...) a(__VA_ARGS__)
-
-#define RIOTA1(x) x
-#define RIOTA2(x) RIOTA1(x##1), RIOTA1(x##0)
-#define RIOTA4(x) RIOTA2(x##1), RIOTA2(x##0)
-#define RIOTA8(x) RIOTA4(x##1), RIOTA4(x##0)
-#define RIOTA16(x) RIOTA8(x##1), RIOTA8(x##0)
-/// Generate a list containing 31, 30, ..., 0, in binary
-#define RIOTA32(x) RIOTA16(x##1), RIOTA16(x##0)
-
-#define CONCAT2(a, b) a##b
-#define CONCAT1(a, b) CONCAT2(a, b)
-#define CONCAT(a, b) CONCAT1(a, b)
-
-#define _ARGS_HEAD(head, ...) head
-#define _ARGS_SKIP4(_1, _2, _3, _4, ...) __VA_ARGS__
-#define _ARGS_SKIP8(...) _APPLY1(_ARGS_SKIP4, _ARGS_SKIP4(__VA_ARGS__))
-#define _ARGS_SKIP16(...) _APPLY2(_ARGS_SKIP8, _ARGS_SKIP8(__VA_ARGS__))
-#define _ARGS_SKIP32(...) _APPLY3(_ARGS_SKIP16, _ARGS_SKIP16(__VA_ARGS__))
-
-/// Return the 33rd argument
-#define _ARG33(...) _APPLY4(_ARGS_HEAD, _ARGS_SKIP32(__VA_ARGS__))
-
-/// Return the number of arguments passed in binary, handles at most 31 elements
-#define VA_ARGS_LENGTH(...) _ARG33(0, ##__VA_ARGS__, RIOTA32(0))
-
-#define LIST_APPLY_000000(fn, sep, ...)
-#define LIST_APPLY_000001(fn, sep, x, ...) fn(x)
-#define LIST_APPLY_000010(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000001(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_000011(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000010(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_000100(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000011(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_000101(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000100(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_000110(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000101(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_000111(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000110(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001000(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000111(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001001(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001000(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001010(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001001(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001011(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001010(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001100(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001011(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001101(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001100(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001110(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001101(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_001111(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001110(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010000(fn, sep, x, ...) fn(x) sep() LIST_APPLY_001111(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010001(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010000(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010010(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010001(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010011(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010010(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010100(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010011(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010101(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010100(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010110(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010101(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_010111(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010110(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011000(fn, sep, x, ...) fn(x) sep() LIST_APPLY_010111(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011001(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011000(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011010(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011001(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011011(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011010(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011100(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011011(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011101(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011100(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011110(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011101(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_011111(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011110(fn, sep, __VA_ARGS__)
-#define LIST_APPLY_(N, fn, sep, ...) CONCAT(LIST_APPLY_, N)(fn, sep, __VA_ARGS__)
-#define LIST_APPLY(fn, sep, ...) \
- LIST_APPLY_(VA_ARGS_LENGTH(__VA_ARGS__), fn, sep, __VA_ARGS__)
-
-#define SEP_COMMA() ,
-#define SEP_COLON() ;
-#define SEP_NONE()
diff --git a/src/opengl.c b/src/opengl.c
deleted file mode 100644
index 5d2d66c..0000000
--- a/src/opengl.c
+++ /dev/null
@@ -1,1514 +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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <xcb/render.h>
-#include <xcb/xcb.h>
-
-#include "backend/gl/gl_common.h"
-#include "backend/gl/glx.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "string_utils.h"
-#include "uthash_extra.h"
-#include "utils.h"
-#include "win.h"
-
-#include "opengl.h"
-
-#ifndef GL_TEXTURE_RECTANGLE
-#define GL_TEXTURE_RECTANGLE 0x84F5
-#endif
-
-static inline XVisualInfo *get_visualinfo_from_visual(session_t *ps, xcb_visualid_t visual) {
- XVisualInfo vreq = {.visualid = visual};
- int nitems = 0;
-
- return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
-}
-
-/**
- * Initialize OpenGL.
- */
-bool glx_init(session_t *ps, bool need_render) {
- bool success = false;
- XVisualInfo *pvis = NULL;
-
- // Check for GLX extension
- if (!ps->glx_exists) {
- log_error("No GLX extension.");
- goto glx_init_end;
- }
-
- // Get XVisualInfo
- pvis = get_visualinfo_from_visual(ps, ps->vis);
- if (!pvis) {
- log_error("Failed to acquire XVisualInfo for current visual.");
- goto glx_init_end;
- }
-
- // Ensure the visual is double-buffered
- if (need_render) {
- int value = 0;
- if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
- log_error("Root visual is not a GL visual.");
- goto glx_init_end;
- }
-
- if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
- log_error("Root visual is not a double buffered GL visual.");
- goto glx_init_end;
- }
- }
-
- // Ensure GLX_EXT_texture_from_pixmap exists
- if (need_render && !glxext.has_GLX_EXT_texture_from_pixmap)
- goto glx_init_end;
-
- // Initialize GLX data structure
- if (!ps->psglx) {
- static const glx_session_t CGLX_SESSION_DEF = CGLX_SESSION_INIT;
- ps->psglx = cmalloc(glx_session_t);
- memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t));
-
- // +1 for the zero terminator
- ps->psglx->blur_passes = ccalloc(ps->o.blur_kernel_count, glx_blur_pass_t);
-
- for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
- glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
- ppass->unifm_factor_center = -1;
- ppass->unifm_offset_x = -1;
- ppass->unifm_offset_y = -1;
- }
-
- ps->psglx->round_passes = ccalloc(1, glx_round_pass_t);
- glx_round_pass_t *ppass = ps->psglx->round_passes;
- ppass->unifm_radius = -1;
- ppass->unifm_texcoord = -1;
- ppass->unifm_texsize = -1;
- ppass->unifm_borderw = -1;
- ppass->unifm_borderc = -1;
- ppass->unifm_resolution = -1;
- ppass->unifm_tex_scr = -1;
- }
-
- glx_session_t *psglx = ps->psglx;
-
- if (!psglx->context) {
- // Get GLX context
-#ifndef DEBUG_GLX_DEBUG_CONTEXT
- psglx->context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
-#else
- {
- GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis);
- if (!fbconfig) {
- log_error("Failed to get GLXFBConfig for root visual "
- "%#lx.",
- pvis->visualid);
- goto glx_init_end;
- }
-
- f_glXCreateContextAttribsARB p_glXCreateContextAttribsARB =
- (f_glXCreateContextAttribsARB)glXGetProcAddress(
- (const GLubyte *)"glXCreateContextAttribsARB");
- if (!p_glXCreateContextAttribsARB) {
- log_error("Failed to get glXCreateContextAttribsARB().");
- goto glx_init_end;
- }
-
- static const int attrib_list[] = {
- GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None};
- psglx->context = p_glXCreateContextAttribsARB(
- ps->dpy, fbconfig, NULL, GL_TRUE, attrib_list);
- }
-#endif
-
- if (!psglx->context) {
- log_error("Failed to get GLX context.");
- goto glx_init_end;
- }
-
- // Attach GLX context
- if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), psglx->context)) {
- log_error("Failed to attach GLX context.");
- goto glx_init_end;
- }
-
-#ifdef DEBUG_GLX_DEBUG_CONTEXT
- {
- f_DebugMessageCallback p_DebugMessageCallback =
- (f_DebugMessageCallback)glXGetProcAddress(
- (const GLubyte *)"glDebugMessageCallback");
- if (!p_DebugMessageCallback) {
- log_error("Failed to get glDebugMessageCallback(0.");
- goto glx_init_end;
- }
- p_DebugMessageCallback(glx_debug_msg_callback, ps);
- }
-#endif
- }
-
- // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles
- // in regions don't overlap, so we must use stencil buffer to make sure
- // we don't paint a region for more than one time, I think?
- if (need_render && !ps->o.glx_no_stencil) {
- GLint val = 0;
- glGetIntegerv(GL_STENCIL_BITS, &val);
- if (!val) {
- log_error("Target window doesn't have stencil buffer.");
- goto glx_init_end;
- }
- }
-
- // Check GL_ARB_texture_non_power_of_two, requires a GLX context and
- // must precede FBConfig fetching
- if (need_render)
- psglx->has_texture_non_power_of_two =
- gl_has_extension("GL_ARB_texture_non_power_of_two");
-
- // Render preparations
- if (need_render) {
- glx_on_root_change(ps);
-
- glDisable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glDisable(GL_BLEND);
-
- if (!ps->o.glx_no_stencil) {
- // Initialize stencil buffer
- glClear(GL_STENCIL_BUFFER_BIT);
- glDisable(GL_STENCIL_TEST);
- glStencilMask(0x1);
- glStencilFunc(GL_EQUAL, 0x1, 0x1);
- }
-
- // Clear screen
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- // glXSwapBuffers(ps->dpy, get_tgt_window(ps));
- }
-
- success = true;
-
-glx_init_end:
- XFree(pvis);
-
- if (!success)
- glx_destroy(ps);
-
- return success;
-}
-
-static void glx_free_prog_main(glx_prog_main_t *pprogram) {
- if (!pprogram)
- return;
- if (pprogram->prog) {
- glDeleteProgram(pprogram->prog);
- pprogram->prog = 0;
- }
- pprogram->unifm_opacity = -1;
- pprogram->unifm_invert_color = -1;
- pprogram->unifm_tex = -1;
-}
-
-/**
- * Destroy GLX related resources.
- */
-void glx_destroy(session_t *ps) {
- if (!ps->psglx)
- return;
-
- // Free all GLX resources of windows
- win_stack_foreach_managed(w, &ps->window_stack) {
- free_win_res_glx(ps, w);
- }
-
- // Free GLSL shaders/programs
- for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
- glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
- if (ppass->frag_shader) {
- glDeleteShader(ppass->frag_shader);
- }
- if (ppass->prog) {
- glDeleteProgram(ppass->prog);
- }
- }
- free(ps->psglx->blur_passes);
-
- glx_round_pass_t *ppass = ps->psglx->round_passes;
- if (ppass->frag_shader) {
- glDeleteShader(ppass->frag_shader);
- }
- if (ppass->prog) {
- glDeleteProgram(ppass->prog);
- }
- free(ps->psglx->round_passes);
-
- glx_free_prog_main(&ps->glx_prog_win);
-
- gl_check_err();
-
- // Destroy GLX context
- if (ps->psglx->context) {
- glXMakeCurrent(ps->dpy, None, NULL);
- glXDestroyContext(ps->dpy, ps->psglx->context);
- ps->psglx->context = NULL;
- }
-
- free(ps->psglx);
- ps->psglx = NULL;
- ps->argb_fbconfig = NULL;
-}
-
-/**
- * Callback to run on root window size change.
- */
-void glx_on_root_change(session_t *ps) {
- glViewport(0, 0, ps->root_width, ps->root_height);
-
- // Initialize matrix, copied from dcompmgr
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, ps->root_width, 0, ps->root_height, -1000.0, 1000.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-}
-
-/**
- * Initialize GLX blur filter.
- */
-bool glx_init_blur(session_t *ps) {
- assert(ps->o.blur_kernel_count > 0);
- assert(ps->o.blur_kerns);
- assert(ps->o.blur_kerns[0]);
-
- // Allocate PBO if more than one blur kernel is present
- if (ps->o.blur_kernel_count > 1) {
- // Try to generate a framebuffer
- GLuint fbo = 0;
- glGenFramebuffers(1, &fbo);
- if (!fbo) {
- log_error("Failed to generate Framebuffer. Cannot do multi-pass "
- "blur with GLX"
- " backend.");
- return false;
- }
- glDeleteFramebuffers(1, &fbo);
- }
-
- {
- char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
- // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
- // Thanks to hiciu for reporting.
- setlocale(LC_NUMERIC, "C");
-
- static const char *FRAG_SHADER_BLUR_PREFIX =
- "#version 110\n"
- "%s"
- "uniform float offset_x;\n"
- "uniform float offset_y;\n"
- "uniform float factor_center;\n"
- "uniform %s tex_scr;\n"
- "\n"
- "void main() {\n"
- " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n";
- static const char *FRAG_SHADER_BLUR_ADD =
- " sum += float(%.7g) * %s(tex_scr, vec2(gl_TexCoord[0].x + offset_x "
- "* float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n";
- static const char *FRAG_SHADER_BLUR_SUFFIX =
- " sum += %s(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * "
- "factor_center;\n"
- " gl_FragColor = sum / (factor_center + float(%.7g));\n"
- "}\n";
-
- const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two;
- const char *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D");
- const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D");
- const char *shader_add = FRAG_SHADER_BLUR_ADD;
- char *extension = NULL;
- if (use_texture_rect) {
- mstrextend(&extension, "#extension GL_ARB_texture_rectangle : "
- "require\n");
- }
- if (!extension) {
- extension = strdup("");
- }
-
- for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
- auto kern = ps->o.blur_kerns[i];
- glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
-
- // Build shader
- int width = kern->w, height = kern->h;
- int nele = width * height - 1;
- assert(nele >= 0);
- auto len =
- strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) +
- strlen(extension) +
- (strlen(shader_add) + strlen(texture_func) + 42) * (uint)nele +
- strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1;
- char *shader_str = ccalloc(len, char);
- char *pc = shader_str;
- sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type);
- pc += strlen(pc);
- assert(strlen(shader_str) < len);
-
- double sum = 0.0;
- for (int j = 0; j < height; ++j) {
- for (int k = 0; k < width; ++k) {
- if (height / 2 == j && width / 2 == k)
- continue;
- double val = kern->data[j * width + k];
- if (val == 0) {
- continue;
- }
- sum += val;
- sprintf(pc, shader_add, val, texture_func,
- k - width / 2, j - height / 2);
- pc += strlen(pc);
- assert(strlen(shader_str) < len);
- }
- }
-
- sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum);
- assert(strlen(shader_str) < len);
- ppass->frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, shader_str);
- free(shader_str);
-
- if (!ppass->frag_shader) {
- log_error("Failed to create fragment shader %d.", i);
- free(extension);
- free(lc_numeric_old);
- return false;
- }
-
- // Build program
- ppass->prog = gl_create_program(&ppass->frag_shader, 1);
- if (!ppass->prog) {
- log_error("Failed to create GLSL program.");
- free(extension);
- free(lc_numeric_old);
- return false;
- }
-
- // Get uniform addresses
-#define P_GET_UNIFM_LOC(name, target) \
- { \
- ppass->target = glGetUniformLocation(ppass->prog, name); \
- if (ppass->target < 0) { \
- log_error("Failed to get location of %d-th uniform '" name \
- "'. Might be troublesome.", \
- i); \
- } \
- }
-
- P_GET_UNIFM_LOC("factor_center", unifm_factor_center);
- P_GET_UNIFM_LOC("offset_x", unifm_offset_x);
- P_GET_UNIFM_LOC("offset_y", unifm_offset_y);
-
-#undef P_GET_UNIFM_LOC
- }
- free(extension);
-
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
- }
-
- gl_check_err();
-
- return true;
-}
-
-/**
- * Initialize GLX rounded corners filter.
- */
-bool glx_init_rounded_corners(session_t *ps) {
- char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
- // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
- // Thanks to hiciu for reporting.
- setlocale(LC_NUMERIC, "C");
-
- static const char *FRAG_SHADER =
- "#version 110\n"
- "%s" // extensions
- "uniform float u_radius;\n"
- "uniform float u_borderw;\n"
- "uniform vec4 u_borderc;\n"
- "uniform vec2 u_texcoord;\n"
- "uniform vec2 u_texsize;\n"
- "uniform vec2 u_resolution;\n"
- "uniform %s tex_scr;\n" // sampler2D | sampler2DRect
- "\n"
- "// https://www.shadertoy.com/view/ltS3zW\n"
- "float RectSDF(vec2 p, vec2 b, float r) {\n"
- " vec2 d = abs(p) - b + vec2(r);\n"
- " return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;\n"
- "}\n\n"
- "void main()\n"
- "{\n"
- " vec2 coord = vec2(u_texcoord.x, "
- "u_resolution.y-u_texsize.y-u_texcoord.y);\n"
- " vec4 u_v4WndBgColor = %s(tex_scr, vec2(gl_TexCoord[0].st));\n"
- " float u_fRadiusPx = u_radius;\n"
- " float u_fHalfBorderThickness = u_borderw / 2.0;\n"
- " vec4 u_v4BorderColor = u_borderc;\n"
- " vec4 u_v4FillColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
- " vec4 v4FromColor = u_v4BorderColor; //Always the border "
- "color. If no border, this still should be set\n"
- " vec4 v4ToColor = u_v4WndBgColor; //Outside color is the "
- "background texture\n"
- "\n"
- " vec2 u_v2HalfShapeSizePx = u_texsize/2.0 - "
- "vec2(u_fHalfBorderThickness);\n"
- " vec2 v_v2CenteredPos = (gl_FragCoord.xy - u_texsize.xy / 2.0 - "
- "coord);\n"
- "\n"
- " float fDist = RectSDF(v_v2CenteredPos, u_v2HalfShapeSizePx, "
- "u_fRadiusPx - u_fHalfBorderThickness);\n"
- " if (u_fHalfBorderThickness > 0.0) {\n"
- " if (fDist < 0.0) {\n"
- " v4ToColor = u_v4FillColor;\n"
- " }\n"
- " fDist = abs(fDist) - u_fHalfBorderThickness;\n"
- " } else {\n"
- " v4FromColor = u_v4FillColor;\n"
- " }\n"
- " float fBlendAmount = smoothstep(-1.0, 1.0, fDist);\n"
- " vec4 c = mix(v4FromColor, v4ToColor, fBlendAmount);\n"
- "\n"
- " // final color\n"
- " gl_FragColor = c;\n"
- "\n"
- "}\n";
-
- const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two;
- const char *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D");
- const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D");
- char *extension = NULL;
- if (use_texture_rect) {
- mstrextend(&extension, "#extension GL_ARB_texture_rectangle : "
- "require\n");
- }
- if (!extension) {
- extension = strdup("");
- }
-
- bool success = false;
- // Build rounded corners shader
- auto ppass = ps->psglx->round_passes;
- auto len = strlen(FRAG_SHADER) + strlen(extension) + strlen(sampler_type) +
- strlen(texture_func) + 1;
- char *shader_str = ccalloc(len, char);
-
- sprintf(shader_str, FRAG_SHADER, extension, sampler_type, texture_func);
- assert(strlen(shader_str) < len);
-
- log_debug("Generated rounded corners shader:\n%s\n", shader_str);
-
- ppass->frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, shader_str);
- free(shader_str);
-
- if (!ppass->frag_shader) {
- log_error("Failed to create rounded corners fragment shader.");
- goto out;
- }
-
- // Build program
- ppass->prog = gl_create_program(&ppass->frag_shader, 1);
- if (!ppass->prog) {
- log_error("Failed to create GLSL program.");
- goto out;
- }
-
- // Get uniform addresses
-#define P_GET_UNIFM_LOC(name, target) \
- { \
- ppass->target = glGetUniformLocation(ppass->prog, name); \
- if (ppass->target < 0) { \
- log_debug("Failed to get location of rounded corners uniform " \
- "'" name "'. Might be troublesome."); \
- } \
- }
- P_GET_UNIFM_LOC("u_radius", unifm_radius);
- P_GET_UNIFM_LOC("u_texcoord", unifm_texcoord);
- P_GET_UNIFM_LOC("u_texsize", unifm_texsize);
- P_GET_UNIFM_LOC("u_borderw", unifm_borderw);
- P_GET_UNIFM_LOC("u_borderc", unifm_borderc);
- P_GET_UNIFM_LOC("u_resolution", unifm_resolution);
- P_GET_UNIFM_LOC("tex_scr", unifm_tex_scr);
-#undef P_GET_UNIFM_LOC
-
- success = true;
-
-out:
- free(extension);
-
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
-
- gl_check_err();
-
- return success;
-}
-
-/**
- * Load a GLSL main program from shader strings.
- */
-bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
- glx_prog_main_t *pprogram) {
- assert(pprogram);
-
- // Build program
- pprogram->prog = gl_create_program_from_str(vshader_str, fshader_str);
- if (!pprogram->prog) {
- log_error("Failed to create GLSL program.");
- return false;
- }
-
- // Get uniform addresses
-#define P_GET_UNIFM_LOC(name, target) \
- { \
- pprogram->target = glGetUniformLocation(pprogram->prog, name); \
- if (pprogram->target < 0) { \
- log_error("Failed to get location of uniform '" name \
- "'. Might be troublesome."); \
- } \
- }
- P_GET_UNIFM_LOC("opacity", unifm_opacity);
- P_GET_UNIFM_LOC("invert_color", unifm_invert_color);
- P_GET_UNIFM_LOC("tex", unifm_tex);
- P_GET_UNIFM_LOC("time", unifm_time);
-#undef P_GET_UNIFM_LOC
-
- gl_check_err();
-
- return true;
-}
-
-static inline void glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex,
- int basey, int dx, int dy, int width, int height) {
- if (width > 0 && height > 0) {
- glCopyTexSubImage2D(tex_tgt, 0, dx - basex, dy - basey, dx,
- ps->root_height - dy - height, width, height);
- }
-}
-
-static inline GLuint glx_gen_texture(GLenum tex_tgt, int width, int height) {
- GLuint tex = 0;
- glGenTextures(1, &tex);
- if (!tex) {
- return 0;
- }
- glEnable(tex_tgt);
- glBindTexture(tex_tgt, tex);
- glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(tex_tgt, 0);
-
- return tex;
-}
-
-/**
- * Bind an OpenGL texture and fill it with pixel data from back buffer
- */
-bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, int y,
- int width, int height) {
- if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) {
- return true;
- }
-
- glx_texture_t *ptex = *pptex;
-
- // log_trace("Copying xy(%d %d) wh(%d %d) ptex(%p)", x, y, width, height, ptex);
-
- // Release texture if parameters are inconsistent
- if (ptex && ptex->texture && (ptex->width != width || ptex->height != height)) {
- free_texture(ps, &ptex);
- }
-
- // Allocate structure
- if (!ptex) {
- ptex = ccalloc(1, glx_texture_t);
- *pptex = ptex;
-
- ptex->width = width;
- ptex->height = height;
- ptex->target = GL_TEXTURE_RECTANGLE;
- if (ps->psglx->has_texture_non_power_of_two) {
- ptex->target = GL_TEXTURE_2D;
- }
- }
-
- // Create texture
- if (!ptex->texture) {
- ptex->texture = glx_gen_texture(ptex->target, width, height);
- }
- if (!ptex->texture) {
- log_error("Failed to allocate texture.");
- return false;
- }
-
- // Read destination pixels into a texture
- glEnable(ptex->target);
- glBindTexture(ptex->target, ptex->texture);
- if (width > 0 && height > 0) {
- glx_copy_region_to_tex(ps, ptex->target, x, y, x, y, width, height);
- }
-
- // Cleanup
- glBindTexture(ptex->target, 0);
- glDisable(ptex->target);
-
- gl_check_err();
-
- return true;
-}
-
-/**
- * Bind a X pixmap to an OpenGL texture.
- */
-bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, int width,
- int height, bool repeat, const struct glx_fbconfig_info *fbcfg) {
- if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID)
- return true;
-
- if (!pixmap) {
- log_error("Binding to an empty pixmap %#010x. This can't work.", pixmap);
- return false;
- }
-
- assert(fbcfg);
- glx_texture_t *ptex = *pptex;
- bool need_release = true;
-
- // Release pixmap if parameters are inconsistent
- if (ptex && ptex->texture && ptex->pixmap != pixmap) {
- glx_release_pixmap(ps, ptex);
- }
-
- // Allocate structure
- if (!ptex) {
- static const glx_texture_t GLX_TEX_DEF = {
- .texture = 0,
- .glpixmap = 0,
- .pixmap = 0,
- .target = 0,
- .width = 0,
- .height = 0,
- .y_inverted = false,
- };
-
- ptex = cmalloc(glx_texture_t);
- memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
- *pptex = ptex;
- }
-
- // Create GLX pixmap
- int depth = 0;
- if (!ptex->glpixmap) {
- need_release = false;
-
- // Retrieve pixmap parameters, if they aren't provided
- if (!width || !height) {
- auto r = xcb_get_geometry_reply(
- ps->c, xcb_get_geometry(ps->c, pixmap), NULL);
- if (!r) {
- log_error("Failed to query info of pixmap %#010x.", pixmap);
- return false;
- }
- if (r->depth > OPENGL_MAX_DEPTH) {
- log_error("Requested depth %d higher than %d.", depth,
- OPENGL_MAX_DEPTH);
- return false;
- }
- depth = r->depth;
- width = r->width;
- height = r->height;
- free(r);
- }
-
- // Determine texture target, copied from compiz
- // The assumption we made here is the target never changes based on any
- // pixmap-specific parameters, and this may change in the future
- GLenum tex_tgt = 0;
- if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts &&
- ps->psglx->has_texture_non_power_of_two)
- tex_tgt = GLX_TEXTURE_2D_EXT;
- else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts)
- tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
- else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts))
- tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
- else
- tex_tgt = GLX_TEXTURE_2D_EXT;
-
- log_debug("depth %d, tgt %#x, rgba %d", depth, tex_tgt,
- (GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt));
-
- GLint attrs[] = {
- GLX_TEXTURE_FORMAT_EXT,
- fbcfg->texture_fmt,
- GLX_TEXTURE_TARGET_EXT,
- (GLint)tex_tgt,
- 0,
- };
-
- ptex->glpixmap = glXCreatePixmap(ps->dpy, fbcfg->cfg, pixmap, attrs);
- ptex->pixmap = pixmap;
- ptex->target =
- (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE);
- ptex->width = width;
- ptex->height = height;
- ptex->y_inverted = fbcfg->y_inverted;
- }
- if (!ptex->glpixmap) {
- log_error("Failed to allocate GLX pixmap.");
- return false;
- }
-
- glEnable(ptex->target);
-
- // Create texture
- if (!ptex->texture) {
- need_release = false;
-
- GLuint texture = 0;
- glGenTextures(1, &texture);
- glBindTexture(ptex->target, texture);
-
- glTexParameteri(ptex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(ptex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- if (repeat) {
- glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_REPEAT);
- } else {
- glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- glBindTexture(ptex->target, 0);
-
- ptex->texture = texture;
- }
- if (!ptex->texture) {
- log_error("Failed to allocate texture.");
- return false;
- }
-
- glBindTexture(ptex->target, ptex->texture);
-
- // The specification requires rebinding whenever the content changes...
- // We can't follow this, too slow.
- if (need_release)
- glXReleaseTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
-
- glXBindTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
-
- // Cleanup
- glBindTexture(ptex->target, 0);
- glDisable(ptex->target);
-
- gl_check_err();
-
- return true;
-}
-
-/**
- * @brief Release binding of a texture.
- */
-void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
- // Release binding
- if (ptex->glpixmap && ptex->texture) {
- glBindTexture(ptex->target, ptex->texture);
- glXReleaseTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
- glBindTexture(ptex->target, 0);
- }
-
- // Free GLX Pixmap
- if (ptex->glpixmap) {
- glXDestroyPixmap(ps->dpy, ptex->glpixmap);
- ptex->glpixmap = 0;
- }
-
- gl_check_err();
-}
-
-/**
- * Set clipping region on the target window.
- */
-void glx_set_clip(session_t *ps, const region_t *reg) {
- // Quit if we aren't using stencils
- if (ps->o.glx_no_stencil)
- return;
-
- glDisable(GL_STENCIL_TEST);
- glDisable(GL_SCISSOR_TEST);
-
- if (!reg)
- return;
-
- int nrects;
- const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
-
- if (nrects == 1) {
- glEnable(GL_SCISSOR_TEST);
- glScissor(rects[0].x1, ps->root_height - rects[0].y2,
- rects[0].x2 - rects[0].x1, rects[0].y2 - rects[0].y1);
- }
-
- gl_check_err();
-}
-
-#define P_PAINTREG_START(var) \
- region_t reg_new; \
- int nrects; \
- const rect_t *rects; \
- assert(width >= 0 && height >= 0); \
- pixman_region32_init_rect(&reg_new, dx, dy, (uint)width, (uint)height); \
- pixman_region32_intersect(&reg_new, &reg_new, (region_t *)reg_tgt); \
- rects = pixman_region32_rectangles(&reg_new, &nrects); \
- glBegin(GL_QUADS); \
- \
- for (int ri = 0; ri < nrects; ++ri) { \
- rect_t var = rects[ri];
-
-#define P_PAINTREG_END() \
- } \
- glEnd(); \
- \
- pixman_region32_fini(&reg_new);
-
-/**
- * Blur contents in a particular region.
- *
- * XXX seems to be way to complex for what it does
- */
-bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
- GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc) {
- assert(ps->psglx->blur_passes[0].prog);
- const bool more_passes = ps->o.blur_kernel_count > 1;
- const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST);
- const bool have_stencil = glIsEnabled(GL_STENCIL_TEST);
- bool ret = false;
-
- // Calculate copy region size
- glx_blur_cache_t ibc = {.width = 0, .height = 0};
- if (!pbc)
- pbc = &ibc;
-
- int mdx = dx, mdy = dy, mwidth = width, mheight = height;
- // log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight);
-
- /*
- if (ps->o.resize_damage > 0) {
- int inc_x = 0, inc_y = 0;
- for (int i = 0; i < MAX_BLUR_PASS; ++i) {
- XFixed *kern = ps->o.blur_kerns[i];
- if (!kern) break;
- inc_x += XFIXED_TO_DOUBLE(kern[0]) / 2;
- inc_y += XFIXED_TO_DOUBLE(kern[1]) / 2;
- }
- inc_x = min2(ps->o.resize_damage, inc_x);
- inc_y = min2(ps->o.resize_damage, inc_y);
-
- mdx = max2(dx - inc_x, 0);
- mdy = max2(dy - inc_y, 0);
- int mdx2 = min2(dx + width + inc_x, ps->root_width),
- mdy2 = min2(dy + height + inc_y, ps->root_height);
- mwidth = mdx2 - mdx;
- mheight = mdy2 - mdy;
- }
- */
-
- GLenum tex_tgt = GL_TEXTURE_RECTANGLE;
- if (ps->psglx->has_texture_non_power_of_two)
- tex_tgt = GL_TEXTURE_2D;
-
- // Free textures if size inconsistency discovered
- if (mwidth != pbc->width || mheight != pbc->height)
- free_glx_bc_resize(ps, pbc);
-
- // Generate FBO and textures if needed
- if (!pbc->textures[0])
- pbc->textures[0] = glx_gen_texture(tex_tgt, mwidth, mheight);
- GLuint tex_scr = pbc->textures[0];
- if (more_passes && !pbc->textures[1])
- pbc->textures[1] = glx_gen_texture(tex_tgt, mwidth, mheight);
- pbc->width = mwidth;
- pbc->height = mheight;
- GLuint tex_scr2 = pbc->textures[1];
- if (more_passes && !pbc->fbo)
- glGenFramebuffers(1, &pbc->fbo);
- const GLuint fbo = pbc->fbo;
-
- if (!tex_scr || (more_passes && !tex_scr2)) {
- log_error("Failed to allocate texture.");
- goto glx_blur_dst_end;
- }
- if (more_passes && !fbo) {
- log_error("Failed to allocate framebuffer.");
- goto glx_blur_dst_end;
- }
-
- // Read destination pixels into a texture
- glEnable(tex_tgt);
- glBindTexture(tex_tgt, tex_scr);
- glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight);
- /*
- if (tex_scr2) {
- glBindTexture(tex_tgt, tex_scr2);
- glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, dx - mdx);
- glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy + height,
- mwidth, mdy + mheight - dy - height);
- glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy, dx - mdx, height);
- glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, dx + width, dy,
- mdx + mwidth - dx - width, height);
- } */
-
- // Texture scaling factor
- GLfloat texfac_x = 1.0f, texfac_y = 1.0f;
- if (tex_tgt == GL_TEXTURE_2D) {
- texfac_x /= (GLfloat)mwidth;
- texfac_y /= (GLfloat)mheight;
- }
-
- // Paint it back
- if (more_passes) {
- glDisable(GL_STENCIL_TEST);
- glDisable(GL_SCISSOR_TEST);
- }
-
- bool last_pass = false;
- for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
- last_pass = (i == ps->o.blur_kernel_count - 1);
- const glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
- assert(ppass->prog);
-
- assert(tex_scr);
- glBindTexture(tex_tgt, tex_scr);
-
- if (!last_pass) {
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, tex_scr2, 0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- log_error("Framebuffer attachment failed.");
- goto glx_blur_dst_end;
- }
- } else {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glDrawBuffer(GL_BACK);
- if (have_scissors)
- glEnable(GL_SCISSOR_TEST);
- if (have_stencil)
- glEnable(GL_STENCIL_TEST);
- }
-
- // Color negation for testing...
- // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
- // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
- // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
-
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glUseProgram(ppass->prog);
- if (ppass->unifm_offset_x >= 0)
- glUniform1f(ppass->unifm_offset_x, texfac_x);
- if (ppass->unifm_offset_y >= 0)
- glUniform1f(ppass->unifm_offset_y, texfac_y);
- if (ppass->unifm_factor_center >= 0)
- glUniform1f(ppass->unifm_factor_center, factor_center);
-
- P_PAINTREG_START(crect) {
- auto rx = (GLfloat)(crect.x1 - mdx) * texfac_x;
- auto ry = (GLfloat)(mheight - (crect.y1 - mdy)) * texfac_y;
- auto rxe = rx + (GLfloat)(crect.x2 - crect.x1) * texfac_x;
- auto rye = ry - (GLfloat)(crect.y2 - crect.y1) * texfac_y;
- auto rdx = (GLfloat)(crect.x1 - mdx);
- auto rdy = (GLfloat)(mheight - crect.y1 + mdy);
- if (last_pass) {
- rdx = (GLfloat)crect.x1;
- rdy = (GLfloat)(ps->root_height - crect.y1);
- }
- auto rdxe = rdx + (GLfloat)(crect.x2 - crect.x1);
- auto rdye = rdy - (GLfloat)(crect.y2 - crect.y1);
-
- // log_trace("%f, %f, %f, %f -> %f, %f, %f, %f", rx, ry,
- // rxe, rye, rdx,
- // rdy, rdxe, rdye);
-
- glTexCoord2f(rx, ry);
- glVertex3f(rdx, rdy, z);
-
- glTexCoord2f(rxe, ry);
- glVertex3f(rdxe, rdy, z);
-
- glTexCoord2f(rxe, rye);
- glVertex3f(rdxe, rdye, z);
-
- glTexCoord2f(rx, rye);
- glVertex3f(rdx, rdye, z);
- }
- P_PAINTREG_END();
-
- glUseProgram(0);
-
- // Swap tex_scr and tex_scr2
- {
- GLuint tmp = tex_scr2;
- tex_scr2 = tex_scr;
- tex_scr = tmp;
- }
- }
-
- ret = true;
-
-glx_blur_dst_end:
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glBindTexture(tex_tgt, 0);
- glDisable(tex_tgt);
- if (have_scissors)
- glEnable(GL_SCISSOR_TEST);
- if (have_stencil)
- glEnable(GL_STENCIL_TEST);
-
- if (&ibc == pbc) {
- free_glx_bc(ps, pbc);
- }
-
- gl_check_err();
-
- return ret;
-}
-
-// TODO(bhagwan) this is a mess and needs a more consistent way of getting the border
-// pixel I tried looking for a notify event for XCB_CW_BORDER_PIXEL (in
-// xcb_create_window()) or a way to get the pixels from xcb_render_picture_t but the
-// documentation for the xcb_xrender extension is literaly non existent...
-//
-// NOTE(yshui) There is no consistent way to get the "border" color of a X window. From
-// the WM's perspective there are multiple ways to implement window borders. Using
-// glReadPixel is probably the most reliable way.
-void glx_read_border_pixel(int root_height, int root_width, int x, int y, int width,
- int height, float *ppixel) {
- assert(ppixel);
-
- // Reset the color so the shader doesn't use it
- ppixel[0] = ppixel[1] = ppixel[2] = ppixel[3] = -1.0F;
-
- // First try bottom left corner past the
- // circle radius (after the rounded corner ends)
- auto screen_x = x;
- auto screen_y = root_height - height - y;
-
- // X is out of bounds
- // move to the right side
- if (screen_x < 0) {
- screen_x += width;
- }
-
- // Y is out of bounds
- // move to to top part
- if (screen_y < 0) {
- screen_y += height;
- }
-
- // All corners are out of bounds, give up
- if (screen_x < 0 || screen_y < 0 || screen_x >= root_width || screen_y >= root_height) {
- return;
- }
-
- // Invert Y-axis so we can query border color from texture (0,0)
- glReadPixels(screen_x, screen_y, 1, 1, GL_RGBA, GL_FLOAT, (void *)ppixel);
-
- log_trace("xy(%d, %d), glxy(%d %d) wh(%d %d), border_col(%.2f, %.2f, %.2f, %.2f)",
- x, y, screen_x, screen_y, width, height, (float)ppixel[0],
- (float)ppixel[1], (float)ppixel[2], (float)ppixel[3]);
-
- gl_check_err();
-}
-
-bool glx_round_corners_dst(session_t *ps, struct managed_win *w,
- const glx_texture_t *ptex, int dx, int dy, int width,
- int height, float z, float cr, const region_t *reg_tgt) {
- assert(ps->psglx->round_passes->prog);
- bool ret = false;
-
- // log_warn("dxy(%d, %d) wh(%d %d) rwh(%d %d) b(%d), f(%d)",
- // dx, dy, width, height, ps->root_width, ps->root_height, w->g.border_width,
- // w->focused);
-
- int mdx = dx, mdy = dy, mwidth = width, mheight = height;
- log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight);
-
- if (w->g.border_width > 0) {
- glx_read_border_pixel(ps->root_height, ps->root_width, dx, dy, width,
- height, &w->border_col[0]);
- }
-
- {
- const glx_round_pass_t *ppass = ps->psglx->round_passes;
- assert(ppass->prog);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-
- glUseProgram(ppass->prog);
-
- // If caller specified a texture use it as source
- log_trace("ptex: %p wh(%d %d) %d %d", ptex, ptex->width, ptex->height,
- ptex->target, ptex->texture);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(ptex->target, ptex->texture);
-
- if (ppass->unifm_tex_scr >= 0) {
- glUniform1i(ppass->unifm_tex_scr, (GLint)0);
- }
- if (ppass->unifm_radius >= 0) {
- glUniform1f(ppass->unifm_radius, cr);
- }
- if (ppass->unifm_texcoord >= 0) {
- glUniform2f(ppass->unifm_texcoord, (float)dx, (float)dy);
- }
- if (ppass->unifm_texsize >= 0) {
- glUniform2f(ppass->unifm_texsize, (float)mwidth, (float)mheight);
- }
- if (ppass->unifm_borderw >= 0) {
- // Don't render rounded border if we don't know the border color
- glUniform1f(ppass->unifm_borderw,
- w->border_col[0] != -1. ? (GLfloat)w->g.border_width : 0);
- }
- if (ppass->unifm_borderc >= 0) {
- glUniform4f(ppass->unifm_borderc, w->border_col[0],
- w->border_col[1], w->border_col[2], w->border_col[3]);
- }
- if (ppass->unifm_resolution >= 0) {
- glUniform2f(ppass->unifm_resolution, (float)ps->root_width,
- (float)ps->root_height);
- }
-
- // Painting
- {
- P_PAINTREG_START(crect) {
- // texture-local coordinates
- auto rx = (GLfloat)(crect.x1 - dx);
- auto ry = (GLfloat)(crect.y1 - dy);
- auto rxe = rx + (GLfloat)(crect.x2 - crect.x1);
- auto rye = ry + (GLfloat)(crect.y2 - crect.y1);
- if (GL_TEXTURE_2D == ptex->target) {
- rx = rx / (GLfloat)width;
- ry = ry / (GLfloat)height;
- rxe = rxe / (GLfloat)width;
- rye = rye / (GLfloat)height;
- }
-
- // coordinates for the texture in the target
- auto rdx = (GLfloat)crect.x1;
- auto rdy = (GLfloat)(ps->root_height - crect.y1);
- auto rdxe = (GLfloat)rdx + (GLfloat)(crect.x2 - crect.x1);
- auto rdye = (GLfloat)rdy - (GLfloat)(crect.y2 - crect.y1);
-
- // Invert Y if needed, this may not work as expected,
- // though. I don't have such a FBConfig to test with.
- ry = 1.0F - ry;
- rye = 1.0F - rye;
-
- // log_trace("Rect %d (i:%d): %f, %f, %f, %f -> %f, %f,
- // %f, %f", ri ,ptex ? ptex->y_inverted : -1, rx, ry,
- // rxe,
- // rye, rdx, rdy, rdxe, rdye);
-
- glTexCoord2f(rx, ry);
- glVertex3f(rdx, rdy, z);
-
- glTexCoord2f(rxe, ry);
- glVertex3f(rdxe, rdy, z);
-
- glTexCoord2f(rxe, rye);
- glVertex3f(rdxe, rdye, z);
-
- glTexCoord2f(rx, rye);
- glVertex3f(rdx, rdye, z);
- }
- P_PAINTREG_END();
- }
-
- glUseProgram(0);
- glDisable(GL_BLEND);
- }
-
- ret = true;
-
- glBindTexture(ptex->target, 0);
- glDisable(ptex->target);
- glDisable(GL_BLEND);
-
- gl_check_err();
-
- return ret;
-}
-
-bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
- GLfloat factor, const region_t *reg_tgt) {
- // It's possible to dim in glx_render(), but it would be over-complicated
- // considering all those mess in color negation and modulation
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glColor4f(0.0f, 0.0f, 0.0f, factor);
-
- P_PAINTREG_START(crect) {
- // XXX what does all of these variables mean?
- GLint rdx = crect.x1;
- GLint rdy = ps->root_height - crect.y1;
- GLint rdxe = rdx + (crect.x2 - crect.x1);
- GLint rdye = rdy - (crect.y2 - crect.y1);
-
- glVertex3i(rdx, rdy, z);
- glVertex3i(rdxe, rdy, z);
- glVertex3i(rdxe, rdye, z);
- glVertex3i(rdx, rdye, z);
- }
- P_PAINTREG_END();
-
- glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
- glDisable(GL_BLEND);
-
- gl_check_err();
-
- return true;
-}
-
-/**
- * @brief Render a region with texture data.
- */
-bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy,
- int width, int height, int z, double opacity, bool argb, bool neg,
- const region_t *reg_tgt, const glx_prog_main_t *pprogram) {
- if (!ptex || !ptex->texture) {
- log_error("Missing texture.");
- return false;
- }
-
- const bool has_prog = pprogram && pprogram->prog;
- bool dual_texture = false;
-
- // It's required by legacy versions of OpenGL to enable texture target
- // before specifying environment. Thanks to madsy for telling me.
- glEnable(ptex->target);
-
- // Enable blending if needed
- if (opacity < 1.0 || argb) {
-
- glEnable(GL_BLEND);
-
- // Needed for handling opacity of ARGB texture
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-
- // This is all weird, but X Render is using premultiplied ARGB format, and
- // we need to use those things to correct it. Thanks to derhass for help.
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glColor4d(opacity, opacity, opacity, opacity);
- }
-
- if (!has_prog) {
- // The default, fixed-function path
- // Color negation
- if (neg) {
- // Simple color negation
- if (!glIsEnabled(GL_BLEND)) {
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_COPY_INVERTED);
- }
- // ARGB texture color negation
- else if (argb) {
- dual_texture = true;
-
- // Use two texture stages because the calculation is too
- // complicated, thanks to madsy for providing code Texture
- // stage 0
- glActiveTexture(GL_TEXTURE0);
-
- // Negation for premultiplied color: color = A - C
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
-
- // Pass texture alpha through
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-
- // Texture stage 1
- glActiveTexture(GL_TEXTURE1);
- glEnable(ptex->target);
- glBindTexture(ptex->target, ptex->texture);
-
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-
- // Modulation with constant factor
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
-
- // Modulation with constant factor
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
-
- glActiveTexture(GL_TEXTURE0);
- }
- // RGB blend color negation
- else {
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-
- // Modulation with constant factor
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB,
- GL_ONE_MINUS_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
-
- // Modulation with constant factor
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
- }
- }
- } else {
- // Programmable path
- assert(pprogram->prog);
- glUseProgram(pprogram->prog);
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- if (pprogram->unifm_opacity >= 0)
- glUniform1f(pprogram->unifm_opacity, (float)opacity);
- if (pprogram->unifm_invert_color >= 0)
- glUniform1i(pprogram->unifm_invert_color, neg);
- if (pprogram->unifm_tex >= 0)
- glUniform1i(pprogram->unifm_tex, 0);
- if (pprogram->unifm_time >= 0)
- glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0f +
- (float)ts.tv_nsec / 1.0e6f);
- }
-
- // log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d", x, y, width, height,
- // dx, dy, ptex->width, ptex->height, z);
-
- // Bind texture
- glBindTexture(ptex->target, ptex->texture);
- if (dual_texture) {
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(ptex->target, ptex->texture);
- glActiveTexture(GL_TEXTURE0);
- }
-
- // Painting
- {
- P_PAINTREG_START(crect) {
- // texture-local coordinates
- auto rx = (GLfloat)(crect.x1 - dx + x);
- auto ry = (GLfloat)(crect.y1 - dy + y);
- auto rxe = rx + (GLfloat)(crect.x2 - crect.x1);
- auto rye = ry + (GLfloat)(crect.y2 - crect.y1);
- // Rectangle textures have [0-w] [0-h] while 2D texture has [0-1]
- // [0-1] Thanks to amonakov for pointing out!
- if (GL_TEXTURE_2D == ptex->target) {
- rx = rx / (GLfloat)ptex->width;
- ry = ry / (GLfloat)ptex->height;
- rxe = rxe / (GLfloat)ptex->width;
- rye = rye / (GLfloat)ptex->height;
- }
-
- // coordinates for the texture in the target
- GLint rdx = crect.x1;
- GLint rdy = ps->root_height - crect.y1;
- GLint rdxe = rdx + (crect.x2 - crect.x1);
- GLint rdye = rdy - (crect.y2 - crect.y1);
-
- // Invert Y if needed, this may not work as expected, though. I
- // don't have such a FBConfig to test with.
- if (!ptex->y_inverted) {
- ry = 1.0f - ry;
- rye = 1.0f - rye;
- }
-
- // log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d", ri, rx,
- // ry, rxe, rye,
- // rdx, rdy, rdxe, rdye);
-
-#define P_TEXCOORD(cx, cy) \
- { \
- if (dual_texture) { \
- glMultiTexCoord2f(GL_TEXTURE0, cx, cy); \
- glMultiTexCoord2f(GL_TEXTURE1, cx, cy); \
- } else \
- glTexCoord2f(cx, cy); \
- }
- P_TEXCOORD(rx, ry);
- glVertex3i(rdx, rdy, z);
-
- P_TEXCOORD(rxe, ry);
- glVertex3i(rdxe, rdy, z);
-
- P_TEXCOORD(rxe, rye);
- glVertex3i(rdxe, rdye, z);
-
- P_TEXCOORD(rx, rye);
- glVertex3i(rdx, rdye, z);
- }
- P_PAINTREG_END();
- }
-
- // Cleanup
- glBindTexture(ptex->target, 0);
- glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glDisable(GL_BLEND);
- glDisable(GL_COLOR_LOGIC_OP);
- glDisable(ptex->target);
-
- if (dual_texture) {
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(ptex->target, 0);
- glDisable(ptex->target);
- glActiveTexture(GL_TEXTURE0);
- }
-
- if (has_prog)
- glUseProgram(0);
-
- gl_check_err();
-
- return true;
-}
diff --git a/src/opengl.h b/src/opengl.h
deleted file mode 100644
index dcd8697..0000000
--- a/src/opengl.h
+++ /dev/null
@@ -1,246 +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.
- *
- */
-
-#pragma once
-
-#include "common.h"
-#include "compiler.h"
-#include "log.h"
-#include "region.h"
-#include "render.h"
-#include "win.h"
-
-#include <GL/gl.h>
-#include <GL/glx.h>
-#include <ctype.h>
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/render.h>
-#include <xcb/xcb.h>
-
-typedef struct {
- /// Fragment shader for blur.
- GLuint frag_shader;
- /// GLSL program for blur.
- GLuint prog;
- /// Location of uniform "offset_x" in blur GLSL program.
- GLint unifm_offset_x;
- /// Location of uniform "offset_y" in blur GLSL program.
- GLint unifm_offset_y;
- /// Location of uniform "factor_center" in blur GLSL program.
- GLint unifm_factor_center;
-} glx_blur_pass_t;
-
-typedef struct {
- /// Fragment shader for rounded corners.
- GLuint frag_shader;
- /// GLSL program for rounded corners.
- GLuint prog;
- /// Location of uniform "radius" in rounded-corners GLSL program.
- GLint unifm_radius;
- /// Location of uniform "texcoord" in rounded-corners GLSL program.
- GLint unifm_texcoord;
- /// Location of uniform "texsize" in rounded-corners GLSL program.
- GLint unifm_texsize;
- /// Location of uniform "borderw" in rounded-corners GLSL program.
- GLint unifm_borderw;
- /// Location of uniform "borderc" in rounded-corners GLSL program.
- GLint unifm_borderc;
- /// Location of uniform "resolution" in rounded-corners GLSL program.
- GLint unifm_resolution;
- /// Location of uniform "texture_scr" in rounded-corners GLSL program.
- GLint unifm_tex_scr;
-
-} glx_round_pass_t;
-
-/// Structure containing GLX-dependent data for a session.
-typedef struct glx_session {
- // === OpenGL related ===
- /// GLX context.
- GLXContext context;
- /// Whether we have GL_ARB_texture_non_power_of_two.
- bool has_texture_non_power_of_two;
- /// Current GLX Z value.
- int z;
- glx_blur_pass_t *blur_passes;
- glx_round_pass_t *round_passes;
-} glx_session_t;
-
-/// @brief Wrapper of a binded GLX texture.
-typedef struct _glx_texture {
- GLuint texture;
- GLXPixmap glpixmap;
- xcb_pixmap_t pixmap;
- GLenum target;
- int width;
- int height;
- bool y_inverted;
-} glx_texture_t;
-
-#define CGLX_SESSION_INIT \
- { .context = NULL }
-
-bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
- GLfloat factor, const region_t *reg_tgt);
-
-bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy,
- int width, int height, int z, double opacity, bool argb, bool neg,
- const region_t *reg_tgt, const glx_prog_main_t *pprogram);
-
-bool glx_init(session_t *ps, bool need_render);
-
-void glx_destroy(session_t *ps);
-
-void glx_on_root_change(session_t *ps);
-
-bool glx_init_blur(session_t *ps);
-
-bool glx_init_rounded_corners(session_t *ps);
-
-#ifdef CONFIG_OPENGL
-bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
- glx_prog_main_t *pprogram);
-#endif
-
-bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, int width,
- int height, bool repeat, const struct glx_fbconfig_info *);
-
-void glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
-
-bool glx_bind_texture(session_t *ps, glx_texture_t **pptex, int x, int y, int width, int height);
-
-void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
-
-/**
- * Check if a texture is binded, or is binded to the given pixmap.
- */
-static inline bool glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
- return ptex && ptex->glpixmap && ptex->texture && (!pixmap || pixmap == ptex->pixmap);
-}
-
-void glx_set_clip(session_t *ps, const region_t *reg);
-
-bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
- GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc);
-
-bool glx_round_corners_dst(session_t *ps, struct managed_win *w,
- const glx_texture_t *ptex, int dx, int dy, int width,
- int height, float z, float cr, const region_t *reg_tgt);
-
-GLuint glx_create_shader(GLenum shader_type, const char *shader_str);
-
-GLuint glx_create_program(const GLuint *const shaders, int nshaders);
-
-GLuint glx_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
-
-unsigned char *glx_take_screenshot(session_t *ps, int *out_length);
-
-/**
- * Check if there's a GLX context.
- */
-static inline bool glx_has_context(session_t *ps) {
- return ps->psglx && ps->psglx->context;
-}
-
-/**
- * Ensure we have a GLX context.
- */
-static inline bool ensure_glx_context(session_t *ps) {
- // Create GLX context
- if (!glx_has_context(ps))
- glx_init(ps, false);
-
- return ps->psglx->context;
-}
-
-/**
- * Free a GLX texture.
- */
-static inline void free_texture_r(session_t *ps attr_unused, GLuint *ptexture) {
- if (*ptexture) {
- assert(glx_has_context(ps));
- glDeleteTextures(1, ptexture);
- *ptexture = 0;
- }
-}
-
-/**
- * Free a GLX Framebuffer object.
- */
-static inline void free_glx_fbo(GLuint *pfbo) {
- if (*pfbo) {
- glDeleteFramebuffers(1, pfbo);
- *pfbo = 0;
- }
- assert(!*pfbo);
-}
-
-/**
- * Free data in glx_blur_cache_t on resize.
- */
-static inline void free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
- free_texture_r(ps, &pbc->textures[0]);
- free_texture_r(ps, &pbc->textures[1]);
- pbc->width = 0;
- pbc->height = 0;
-}
-
-/**
- * Free a glx_blur_cache_t
- */
-static inline void free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
- free_glx_fbo(&pbc->fbo);
- free_glx_bc_resize(ps, pbc);
-}
-
-/**
- * Free a glx_texture_t.
- */
-static inline void free_texture(session_t *ps, glx_texture_t **pptex) {
- glx_texture_t *ptex = *pptex;
-
- // Quit if there's nothing
- if (!ptex) {
- return;
- }
-
- glx_release_pixmap(ps, ptex);
-
- free_texture_r(ps, &ptex->texture);
-
- // Free structure itself
- free(ptex);
- *pptex = NULL;
-}
-
-/**
- * Free GLX part of paint_t.
- */
-static inline void free_paint_glx(session_t *ps, paint_t *ppaint) {
- free_texture(ps, &ppaint->ptex);
-#ifdef CONFIG_OPENGL
- free(ppaint->fbcfg);
-#endif
- ppaint->fbcfg = NULL;
-}
-
-/**
- * Free GLX part of win.
- */
-static inline void free_win_res_glx(session_t *ps, struct managed_win *w) {
- free_paint_glx(ps, &w->paint);
- free_paint_glx(ps, &w->shadow_paint);
-#ifdef CONFIG_OPENGL
- free_glx_bc(ps, &w->glx_blur_cache);
- free_texture(ps, &w->glx_texture_bg);
-#endif
-}
diff --git a/src/options.c b/src/options.c
deleted file mode 100644
index 6a7bb49..0000000
--- a/src/options.c
+++ /dev/null
@@ -1,1128 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-#include <getopt.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <xcb/render.h> // for xcb_render_fixed_t, XXX
-
-#include "backend/backend.h"
-#include "common.h"
-#include "config.h"
-#include "log.h"
-#include "options.h"
-#include "utils.h"
-#include "win.h"
-
-#pragma GCC diagnostic error "-Wunused-parameter"
-
-/**
- * Print usage text.
- */
-static void usage(const char *argv0, int ret) {
-#define WARNING_DISABLED " (DISABLED AT COMPILE TIME)"
- static const char *usage_text =
- "picom (" COMPTON_VERSION ")\n"
- "Please report bugs to https://github.com/yshui/picom\n\n"
- "usage: %s [options]\n"
- "Options:\n"
- "\n"
- "-r radius\n"
- " The blur radius for shadows. (default 12)\n"
- "\n"
- "-o opacity\n"
- " The translucency for shadows. (default .75)\n"
- "\n"
- "-l left-offset\n"
- " The left offset for shadows. (default -15)\n"
- "\n"
- "-t top-offset\n"
- " The top offset for shadows. (default -15)\n"
- "\n"
- "-I fade-in-step\n"
- " Opacity change between steps while fading in. (default 0.028)\n"
- "\n"
- "-O fade-out-step\n"
- " Opacity change between steps while fading out. (default 0.03)\n"
- "\n"
- "-D fade-delta-time\n"
- " The time between steps in a fade in milliseconds. (default 10)\n"
- "\n"
- "-m opacity\n"
- " The opacity for menus. (default 1.0)\n"
- "\n"
- "-c\n"
- " Enabled client-side shadows on windows.\n"
- "\n"
- "-C\n"
- " Avoid drawing shadows on dock/panel windows.\n"
- "\n"
- "-z\n"
- " Zero the part of the shadow's mask behind the window.\n"
- "\n"
- "-f\n"
- " Fade windows in/out when opening/closing and when opacity\n"
- " changes, unless --no-fading-openclose is used.\n"
- "\n"
- "-F\n"
- " Equals to -f. Deprecated.\n"
- "\n"
- "--animations\n"
- " Run animations for window geometry changes (movement and scaling).\n"
- "\n"
- "--animation-for-open-window\n"
- " Which animation to run when opening a window.\n"
- " Must be one of `none`, `fly-in`, `zoom`,\n"
- " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n"
- " (default: none).\n"
- "\n"
- "--animation-for-transient-window\n"
- " Which animation to run when opening a transient window.\n"
- " Must be one of `none`, `fly-in`, `zoom`,\n"
- " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n"
- " (default: none).\n"
- "\n"
- "--animation-for-unmap-window\n"
- " Which animation to run when hiding (e.g. minimize) a window.\n"
- " Must be one of `auto`, `none`, `fly-in`, `zoom`,\n"
- " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n"
- " `slide-in`, `slide-out`\n"
- " (default: auto).\n"
- "\n"
- "--animation-for-workspace-switch-in\n"
- " Which animation to run on switching workspace for windows\n"
- " comming into view.\n"
- " IMPORTANT: window manager must set _NET_CURRENT_DESKTOP\n"
- " before doing the hide/show of windows\n"
- " Must be one of `auto`, `none`, `fly-in`, `zoom`,\n"
- " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n"
- " `slide-in`, `slide-out`\n"
- " (default: auto).\n"
- "\n"
- "--animation-for-workspace-switch-out\n"
- " Which animation to run on switching workspace for windows\n"
- " going out of view.\n"
- " IMPORTANT: window manager must set _NET_CURRENT_DESKTOP\n"
- " before doing the hide/show of windows\n"
- " Must be one of `auto`, `none`, `fly-in`, `zoom`,\n"
- " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n"
- " `slide-in`, `slide-out`\n"
- " (default: auto).\n"
- "\n"
- "--animation-stiffness\n"
- " Stiffness (a.k.a. tension) parameter for animation (default: 200.0).\n"
- "\n"
- "--animation-dampening\n"
- " Dampening (a.k.a. friction) parameter for animation (default: 25.0).\n"
- "\n"
- "--animation-window-mass\n"
- " Mass parameter for animation (default: 1.0).\n"
- "\n"
- "--animation-delta\n"
- " The time between steps in animation, in milliseconds. (> 0, defaults to 10).\n"
- "\n"
- "--animation-force-steps\n"
- " Force animations to go step by step even if cpu usage is high \n"
- " (default: false)\n"
- "\n"
- "--animation-clamping\n"
- " Whether to clamp animations (default: true)\n"
- "\n"
- "-i opacity\n"
- " Opacity of inactive windows. (0.1 - 1.0)\n"
- "\n"
- "-e opacity\n"
- " Opacity of window titlebars and borders. (0.1 - 1.0)\n"
- "\n"
- "-G\n"
- " Don't draw shadows on DND windows\n"
- "\n"
- "-b\n"
- " Daemonize process.\n"
- "\n"
- "--show-all-xerrors\n"
- " Show all X errors (for debugging).\n"
- "\n"
- "--config path\n"
- " Look for configuration file at the path. Use /dev/null to avoid\n"
- " loading configuration file."
-#ifndef CONFIG_LIBCONFIG
- WARNING_DISABLED
-#endif
- "\n\n"
- "--write-pid-path path\n"
- " Write process ID to a file.\n"
- "\n"
- "--shadow-color color\n"
- " Color of shadow, as a hex RGB string (defaults to #000000)\n"
- "\n"
- "--shadow-red value\n"
- " Red color value of shadow (0.0 - 1.0, defaults to 0).\n"
- "\n"
- "--shadow-green value\n"
- " Green color value of shadow (0.0 - 1.0, defaults to 0).\n"
- "\n"
- "--shadow-blue value\n"
- " Blue color value of shadow (0.0 - 1.0, defaults to 0).\n"
- "\n"
- "--inactive-opacity-override\n"
- " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n"
- "\n"
- "--inactive-dim value\n"
- " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n"
- "\n"
- "--active-opacity opacity\n"
- " Default opacity for active windows. (0.0 - 1.0)\n"
- "\n"
- "--corner-radius value\n"
- " Sets the radius of rounded window corners. When > 0, the compositor\n"
- " will round the corners of windows. (defaults to 0).\n"
- "\n"
- "--rounded-corners-exclude condition\n"
- " Exclude conditions for rounded corners.\n"
- "\n"
- "--mark-wmwin-focused\n"
- " Try to detect WM windows and mark them as active.\n"
- "\n"
- "--shadow-exclude condition\n"
- " Exclude conditions for shadows.\n"
- "\n"
- "--fade-exclude condition\n"
- " Exclude conditions for fading.\n"
- "\n"
- "--mark-ovredir-focused\n"
- " Mark windows that have no WM frame as active.\n"
- "\n"
- "--no-fading-openclose\n"
- " Do not fade on window open/close.\n"
- "\n"
- "--no-fading-destroyed-argb\n"
- " Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n"
- " in Openbox, Fluxbox, etc.\n"
- "\n"
- "--shadow-ignore-shaped\n"
- " Do not paint shadows on shaped windows. (Deprecated, use\n"
- " --shadow-exclude \'bounding_shaped\' or\n"
- " --shadow-exclude \'bounding_shaped && !rounded_corners\' instead.)\n"
- "\n"
- "--detect-rounded-corners\n"
- " Try to detect windows with rounded corners and don't consider\n"
- " them shaped windows. Affects --shadow-ignore-shaped,\n"
- " --unredir-if-possible, and possibly others. You need to turn this\n"
- " on manually if you want to match against rounded_corners in\n"
- " conditions.\n"
- "\n"
- "--detect-client-opacity\n"
- " Detect _NET_WM_OPACITY on client windows, useful for window\n"
- " managers not passing _NET_WM_OPACITY of client windows to frame\n"
- " windows.\n"
- "\n"
- "--refresh-rate val\n"
- " Specify refresh rate of the screen. If not specified or 0, we\n"
- " will try detecting this with X RandR extension.\n"
- "\n"
- "--vsync\n"
- " Enable VSync\n"
- "\n"
- "--paint-on-overlay\n"
- " Painting on X Composite overlay window.\n"
- "\n"
- "--use-ewmh-active-win\n"
- " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n"
- " window is focused instead of using FocusIn/Out events.\n"
- "\n"
- "--unredir-if-possible\n"
- " Unredirect all windows if a full-screen opaque window is\n"
- " detected, to maximize performance for full-screen windows.\n"
- "\n"
- "--unredir-if-possible-delay ms\n"
- " Delay before unredirecting the window, in milliseconds.\n"
- " Defaults to 0.\n"
- "\n"
- "--unredir-if-possible-exclude condition\n"
- " Conditions of windows that shouldn't be considered full-screen\n"
- " for unredirecting screen.\n"
- "\n"
- "--focus-exclude condition\n"
- " Specify a list of conditions of windows that should always be\n"
- " considered focused.\n"
- "\n"
- "--inactive-dim-fixed\n"
- " Use fixed inactive dim value.\n"
- "\n"
- "--max-brightness\n"
- " Dims windows which average brightness is above this threshold.\n"
- " Requires --no-use-damage.\n"
- " Default: 1.0 or no dimming.\n"
- "\n"
- "--detect-transient\n"
- " Use WM_TRANSIENT_FOR to group windows, and consider windows in\n"
- " the same group focused at the same time.\n"
- "\n"
- "--detect-client-leader\n"
- " Use WM_CLIENT_LEADER to group windows, and consider windows in\n"
- " the same group focused at the same time. WM_TRANSIENT_FOR has\n"
- " higher priority if --detect-transient is enabled, too.\n"
- "\n"
- "--blur-method\n"
- " The algorithm used for background bluring. Available choices are:\n"
- " 'none' to disable, 'gaussian', 'box' or 'kernel' for custom\n"
- " convolution blur with --blur-kern.\n"
- " Note: 'gaussian' and 'box' require --experimental-backends.\n"
- "\n"
- "--blur-size\n"
- " The radius of the blur kernel for 'box' and 'gaussian' blur method.\n"
- "\n"
- "--blur-deviation\n"
- " The standard deviation for the 'gaussian' blur method.\n"
- "\n"
- "--blur-strength\n"
- " The strength level of the 'dual_kawase' blur method.\n"
- "\n"
- "--blur-background\n"
- " Blur background of semi-transparent / ARGB windows. Bad in\n"
- " performance. The switch name may change without prior\n"
- " notifications.\n"
- "\n"
- "--blur-background-frame\n"
- " Blur background of windows when the window frame is not opaque.\n"
- " Implies --blur-background. Bad in performance. The switch name\n"
- " may change.\n"
- "\n"
- "--blur-background-fixed\n"
- " Use fixed blur strength instead of adjusting according to window\n"
- " opacity.\n"
- "\n"
- "--blur-kern matrix\n"
- " Specify the blur convolution kernel, with the following format:\n"
- " WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n"
- " The element in the center must not be included, it will be forever\n"
- " 1.0 or changing based on opacity, depending on whether you have\n"
- " --blur-background-fixed.\n"
- " A 7x7 Gaussian blur kernel looks like:\n"
- " --blur-kern "
- "'7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0."
- "000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0."
- "029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0."
- "493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0."
- "243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0."
- "003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0."
- "000003'\n"
- " Up to 4 blur kernels may be specified, separated with semicolon, for\n"
- " multi-pass blur.\n"
- " May also be one the predefined kernels: 3x3box (default), 5x5box,\n"
- " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n"
- " 11x11gaussian.\n"
- "\n"
- "--blur-background-exclude condition\n"
- " Exclude conditions for background blur.\n"
- "\n"
- "--resize-damage integer\n"
- " Resize damaged region by a specific number of pixels. A positive\n"
- " value enlarges it while a negative one shrinks it. Useful for\n"
- " fixing the line corruption issues of blur. May or may not\n"
- " work with --glx-no-stencil. Shrinking doesn't function correctly.\n"
- "\n"
- "--invert-color-include condition\n"
- " Specify a list of conditions of windows that should be painted with\n"
- " inverted color. Resource-hogging, and is not well tested.\n"
- "\n"
- "--opacity-rule opacity:condition\n"
- " Specify a list of opacity rules, in the format \"PERCENT:PATTERN\",\n"
- " like \'50:name *= \"Firefox\"'. picom-trans is recommended over\n"
- " this. Note we do not distinguish 100%% and unset, and we don't make\n"
- " any guarantee about possible conflicts with other programs that set\n"
- " _NET_WM_WINDOW_OPACITY on frame or client windows.\n"
- "\n"
- "--shadow-exclude-reg geometry\n"
- " Specify a X geometry that describes the region in which shadow\n"
- " should not be painted in, such as a dock window region.\n"
- " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n"
- " on the bottom of the screen should not have shadows painted on.\n"
- "\n"
- "--clip-shadow-above condition\n"
- " Specify a list of conditions of windows to not paint a shadow over,\n"
- " such as a dock window.\n"
- "\n"
- "--xinerama-shadow-crop\n"
- " Crop shadow of a window fully on a particular Xinerama screen to the\n"
- " screen.\n"
- "\n"
- "--backend backend\n"
- " Choose backend. Possible choices are xrender, glx, and\n"
- " xr_glx_hybrid."
-#ifndef CONFIG_OPENGL
- " (GLX BACKENDS DISABLED AT COMPILE TIME)"
-#endif
- "\n\n"
- "--glx-no-stencil\n"
- " GLX backend: Avoid using stencil buffer. Might cause issues\n"
- " when rendering transparent content. My tests show a 15%% performance\n"
- " boost.\n"
- "\n"
- "--glx-no-rebind-pixmap\n"
- " GLX backend: Avoid rebinding pixmap on window damage. Probably\n"
- " could improve performance on rapid window content changes, but is\n"
- " known to break things on some drivers (LLVMpipe, xf86-video-intel,\n"
- " etc.).\n"
- "\n"
- "--no-use-damage\n"
- " Disable the use of damage information. This cause the whole screen to\n"
- " be redrawn everytime, instead of the part of the screen that has\n"
- " actually changed. Potentially degrades the performance, but might fix\n"
- " some artifacts.\n"
- "\n"
- "--xrender-sync-fence\n"
- " Additionally use X Sync fence to sync clients' draw calls. Needed\n"
- " on nvidia-drivers with GLX backend for some users.\n"
- "\n"
- "--force-win-blend\n"
- " Force all windows to be painted with blending. Useful if you have a\n"
- " --glx-fshader-win that could turn opaque pixels transparent.\n"
- "\n"
- "--dbus\n"
- " Enable remote control via D-Bus. See the D-BUS API section in the\n"
- " man page for more details."
-#ifndef CONFIG_DBUS
- WARNING_DISABLED
-#endif
- "\n\n"
- "--benchmark cycles\n"
- " Benchmark mode. Repeatedly paint until reaching the specified cycles.\n"
- "\n"
- "--benchmark-wid window-id\n"
- " Specify window ID to repaint in benchmark mode. If omitted or is 0,\n"
- " the whole screen is repainted.\n"
- "\n"
- "--monitor-repaint\n"
- " Highlight the updated area of the screen. For debugging the xrender\n"
- " backend only.\n"
- "\n"
- "--debug-mode\n"
- " Render into a separate window, and don't take over the screen. Useful\n"
- " when you want to attach a debugger to picom\n"
- "\n"
- "--no-ewmh-fullscreen\n"
- " Do not use EWMH to detect fullscreen windows. Reverts to checking\n"
- " if a window is fullscreen based only on its size and coordinates.\n"
- "\n"
- "--transparent-clipping\n"
- " Make transparent windows clip other windows like non-transparent windows\n"
- " do, instead of blending on top of them\n";
- FILE *f = (ret ? stderr : stdout);
- fprintf(f, usage_text, argv0);
-#undef WARNING_DISABLED
-}
-
-static const char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb";
-static const struct option longopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"config", required_argument, NULL, 256},
- {"shadow-radius", required_argument, NULL, 'r'},
- {"shadow-opacity", required_argument, NULL, 'o'},
- {"shadow-offset-x", required_argument, NULL, 'l'},
- {"shadow-offset-y", required_argument, NULL, 't'},
- {"fade-in-step", required_argument, NULL, 'I'},
- {"fade-out-step", required_argument, NULL, 'O'},
- {"fade-delta", required_argument, NULL, 'D'},
- {"menu-opacity", required_argument, NULL, 'm'},
- {"shadow", no_argument, NULL, 'c'},
- {"no-dock-shadow", no_argument, NULL, 'C'},
- {"clear-shadow", no_argument, NULL, 'z'},
- {"fading", no_argument, NULL, 'f'},
- {"inactive-opacity", required_argument, NULL, 'i'},
- {"frame-opacity", required_argument, NULL, 'e'},
- {"daemon", no_argument, NULL, 'b'},
- {"no-dnd-shadow", no_argument, NULL, 'G'},
- {"shadow-red", required_argument, NULL, 257},
- {"shadow-green", required_argument, NULL, 258},
- {"shadow-blue", required_argument, NULL, 259},
- {"inactive-opacity-override", no_argument, NULL, 260},
- {"inactive-dim", required_argument, NULL, 261},
- {"mark-wmwin-focused", no_argument, NULL, 262},
- {"shadow-exclude", required_argument, NULL, 263},
- {"mark-ovredir-focused", no_argument, NULL, 264},
- {"no-fading-openclose", no_argument, NULL, 265},
- {"shadow-ignore-shaped", no_argument, NULL, 266},
- {"detect-rounded-corners", no_argument, NULL, 267},
- {"detect-client-opacity", no_argument, NULL, 268},
- {"refresh-rate", required_argument, NULL, 269},
- {"vsync", optional_argument, NULL, 270},
- {"alpha-step", required_argument, NULL, 271},
- {"dbe", no_argument, NULL, 272},
- {"paint-on-overlay", no_argument, NULL, 273},
- {"sw-opti", no_argument, NULL, 274},
- {"vsync-aggressive", no_argument, NULL, 275},
- {"use-ewmh-active-win", no_argument, NULL, 276},
- {"respect-prop-shadow", no_argument, NULL, 277},
- {"unredir-if-possible", no_argument, NULL, 278},
- {"focus-exclude", required_argument, NULL, 279},
- {"inactive-dim-fixed", no_argument, NULL, 280},
- {"detect-transient", no_argument, NULL, 281},
- {"detect-client-leader", no_argument, NULL, 282},
- {"blur-background", no_argument, NULL, 283},
- {"blur-background-frame", no_argument, NULL, 284},
- {"blur-background-fixed", no_argument, NULL, 285},
- {"dbus", no_argument, NULL, 286},
- {"logpath", required_argument, NULL, 287},
- {"invert-color-include", required_argument, NULL, 288},
- {"opengl", no_argument, NULL, 289},
- {"backend", required_argument, NULL, 290},
- {"glx-no-stencil", no_argument, NULL, 291},
- {"benchmark", required_argument, NULL, 293},
- {"benchmark-wid", required_argument, NULL, 294},
- {"blur-background-exclude", required_argument, NULL, 296},
- {"active-opacity", required_argument, NULL, 297},
- {"glx-no-rebind-pixmap", no_argument, NULL, 298},
- {"glx-swap-method", required_argument, NULL, 299},
- {"fade-exclude", required_argument, NULL, 300},
- {"blur-kern", required_argument, NULL, 301},
- {"resize-damage", required_argument, NULL, 302},
- {"glx-use-gpushader4", no_argument, NULL, 303},
- {"opacity-rule", required_argument, NULL, 304},
- {"shadow-exclude-reg", required_argument, NULL, 305},
- {"paint-exclude", required_argument, NULL, 306},
- {"xinerama-shadow-crop", no_argument, NULL, 307},
- {"unredir-if-possible-exclude", required_argument, NULL, 308},
- {"unredir-if-possible-delay", required_argument, NULL, 309},
- {"write-pid-path", required_argument, NULL, 310},
- {"vsync-use-glfinish", no_argument, NULL, 311},
- {"xrender-sync", no_argument, NULL, 312},
- {"xrender-sync-fence", no_argument, NULL, 313},
- {"show-all-xerrors", no_argument, NULL, 314},
- {"no-fading-destroyed-argb", no_argument, NULL, 315},
- {"force-win-blend", no_argument, NULL, 316},
- {"glx-fshader-win", required_argument, NULL, 317},
- {"version", no_argument, NULL, 318},
- {"no-x-selection", no_argument, NULL, 319},
- {"no-name-pixmap", no_argument, NULL, 320},
- {"log-level", required_argument, NULL, 321},
- {"log-file", required_argument, NULL, 322},
- {"use-damage", no_argument, NULL, 323},
- {"no-use-damage", no_argument, NULL, 324},
- {"no-vsync", no_argument, NULL, 325},
- {"max-brightness", required_argument, NULL, 326},
- {"transparent-clipping", no_argument, NULL, 327},
- {"blur-method", required_argument, NULL, 328},
- {"blur-size", required_argument, NULL, 329},
- {"blur-deviation", required_argument, NULL, 330},
- {"blur-strength", required_argument, NULL, 331},
- {"shadow-color", required_argument, NULL, 332},
- {"corner-radius", required_argument, NULL, 333},
- {"rounded-corners-exclude", required_argument, NULL, 334},
- {"clip-shadow-above", required_argument, NULL, 335},
- {"experimental-backends", no_argument, NULL, 733},
- {"monitor-repaint", no_argument, NULL, 800},
- {"diagnostics", no_argument, NULL, 801},
- {"debug-mode", no_argument, NULL, 802},
- {"no-ewmh-fullscreen", no_argument, NULL, 803},
- {"animations", no_argument, NULL, 804},
- {"animation-stiffness", required_argument, NULL, 805},
- {"animation-dampening", required_argument, NULL, 806},
- {"animation-window-mass", required_argument, NULL, 807},
- {"animation-clamping", no_argument, NULL, 808},
- {"animation-for-open-window", required_argument, NULL, 809},
- {"animation-for-transient-window", required_argument, NULL, 810},
- // Must terminate with a NULL entry
- {NULL, 0, NULL, 0},
-};
-
-/// Get config options that are needed to parse the rest of the options
-/// Return true if we should quit
-bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
- bool *fork, int *exit_code) {
- int o = 0, longopt_idx = -1;
-
- // Pre-parse the commandline arguments to check for --config and invalid
- // switches
- // Must reset optind to 0 here in case we reread the commandline
- // arguments
- optind = 1;
- *config_file = NULL;
- *exit_code = 0;
- while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
- if (o == 256) {
- *config_file = strdup(optarg);
- } else if (o == 'h') {
- usage(argv[0], 0);
- return true;
-
- } else if (o == 'b') {
- *fork = true;
- } else if (o == 'd') {
- log_error("-d is removed, please use the DISPLAY "
- "environment variable");
- goto err;
- } else if (o == 314) {
- *all_xerrors = true;
- } else if (o == 318) {
- printf("%s\n", COMPTON_VERSION);
- return true;
- } else if (o == 'S') {
- log_error("-S is no longer available");
- goto err;
- } else if (o == 320) {
- log_error("--no-name-pixmap is no longer available");
- goto err;
- } else if (o == '?' || o == ':') {
- usage(argv[0], 1);
- goto err;
- }
- }
-
- // Check for abundant positional arguments
- if (optind < argc) {
- // log is not initialized here yet
- fprintf(stderr, "picom doesn't accept positional arguments.\n");
- goto err;
- }
-
- return false;
-err:
- *exit_code = 1;
- return true;
-}
-
-/**
- * Process arguments and configuration files.
- */
-bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
- bool fading_enable, bool conv_kern_hasneg, win_option_mask_t *winopt_mask) {
-
- int o = 0, longopt_idx = -1;
-
- char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
-
- // Enforce LC_NUMERIC locale "C" here to make sure dots are recognized
- // instead of commas in atof().
- setlocale(LC_NUMERIC, "C");
-
- // Parse commandline arguments. Range checking will be done later.
-
- bool failed = false;
- const char *deprecation_message attr_unused =
- "has been removed. If you encounter problems "
- "without this feature, please feel free to "
- "open a bug report.";
- optind = 1;
- while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
- switch (o) {
-#define P_CASEBOOL(idx, option) \
- case idx: \
- opt->option = true; \
- break
-#define P_CASELONG(idx, option) \
- case idx: \
- if (!parse_long(optarg, &opt->option)) { \
- exit(1); \
- } \
- break
-#define P_CASEINT(idx, option) \
- case idx: \
- if (!parse_int(optarg, &opt->option)) { \
- exit(1); \
- } \
- break
-
- // clang-format off
- // Short options
- case 318:
- case 'h':
- // These options should cause us to exit early,
- // so assert(false) here
- assert(false);
- break;
- case 'd':
- case 'b':
- case 'S':
- case 314:
- case 320:
- // These options are handled by get_early_config()
- break;
- P_CASEINT('D', fade_delta);
- case 'I': opt->fade_in_step = normalize_d(atof(optarg)); break;
- case 'O': opt->fade_out_step = normalize_d(atof(optarg)); break;
- case 'c': shadow_enable = true; break;
- case 'C':
- log_error("Option `--no-dock-shadow`/`-C` has been removed. Please"
- " use the wintype option `shadow` of `dock` instead.");
- failed = true; break;
- case 'G':
- log_error("Option `--no-dnd-shadow`/`-G` has been removed. Please "
- "use the wintype option `shadow` of `dnd` instead.");
- failed = true; break;
- case 'm':;
- double tmp;
- tmp = normalize_d(atof(optarg));
- winopt_mask[WINTYPE_DROPDOWN_MENU].opacity = true;
- winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
- opt->wintype_option[WINTYPE_POPUP_MENU].opacity = tmp;
- opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = tmp;
- break;
- case 'f':
- case 'F':
- fading_enable = true;
- break;
- P_CASEINT('r', shadow_radius);
- case 'o':
- opt->shadow_opacity = atof(optarg);
- break;
- P_CASEINT('l', shadow_offset_x);
- P_CASEINT('t', shadow_offset_y);
- case 'i':
- opt->inactive_opacity = normalize_d(atof(optarg));
- break;
- case 'e': opt->frame_opacity = atof(optarg); break;
- case 'z':
- log_warn("clear-shadow is removed, shadows are automatically "
- "cleared now. If you want to prevent shadow from been "
- "cleared under certain types of windows, you can use "
- "the \"full-shadow\" per window type option.");
- break;
- case 'n':
- case 'a':
- case 's':
- log_error("-n, -a, and -s have been removed.");
- failed = true; break;
- // Long options
- case 256:
- // --config
- break;
- case 332:;
- // --shadow-color
- struct color rgb;
- rgb = hex_to_rgb(optarg);
- opt->shadow_red = rgb.red;
- opt->shadow_green = rgb.green;
- opt->shadow_blue = rgb.blue;
- break;
- case 257:
- // --shadow-red
- opt->shadow_red = atof(optarg);
- break;
- case 258:
- // --shadow-green
- opt->shadow_green = atof(optarg);
- break;
- case 259:
- // --shadow-blue
- opt->shadow_blue = atof(optarg);
- break;
- P_CASEBOOL(260, inactive_opacity_override);
- case 261:
- // --inactive-dim
- opt->inactive_dim = atof(optarg);
- break;
- P_CASEBOOL(262, mark_wmwin_focused);
- case 263:
- // --shadow-exclude
- condlst_add(&opt->shadow_blacklist, optarg);
- break;
- P_CASEBOOL(264, mark_ovredir_focused);
- P_CASEBOOL(265, no_fading_openclose);
- P_CASEBOOL(266, shadow_ignore_shaped);
- P_CASEBOOL(267, detect_rounded_corners);
- P_CASEBOOL(268, detect_client_opacity);
- P_CASEINT(269, refresh_rate);
- case 270:
- if (optarg) {
- opt->vsync = parse_vsync(optarg);
- log_warn("--vsync doesn't take argument anymore. \"%s\" "
- "is interpreted as \"%s\" for compatibility, but "
- "this will stop working soon",
- optarg, opt->vsync ? "true" : "false");
- } else {
- opt->vsync = true;
- }
- break;
- case 271:
- // --alpha-step
- log_error("--alpha-step has been removed, we now tries to "
- "make use of all alpha values");
- failed = true; break;
- case 272:
- log_error("--dbe has been removed");
- failed = true; break;
- case 273:
- log_error("--paint-on-overlay has been removed, the feature is enabled "
- "whenever possible");
- failed = true; break;
- P_CASEBOOL(274, sw_opti);
- case 275:
- // --vsync-aggressive
- log_warn("--vsync-aggressive has been deprecated, please remove it"
- " from the command line options");
- break;
- P_CASEBOOL(276, use_ewmh_active_win);
- case 277:
- // --respect-prop-shadow
- log_warn("--respect-prop-shadow option has been deprecated, its "
- "functionality will always be enabled. Please remove it "
- "from the command line options");
- break;
- P_CASEBOOL(278, unredir_if_possible);
- case 279:
- // --focus-exclude
- condlst_add(&opt->focus_blacklist, optarg);
- break;
- P_CASEBOOL(280, inactive_dim_fixed);
- P_CASEBOOL(281, detect_transient);
- P_CASEBOOL(282, detect_client_leader);
- case 283:
- // --blur_background
- opt->blur_method = BLUR_METHOD_KERNEL;
- break;
- P_CASEBOOL(284, blur_background_frame);
- P_CASEBOOL(285, blur_background_fixed);
- P_CASEBOOL(286, dbus);
- case 287:
- log_warn("Please use --log-file instead of --logpath");
- // fallthrough
- case 322:
- // --logpath, --log-file
- free(opt->logpath);
- opt->logpath = strdup(optarg);
- break;
- case 288:
- // --invert-color-include
- condlst_add(&opt->invert_color_list, optarg);
- break;
- case 289:
- // --opengl
- opt->backend = BKEND_GLX;
- break;
- case 290:
- // --backend
- opt->backend = parse_backend(optarg);
- if (opt->backend >= NUM_BKEND)
- exit(1);
- break;
- P_CASEBOOL(291, glx_no_stencil);
- P_CASEINT(293, benchmark);
- case 294:
- // --benchmark-wid
- opt->benchmark_wid = (xcb_window_t)strtol(optarg, NULL, 0);
- break;
- case 296:
- // --blur-background-exclude
- condlst_add(&opt->blur_background_blacklist, optarg);
- break;
- case 297:
- // --active-opacity
- opt->active_opacity = normalize_d(atof(optarg));
- break;
- P_CASEBOOL(298, glx_no_rebind_pixmap);
- case 299: {
- // --glx-swap-method
- char *endptr;
- long tmpval = strtol(optarg, &endptr, 10);
- bool should_remove = true;
- if (*endptr || !(*optarg)) {
- // optarg is not a number, or an empty string
- tmpval = -1;
- }
- if (strcmp(optarg, "undefined") != 0 && tmpval != 0) {
- // If not undefined, we will use damage and buffer-age to
- // limit the rendering area.
- opt->use_damage = true;
- should_remove = false;
- }
- log_warn("--glx-swap-method has been deprecated, your setting "
- "\"%s\" should be %s.",
- optarg,
- !should_remove ? "replaced by `--use-damage`" :
- "removed");
- break;
- }
- case 300:
- // --fade-exclude
- condlst_add(&opt->fade_blacklist, optarg);
- break;
- case 301:
- // --blur-kern
- opt->blur_kerns = parse_blur_kern_lst(optarg, &conv_kern_hasneg,
- &opt->blur_kernel_count);
- if (!opt->blur_kerns) {
- exit(1);
- }
- break;
- P_CASEINT(302, resize_damage);
- case 303:
- // --glx-use-gpushader4
- log_warn("--glx-use-gpushader4 is deprecated since v6."
- " Please remove it from command line options.");
- break;
- case 304:
- // --opacity-rule
- if (!parse_rule_opacity(&opt->opacity_rules, optarg))
- exit(1);
- break;
- case 305:
- // --shadow-exclude-reg
- free(opt->shadow_exclude_reg_str);
- opt->shadow_exclude_reg_str = strdup(optarg);
- log_warn("--shadow-exclude-reg is deprecated. You are likely "
- "better off using --clip-shadow-above anyway");
- break;
- case 306:
- // --paint-exclude
- condlst_add(&opt->paint_blacklist, optarg);
- break;
- P_CASEBOOL(307, xinerama_shadow_crop);
- case 308:
- // --unredir-if-possible-exclude
- condlst_add(&opt->unredir_if_possible_blacklist, optarg);
- break;
- P_CASELONG(309, unredir_if_possible_delay);
- case 310:
- // --write-pid-path
- free(opt->write_pid_path);
- opt->write_pid_path = strdup(optarg);
- if (*opt->write_pid_path != '/') {
- log_warn("--write-pid-path is not an absolute path");
- }
- break;
- P_CASEBOOL(311, vsync_use_glfinish);
- case 312:
- // --xrender-sync
- log_error("Please use --xrender-sync-fence instead of --xrender-sync");
- failed = true; break;
- P_CASEBOOL(313, xrender_sync_fence);
- P_CASEBOOL(315, no_fading_destroyed_argb);
- P_CASEBOOL(316, force_win_blend);
- case 317:
- opt->glx_fshader_win_str = strdup(optarg);
- break;
- case 321: {
- enum log_level tmp_level = string_to_log_level(optarg);
- if (tmp_level == LOG_LEVEL_INVALID) {
- log_warn("Invalid log level, defaults to WARN");
- } else {
- log_set_level_tls(tmp_level);
- }
- break;
- }
- P_CASEBOOL(319, no_x_selection);
- P_CASEBOOL(323, use_damage);
- case 324:
- opt->use_damage = false;
- break;
- case 325:
- opt->vsync = false;
- break;
-
- case 326:
- opt->max_brightness = atof(optarg);
- break;
- P_CASEBOOL(327, transparent_clipping);
- case 328: {
- // --blur-method
- enum blur_method method = parse_blur_method(optarg);
- if (method >= BLUR_METHOD_INVALID) {
- log_warn("Invalid blur method %s, ignoring.", optarg);
- } else {
- opt->blur_method = method;
- }
- break;
- }
- case 329:
- // --blur-size
- opt->blur_radius = atoi(optarg);
- break;
- case 330:
- // --blur-deviation
- opt->blur_deviation = atof(optarg);
- break;
- case 331:
- // --blur-strength
- opt->blur_strength = atoi(optarg);
- break;
- case 333:
- // --cornor-radius
- opt->corner_radius = atoi(optarg);
- break;
- case 334:
- // --rounded-corners-exclude
- condlst_add(&opt->rounded_corners_blacklist, optarg);
- break;
- case 335:
- // --clip-shadow-above
- condlst_add(&opt->shadow_clip_list, optarg);
- break;
- P_CASEBOOL(733, experimental_backends);
- P_CASEBOOL(800, monitor_repaint);
- case 801: opt->print_diagnostics = true; break;
- P_CASEBOOL(802, debug_mode);
- P_CASEBOOL(803, no_ewmh_fullscreen);
- P_CASEBOOL(804, animations);
- case 805:
- // --animation-stiffness
- opt->animation_stiffness = atof(optarg);
- break;
- case 806:
- // --animation-dampening
- opt->animation_dampening = atof(optarg);
- break;
- case 807:
- // --animation-window-masss
- opt->animation_window_mass = atof(optarg);
- break;
- case 808:
- // --animation-clamping
- opt->animation_clamping = true;
- break;
- case 809: {
- // --animation-for-open-window
- enum open_window_animation animation = parse_open_window_animation(optarg);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_warn("Invalid open-window animation %s, ignoring.", optarg);
- } else {
- opt->animation_for_open_window = animation;
- }
- break;
- }
- case 810: {
- // --animation-for-transient-window
- enum open_window_animation animation = parse_open_window_animation(optarg);
- if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
- log_warn("Invalid transient-window animation %s, ignoring.", optarg);
- } else {
- opt->animation_for_transient_window = animation;
- }
- break;
- }
- default: usage(argv[0], 1); break;
-#undef P_CASEBOOL
- }
- // clang-format on
-
- if (failed) {
- // Parsing this option has failed, break the loop
- break;
- }
- }
-
- // Restore LC_NUMERIC
- setlocale(LC_NUMERIC, lc_numeric_old);
- free(lc_numeric_old);
-
- if (failed) {
- return false;
- }
-
- if (opt->monitor_repaint && opt->backend != BKEND_XRENDER &&
- !opt->experimental_backends) {
- log_warn("--monitor-repaint has no effect when backend is not xrender");
- }
-
- if (opt->experimental_backends && !backend_list[opt->backend]) {
- log_error("Backend \"%s\" is not available as part of the experimental "
- "backends.",
- BACKEND_STRS[opt->backend]);
- return false;
- }
-
- if (opt->debug_mode && !opt->experimental_backends) {
- log_error("Debug mode only works with the experimental backends.");
- return false;
- }
-
- if (opt->transparent_clipping && !opt->experimental_backends) {
- log_error("Transparent clipping only works with the experimental "
- "backends");
- return false;
- }
-
- // Range checking and option assignments
- opt->fade_delta = max2(opt->fade_delta, 1);
- opt->shadow_radius = max2(opt->shadow_radius, 0);
- opt->shadow_red = normalize_d(opt->shadow_red);
- opt->shadow_green = normalize_d(opt->shadow_green);
- opt->shadow_blue = normalize_d(opt->shadow_blue);
- opt->inactive_dim = normalize_d(opt->inactive_dim);
- opt->frame_opacity = normalize_d(opt->frame_opacity);
- opt->shadow_opacity = normalize_d(opt->shadow_opacity);
- opt->refresh_rate = normalize_i_range(opt->refresh_rate, 0, 300);
-
- opt->max_brightness = normalize_d(opt->max_brightness);
- if (opt->max_brightness < 1.0) {
- if (opt->use_damage) {
- log_warn("--max-brightness requires --no-use-damage. Falling "
- "back to 1.0");
- opt->max_brightness = 1.0;
- }
-
- if (!opt->experimental_backends || opt->backend != BKEND_GLX) {
- log_warn("--max-brightness requires the experimental glx "
- "backend. Falling back to 1.0");
- opt->max_brightness = 1.0;
- }
- }
-
- // --blur-background-frame implies --blur-background
- if (opt->blur_background_frame && opt->blur_method == BLUR_METHOD_NONE) {
- opt->blur_method = BLUR_METHOD_KERNEL;
- }
-
- // Apply default wintype options that are dependent on global options
- set_default_winopts(opt, winopt_mask, shadow_enable, fading_enable,
- opt->blur_method != BLUR_METHOD_NONE);
-
- // Other variables determined by options
-
- // Determine whether we track window grouping
- if (opt->detect_transient || opt->detect_client_leader) {
- opt->track_leader = true;
- }
-
- // Fill default blur kernel
- if (opt->blur_method == BLUR_METHOD_KERNEL &&
- (!opt->blur_kerns || !opt->blur_kerns[0])) {
- opt->blur_kerns = parse_blur_kern_lst("3x3box", &conv_kern_hasneg,
- &opt->blur_kernel_count);
- CHECK(opt->blur_kerns);
- CHECK(opt->blur_kernel_count);
- }
-
- // Sanitize parameters for dual-filter kawase blur
- if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) {
- if (opt->blur_strength <= 0 && opt->blur_radius > 500) {
- log_warn("Blur radius >500 not supported by dual_kawase method, "
- "capping to 500.");
- opt->blur_radius = 500;
- }
- if (opt->blur_strength > 20) {
- log_warn("Blur strength >20 not supported by dual_kawase method, "
- "capping to 20.");
- opt->blur_strength = 20;
- }
- if (!opt->experimental_backends) {
- log_warn("Dual-kawase blur is not implemented by the legacy "
- "backends, you must use the `experimental-backends` "
- "option.");
- }
- }
-
- if (opt->resize_damage < 0) {
- log_warn("Negative --resize-damage will not work correctly.");
- }
-
- if (opt->backend == BKEND_XRENDER && conv_kern_hasneg) {
- log_warn("A convolution kernel with negative values may not work "
- "properly under X Render backend.");
- }
-
- if (opt->corner_radius > 0 && opt->experimental_backends) {
- log_warn("Rounded corner is only supported on legacy backends, it "
- "will be disabled");
- opt->corner_radius = 0;
- }
-
- return true;
-}
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/options.h b/src/options.h
deleted file mode 100644
index 08aa15e..0000000
--- a/src/options.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-
-/// Parse command line options
-
-#include <stdbool.h>
-#include <xcb/render.h> // for xcb_render_fixed_t
-
-#include "compiler.h"
-#include "config.h"
-#include "types.h"
-#include "win.h" // for wintype_t
-
-typedef struct session session_t;
-
-/// Get config options that are needed to parse the rest of the options
-/// Return true if we should quit
-bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
- bool *fork, int *exit_code);
-
-/**
- * Process arguments and configuration files.
- *
- * Parameters:
- * shadow_enable = Carry overs from parse_config
- * fading_enable
- * conv_kern_hasneg
- * winopt_mask
- * Returns:
- * Whether configuration are processed successfully.
- */
-bool must_use get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
- bool fading_enable, bool conv_kern_hasneg,
- win_option_mask_t *winopt_mask);
-
-// vim: set noet sw=8 ts=8:
diff --git a/src/picom.c b/src/picom.c
deleted file mode 100644
index eb479f3..0000000
--- a/src/picom.c
+++ /dev/null
@@ -1,2790 +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 const long SWOPTI_TOLERANCE = 3000;
-
-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",
- 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);
- }
-
- 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;
- }
- 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.experimental_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];
-
- if (!initialize_blur(ps)) {
- log_fatal("Failed to prepare for background blur, aborting...");
- ps->backend_data->ops->deinit(ps->backend_data);
- ps->backend_data = NULL;
- quit(ps);
- return false;
- }
-
- // 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;
-}
-
-/// 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.experimental_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.experimental_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);
- }
-
- if (ps->o.sw_opti && !ps->o.refresh_rate) {
- update_refresh_rate(ps);
- if (!ps->refresh_rate) {
- log_warn("Refresh rate detection failed. swopti will be "
- "temporarily disabled");
- }
- }
- 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.
- // Same goes for `animation_running`.
- 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
- 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);
-
- 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 (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 (!ps->root_desktop_switch_direction) {
- 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->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->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);
- ps->backend_data->ops->set_image_property(
- ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
- ps->root_image, (int[]){ps->root_width, ps->root_height});
- }
- }
-
- // 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(COMPTON_VERSION), COMPTON_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;
-}
-
-/**
- * Update refresh rate info with X Randr extension.
- */
-void update_refresh_rate(session_t *ps) {
- xcb_randr_get_screen_info_reply_t *randr_info = xcb_randr_get_screen_info_reply(
- ps->c, xcb_randr_get_screen_info(ps->c, ps->root), NULL);
-
- if (!randr_info)
- return;
- ps->refresh_rate = randr_info->rate;
- free(randr_info);
-
- if (ps->refresh_rate)
- ps->refresh_intv = US_PER_SEC / ps->refresh_rate;
- else
- ps->refresh_intv = 0;
-}
-
-/**
- * Initialize refresh-rated based software optimization.
- *
- * @return true for success, false otherwise
- */
-static bool swopti_init(session_t *ps) {
- log_warn("--sw-opti is going to be deprecated. If you get real benefits from "
- "using "
- "this option, please open an issue to let us know.");
- // Prepare refresh rate
- // Check if user provides one
- ps->refresh_rate = ps->o.refresh_rate;
- if (ps->refresh_rate)
- ps->refresh_intv = US_PER_SEC / ps->refresh_rate;
-
- // Auto-detect refresh rate otherwise
- if (!ps->refresh_rate && ps->randr_exists) {
- update_refresh_rate(ps);
- }
-
- // Turn off vsync_sw if we can't get the refresh rate
- if (!ps->refresh_rate)
- return false;
-
- return true;
-}
-
-/**
- * Modify a struct timeval timeout value to render at a fixed pace.
- *
- * @param ps current session
- * @param[in,out] ptv pointer to the timeout
- */
-static double swopti_handle_timeout(session_t *ps) {
- if (!ps->refresh_intv)
- return 0;
-
- // Get the microsecond offset of the time when the we reach the timeout
- // I don't think a 32-bit long could overflow here.
- long offset = (get_time_timeval().tv_usec - ps->paint_tm_offset) % ps->refresh_intv;
- // XXX this formula dones't work if refresh rate is not a whole number
- if (offset < 0)
- offset += ps->refresh_intv;
-
- // If the target time is sufficiently close to a refresh time, don't add
- // an offset, to avoid certain blocking conditions.
- if (offset < SWOPTI_TOLERANCE || offset > ps->refresh_intv - SWOPTI_TOLERANCE)
- return 0;
-
- // Add an offset so we wait until the next refresh after timeout
- return (double)(ps->refresh_intv - offset) / 1e6;
-}
-
-/**
- * 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.experimental_backends);
- return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
- }
- if (ps->o.experimental_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.experimental_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.experimental_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) {
- // This function is not used if we are using --swopti
- 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
- if (!ps->o.benchmark) {
- ev_idle_stop(EV_A_ & ps->draw_idle);
- }
-}
-
-static void delayed_draw_timer_callback(EV_P_ ev_timer *w, int revents) {
- session_t *ps = session_ptr(w, delayed_draw_timer);
- draw_callback_impl(EV_A_ ps, revents);
-
- // We might have stopped the ev_idle in delayed_draw_callback,
- // so we restart it if we are in benchmark mode
- if (ps->o.benchmark)
- ev_idle_start(EV_A_ & ps->draw_idle);
-}
-
-static void delayed_draw_callback(EV_P_ ev_idle *w, int revents) {
- // This function is only used if we are using --swopti
- session_t *ps = session_ptr(w, draw_idle);
- assert(ps->redraw_needed);
- assert(!ev_is_active(&ps->delayed_draw_timer));
-
- double delay = swopti_handle_timeout(ps);
- if (delay < 1e-6) {
- if (!ps->o.benchmark) {
- ev_idle_stop(EV_A_ & ps->draw_idle);
- }
- return draw_callback_impl(EV_A_ ps, revents);
- }
-
- // This is a little bit hacky. When we get to this point in code, we need
- // to update the screen , but we will only be updating after a delay, So
- // we want to stop the ev_idle, so this callback doesn't get call repeatedly
- // during the delay, we also want queue_redraw to not restart the ev_idle.
- // So we stop ev_idle and leave ps->redraw_needed to be true. (effectively,
- // ps->redraw_needed means if redraw is needed or if draw is in progress).
- //
- // We do this anyway even if we are in benchmark mode. That means we will
- // have to restart draw_idle after the draw actually happened when we are in
- // benchmark mode.
- ev_idle_stop(EV_A_ & ps->draw_idle);
-
- ev_timer_set(&ps->delayed_draw_timer, delay, 0);
- ev_timer_start(EV_A_ & ps->delayed_draw_timer);
-}
-
-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);
-}
-
-/**
- * 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,
- .gaussian_map = NULL,
-
- .refresh_rate = 0,
- .refresh_intv = 0UL,
- .paint_tm_offset = 0L,
-
-#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.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.opacity_rules) &&
- c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
- c2_list_postprocess(ps, ps->o.focus_blacklist))) {
- log_error("Post-processing of conditionals failed, some of your rules "
- "might not work");
- }
-
- ps->gaussian_map = gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
- sum_kernel_preprocess(ps->gaussian_map);
-
- 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.sw_opti && !ps->o.refresh_rate) || ps->o.xinerama_shadow_crop) {
- if (!ps->randr_exists) {
- log_fatal("No XRandR extension. sw-opti, refresh-rate or "
- "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.experimental_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.experimental_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.experimental_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.experimental_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;
- }
- }
-
- // Initialize software optimization
- if (ps->o.sw_opti)
- ps->o.sw_opti = swopti_init(ps);
-
- // 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.sw_opti && !ps->o.refresh_rate) || 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);
- if (ps->o.sw_opti)
- ev_idle_init(&ps->draw_idle, delayed_draw_callback);
- else
- ev_idle_init(&ps->draw_idle, draw_callback);
-
- ev_init(&ps->fade_timer, fade_timer_callback);
- ev_init(&ps->animation_timer, animation_timer_callback);
- ev_init(&ps->delayed_draw_timer, delayed_draw_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
- free_wincondlst(&ps->o.shadow_blacklist);
- free_wincondlst(&ps->o.shadow_clip_list);
- free_wincondlst(&ps->o.fade_blacklist);
- free_wincondlst(&ps->o.focus_blacklist);
- free_wincondlst(&ps->o.invert_color_list);
- free_wincondlst(&ps->o.blur_background_blacklist);
- free_wincondlst(&ps->o.opacity_rules);
- free_wincondlst(&ps->o.paint_blacklist);
- free_wincondlst(&ps->o.unredir_if_possible_blacklist);
- free_wincondlst(&ps->o.rounded_corners_blacklist);
-
- // 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);
-
-#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.experimental_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);
- free_conv(ps->gaussian_map);
- 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) {
- if (ps->o.sw_opti)
- ps->paint_tm_offset = get_time_timeval().tv_usec;
-
- // 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;
-}
diff --git a/src/picom.h b/src/picom.h
deleted file mode 100644
index 25f7580..0000000
--- a/src/picom.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c)
-
-// Throw everything in here.
-// !!! DON'T !!!
-
-// === Includes ===
-
-#include <locale.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <xcb/xproto.h>
-
-#include <X11/Xutil.h>
-#include "backend/backend.h"
-#include "c2.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h" // XXX clean up
-#include "region.h"
-#include "render.h"
-#include "types.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-enum root_flags {
- ROOT_FLAGS_SCREEN_CHANGE = 1, // Received RandR screen change notify, we
- // use this to track refresh rate changes
- ROOT_FLAGS_CONFIGURED = 2 // Received configure notify on the root window
-};
-
-// == Functions ==
-// TODO(yshui) move static inline functions that are only used in picom.c, into picom.c
-
-void add_damage(session_t *ps, const region_t *damage);
-
-uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode);
-
-void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce);
-
-void update_refresh_rate(session_t *ps);
-
-void root_damaged(session_t *ps);
-
-void cxinerama_upd_scrs(session_t *ps);
-
-void queue_redraw(session_t *ps);
-
-void discard_ignore(session_t *ps, unsigned long sequence);
-
-void set_root_flags(session_t *ps, uint64_t flags);
-
-void quit(session_t *ps);
-
-xcb_window_t session_get_target_window(session_t *);
-
-uint8_t session_redirection_mode(session_t *ps);
-
-/**
- * Set a <code>switch_t</code> array of all unset wintypes to true.
- */
-static inline void wintype_arr_enable_unset(switch_t arr[]) {
- wintype_t i;
-
- for (i = 0; i < NUM_WINTYPES; ++i)
- if (UNSET == arr[i])
- arr[i] = ON;
-}
-
-/**
- * Check if a window ID exists in an array of window IDs.
- *
- * @param arr the array of window IDs
- * @param count amount of elements in the array
- * @param wid window ID to search for
- */
-static inline bool array_wid_exists(const xcb_window_t *arr, int count, xcb_window_t wid) {
- while (count--) {
- if (arr[count] == wid) {
- return true;
- }
- }
-
- return false;
-}
-
-/**
- * Destroy a condition list.
- */
-static inline void free_wincondlst(c2_lptr_t **pcondlst) {
- while ((*pcondlst = c2_free_lptr(*pcondlst)))
- continue;
-}
-
-#ifndef CONFIG_OPENGL
-static inline void free_paint_glx(session_t *ps attr_unused, paint_t *p attr_unused) {
-}
-static inline void
-free_win_res_glx(session_t *ps attr_unused, struct managed_win *w attr_unused) {
-}
-#endif
-
-/**
- * Dump an drawable's info.
- */
-static inline void dump_drawable(session_t *ps, xcb_drawable_t drawable) {
- auto r = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, drawable), NULL);
- if (!r) {
- log_trace("Drawable %#010x: Failed", drawable);
- return;
- }
- log_trace("Drawable %#010x: x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u",
- drawable, r->x, r->y, r->width, r->height, r->border_width, r->depth);
- free(r);
-}
diff --git a/src/picom.modulemap b/src/picom.modulemap
deleted file mode 100644
index 787c4ff..0000000
--- a/src/picom.modulemap
+++ /dev/null
@@ -1,214 +0,0 @@
-// modulemap
-
-module compiler {
- header "compiler.h"
-}
-module string_utils {
- header "string_utils.h"
-}
-module dbus {
- header "dbus.h"
-}
-module kernel {
- header "kernel.h"
-}
-module utils {
- // Has macros expands to calloc/malloc
- header "utils.h"
- export libc.stdlib
-}
-module region {
- header "region.h"
-}
-module picom {
- header "picom.h"
-}
-module types {
- header "types.h"
-}
-module c2 {
- header "c2.h"
-}
-module render {
- header "render.h"
-}
-module options {
- header "options.h"
-}
-module opengl {
- header "opengl.h"
-}
-module diagnostic {
- header "diagnostic.h"
-}
-module win_defs {
- header "win_defs.h"
-}
-module win {
- header "win.h"
- export win_defs
-}
-module log {
- header "log.h"
- export compiler
-}
-module x {
- header "x.h"
-}
-module vsync {
- header "vsync.h"
-}
-module common {
- header "common.h"
-}
-module config {
- header "config.h"
-}
-module xrescheck {
- header "xrescheck.h"
-}
-module cache {
- header "cache.h"
-}
-module backend {
- module gl {
- module gl_common {
- header "backend/gl/gl_common.h"
- }
- module glx {
- header "backend/gl/glx.h"
- export GL.glx
- }
- }
- module backend {
- header "backend/backend.h"
- }
- module backend_common {
- header "backend/backend_common.h"
- }
-}
-module xcb [system] {
- module xcb {
- header "/usr/include/xcb/xcb.h"
- export *
- }
- module randr {
- header "/usr/include/xcb/randr.h"
- export *
- }
- module render {
- header "/usr/include/xcb/render.h"
- export *
- }
- module sync {
- header "/usr/include/xcb/sync.h"
- export *
- }
- module composite {
- header "/usr/include/xcb/composite.h"
- export *
- }
- module xfixes {
- header "/usr/include/xcb/xfixes.h"
- export *
- }
- module damage {
- header "/usr/include/xcb/damage.h"
- export *
- }
- module xproto {
- header "/usr/include/xcb/xproto.h"
- export *
- }
- module present {
- header "/usr/include/xcb/present.h"
- }
- module util {
- module render {
- header "/usr/include/xcb/xcb_renderutil.h"
- export *
- }
- }
-}
-module X11 [system] {
- module Xlib {
- header "/usr/include/X11/Xlib.h"
- export *
- }
- module Xutil {
- header "/usr/include/X11/Xutil.h"
- export *
- }
-}
-module GL [system] {
- module glx {
- header "/usr/include/GL/glx.h"
- export *
- }
- module gl {
- header "/usr/include/GL/gl.h"
- export *
- }
-}
-module libc [system] {
- export *
- module assert {
- export *
- textual header "/usr/include/assert.h"
- }
- module string {
- export *
- header "/usr/include/string.h"
- }
- module ctype {
- export *
- header "/usr/include/ctype.h"
- }
- module errno {
- export *
- header "/usr/include/errno.h"
- }
- module fenv {
- export *
- header "/usr/include/fenv.h"
- }
- module inttypes {
- export *
- header "/usr/include/inttypes.h"
- }
- module math {
- export *
- header "/usr/include/math.h"
- }
- module setjmp {
- export *
- header "/usr/include/setjmp.h"
- }
- module stdio {
- export *
- header "/usr/include/stdio.h"
- }
-
- module stdlib [system] {
- export *
- header "/usr/include/stdlib.h"
- }
-}
-
-// glib specific header. In it's own module because it
-// doesn't exist on some systems with unpatched glib 2.26+
-module "xlocale.h" [system] {
- export *
- header "/usr/include/xlocale.h"
-}
-
-// System header that we have difficult with merging.
-module "sys_types.h" [system] {
- export *
- header "/usr/include/sys/types.h"
-}
-
-module "signal.h" [system] {
- export *
- header "/usr/include/signal.h"
-}
diff --git a/src/region.h b/src/region.h
deleted file mode 100644
index bda66e2..0000000
--- a/src/region.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-#pragma once
-#include <pixman.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <xcb/xcb.h>
-
-#include "log.h"
-#include "utils.h"
-
-typedef struct pixman_region32 pixman_region32_t;
-typedef struct pixman_box32 pixman_box32_t;
-typedef pixman_region32_t region_t;
-typedef pixman_box32_t rect_t;
-
-RC_TYPE(region_t, rc_region, pixman_region32_init, pixman_region32_fini, static inline)
-
-static inline void dump_region(const region_t *x) {
- if (log_get_level_tls() < LOG_LEVEL_TRACE) {
- return;
- }
- int nrects;
- const rect_t *rects = pixman_region32_rectangles((region_t *)x, &nrects);
- log_trace("nrects: %d", nrects);
- for (int i = 0; i < nrects; i++)
- log_trace("(%d, %d) - (%d, %d)", rects[i].x1, rects[i].y1, rects[i].x2,
- rects[i].y2);
-}
-
-/// Convert one xcb rectangle to our rectangle type
-static inline rect_t from_x_rect(const xcb_rectangle_t *rect) {
- return (rect_t){
- .x1 = rect->x,
- .y1 = rect->y,
- .x2 = rect->x + rect->width,
- .y2 = rect->y + rect->height,
- };
-}
-
-/// Convert an array of xcb rectangles to our rectangle type
-/// Returning an array that needs to be freed
-static inline rect_t *from_x_rects(int nrects, const xcb_rectangle_t *rects) {
- rect_t *ret = ccalloc(nrects, rect_t);
- for (int i = 0; i < nrects; i++) {
- ret[i] = from_x_rect(rects + i);
- }
- return ret;
-}
-
-/**
- * Resize a region.
- */
-static inline void _resize_region(const region_t *region, region_t *output, int dx,
- int dy) {
- if (!region || !output) {
- return;
- }
- if (!dx && !dy) {
- if (region != output) {
- pixman_region32_copy(output, (region_t *)region);
- }
- return;
- }
- // Loop through all rectangles
- int nrects;
- int nnewrects = 0;
- const rect_t *rects = pixman_region32_rectangles((region_t *)region, &nrects);
- auto newrects = ccalloc(nrects, rect_t);
- for (int i = 0; i < nrects; i++) {
- int x1 = rects[i].x1 - dx;
- int y1 = rects[i].y1 - dy;
- int x2 = rects[i].x2 + dx;
- int y2 = rects[i].y2 + dy;
- int wid = x2 - x1;
- int hei = y2 - y1;
- if (wid <= 0 || hei <= 0) {
- continue;
- }
- newrects[nnewrects] =
- (rect_t){.x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2};
- ++nnewrects;
- }
-
- pixman_region32_fini(output);
- pixman_region32_init_rects(output, newrects, nnewrects);
-
- free(newrects);
-}
-
-static inline region_t resize_region(const region_t *region, int dx, int dy) {
- region_t ret;
- pixman_region32_init(&ret);
- _resize_region(region, &ret, dx, dy);
- return ret;
-}
-
-static inline void resize_region_in_place(region_t *region, int dx, int dy) {
- return _resize_region(region, region, dx, dy);
-}
diff --git a/src/render.c b/src/render.c
deleted file mode 100644
index ac9b40e..0000000
--- a/src/render.c
+++ /dev/null
@@ -1,1500 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/composite.h>
-#include <xcb/render.h>
-#include <xcb/sync.h>
-#include <xcb/xcb_image.h>
-#include <xcb/xcb_renderutil.h>
-
-#include "common.h"
-#include "options.h"
-
-#ifdef CONFIG_OPENGL
-#include "backend/gl/glx.h"
-#include "opengl.h"
-
-#ifndef GLX_BACK_BUFFER_AGE_EXT
-#define GLX_BACK_BUFFER_AGE_EXT 0x20F4
-#endif
-
-#endif
-
-#include "compiler.h"
-#include "config.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "types.h"
-#include "utils.h"
-#include "vsync.h"
-#include "win.h"
-#include "x.h"
-
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "render.h"
-
-#define XRFILTER_CONVOLUTION "convolution"
-#define XRFILTER_GAUSSIAN "gaussian"
-#define XRFILTER_BINOMIAL "binomial"
-
-/**
- * Bind texture in paint_t if we are using GLX backend.
- */
-static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei,
- bool repeat, int depth, xcb_visualid_t visual, bool force) {
-#ifdef CONFIG_OPENGL
- // XXX This is a mess. But this will go away after the backend refactor.
- if (!ppaint->pixmap)
- return false;
-
- struct glx_fbconfig_info *fbcfg;
- if (!visual) {
- assert(depth == 32);
- if (!ps->argb_fbconfig) {
- ps->argb_fbconfig =
- glx_find_fbconfig(ps->dpy, ps->scr,
- (struct xvisual_info){.red_size = 8,
- .green_size = 8,
- .blue_size = 8,
- .alpha_size = 8,
- .visual_depth = 32});
- }
- if (!ps->argb_fbconfig) {
- log_error("Failed to find appropriate FBConfig for 32 bit depth");
- return false;
- }
- fbcfg = ps->argb_fbconfig;
- } else {
- auto m = x_get_visual_info(ps->c, visual);
- if (m.visual_depth < 0) {
- return false;
- }
-
- if (depth && depth != m.visual_depth) {
- log_error("Mismatching visual depth: %d != %d", depth, m.visual_depth);
- return false;
- }
-
- if (!ppaint->fbcfg) {
- ppaint->fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, m);
- }
- if (!ppaint->fbcfg) {
- log_error("Failed to find appropriate FBConfig for X pixmap");
- return false;
- }
- fbcfg = ppaint->fbcfg;
- }
-
- if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))
- return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
- repeat, fbcfg);
-#else
- (void)ps;
- (void)ppaint;
- (void)wid;
- (void)hei;
- (void)repeat;
- (void)depth;
- (void)visual;
- (void)force;
-#endif
- return true;
-}
-
-/**
- * Check if current backend uses XRender for rendering.
- */
-static inline bool bkend_use_xrender(session_t *ps) {
- return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
-}
-
-int maximum_buffer_age(session_t *ps) {
- if (bkend_use_glx(ps) && ps->o.use_damage) {
- return CGLX_MAX_BUFFER_AGE;
- }
- return 1;
-}
-
-static int get_buffer_age(session_t *ps) {
-#ifdef CONFIG_OPENGL
- if (bkend_use_glx(ps)) {
- if (!glxext.has_GLX_EXT_buffer_age && ps->o.use_damage) {
- log_warn("GLX_EXT_buffer_age not supported by your driver,"
- "`use-damage` has to be disabled");
- ps->o.use_damage = false;
- }
- if (ps->o.use_damage) {
- unsigned int val;
- glXQueryDrawable(ps->dpy, get_tgt_window(ps),
- GLX_BACK_BUFFER_AGE_EXT, &val);
- return (int)val ?: -1;
- }
- return -1;
- }
-#endif
- return ps->o.use_damage ? 1 : -1;
-}
-
-/**
- * Reset filter on a <code>Picture</code>.
- */
-static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
-#define FILTER "Nearest"
- xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL);
-#undef FILTER
-}
-
-/// Set the input/output clip region of the target buffer (not the actual target!)
-static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg) {
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID:
- x_set_picture_clip_region(ps->c, ps->tgt_buffer.pict, 0, 0, reg);
- break;
-#ifdef CONFIG_OPENGL
- case BKEND_GLX: glx_set_clip(ps, reg); break;
-#endif
- default: assert(false);
- }
-}
-
-/**
- * Destroy a <code>Picture</code>.
- */
-void free_picture(xcb_connection_t *c, xcb_render_picture_t *p) {
- if (*p) {
- xcb_render_free_picture(c, *p);
- *p = XCB_NONE;
- }
-}
-
-/**
- * Free paint_t.
- */
-void free_paint(session_t *ps, paint_t *ppaint) {
-#ifdef CONFIG_OPENGL
- free_paint_glx(ps, ppaint);
-#endif
- free_picture(ps->c, &ppaint->pict);
- if (ppaint->pixmap)
- xcb_free_pixmap(ps->c, ppaint->pixmap);
- ppaint->pixmap = XCB_NONE;
-}
-
-uint32_t
-make_circle(int cx, int cy, int radius, uint32_t max_ntraps, xcb_render_trapezoid_t traps[]) {
- uint32_t n = 0, k = 0;
- int y1, y2;
- double w;
- while (k < max_ntraps) {
- y1 = (int)(-radius * cos(M_PI * k / max_ntraps));
- traps[n].top = (cy + y1) * 65536;
- traps[n].left.p1.y = (cy + y1) * 65536;
- traps[n].right.p1.y = (cy + y1) * 65536;
- w = sqrt(radius * radius - y1 * y1) * 65536;
- traps[n].left.p1.x = (int)((cx * 65536) - w);
- traps[n].right.p1.x = (int)((cx * 65536) + w);
-
- do {
- k++;
- y2 = (int)(-radius * cos(M_PI * k / max_ntraps));
- } while (y1 == y2);
-
- traps[n].bottom = (cy + y2) * 65536;
- traps[n].left.p2.y = (cy + y2) * 65536;
- traps[n].right.p2.y = (cy + y2) * 65536;
- w = sqrt(radius * radius - y2 * y2) * 65536;
- traps[n].left.p2.x = (int)((cx * 65536) - w);
- traps[n].right.p2.x = (int)((cx * 65536) + w);
- n++;
- }
- return n;
-}
-
-uint32_t make_rectangle(int x, int y, int wid, int hei, xcb_render_trapezoid_t traps[]) {
- traps[0].top = y * 65536;
- traps[0].left.p1.y = y * 65536;
- traps[0].left.p1.x = x * 65536;
- traps[0].left.p2.y = (y + hei) * 65536;
- traps[0].left.p2.x = x * 65536;
- traps[0].bottom = (y + hei) * 65536;
- traps[0].right.p1.x = (x + wid) * 65536;
- traps[0].right.p1.y = y * 65536;
- traps[0].right.p2.x = (x + wid) * 65536;
- traps[0].right.p2.y = (y + hei) * 65536;
- return 1;
-}
-
-uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps,
- int cr, int wid, int hei) {
- uint32_t n = make_circle(cr, cr, cr, max_ntraps, traps);
- n += make_circle(wid - cr, cr, cr, max_ntraps, traps + n);
- n += make_circle(wid - cr, hei - cr, cr, max_ntraps, traps + n);
- n += make_circle(cr, hei - cr, cr, max_ntraps, traps + n);
- n += make_rectangle(0, cr, wid, hei - 2 * cr, traps + n);
- n += make_rectangle(cr, 0, wid - 2 * cr, cr, traps + n);
- n += make_rectangle(cr, hei - cr, wid - 2 * cr, cr, traps + n);
- return n;
-}
-
-void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int fullwid,
- int fullhei, double opacity, bool argb, bool neg, int cr,
- xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint,
- const glx_prog_main_t *pprogram, clip_t *clip) {
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID: {
- auto alpha_step = (int)(opacity * MAX_ALPHA);
- xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step];
- if (alpha_step != 0) {
- if (cr) {
- xcb_render_picture_t p_tmp = x_create_picture_with_standard(
- ps->c, ps->root, fullwid, fullhei,
- XCB_PICT_STANDARD_ARGB_32, 0, 0);
- xcb_render_color_t trans = {
- .red = 0, .blue = 0, .green = 0, .alpha = 0};
- const xcb_rectangle_t rect = {
- .x = 0,
- .y = 0,
- .width = to_u16_checked(fullwid),
- .height = to_u16_checked(fullhei)};
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
- p_tmp, trans, 1, &rect);
-
- uint32_t max_ntraps = to_u32_checked(cr);
- xcb_render_trapezoid_t traps[4 * max_ntraps + 3];
-
- uint32_t n = make_rounded_window_shape(
- traps, max_ntraps, cr, fullwid, fullhei);
-
- xcb_render_trapezoids(
- ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
- x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
- 0, 0, n, traps);
-
- xcb_render_composite(
- ps->c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp,
- ps->tgt_buffer.pict, to_i16_checked(x),
- to_i16_checked(y), to_i16_checked(x), to_i16_checked(y),
- to_i16_checked(dx), to_i16_checked(dy),
- to_u16_checked(wid), to_u16_checked(hei));
-
- xcb_render_free_picture(ps->c, p_tmp);
-
- } else {
- xcb_render_picture_t p_tmp = alpha_pict;
- if (clip) {
- p_tmp = x_create_picture_with_standard(
- ps->c, ps->root, wid, hei,
- XCB_PICT_STANDARD_ARGB_32, 0, 0);
-
- xcb_render_color_t black = {
- .red = 255, .blue = 255, .green = 255, .alpha = 255};
- const xcb_rectangle_t rect = {
- .x = 0,
- .y = 0,
- .width = to_u16_checked(wid),
- .height = to_u16_checked(hei)};
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
- p_tmp, black, 1, &rect);
- if (alpha_pict) {
- xcb_render_composite(
- ps->c, XCB_RENDER_PICT_OP_SRC,
- alpha_pict, XCB_NONE, p_tmp, 0, 0, 0,
- 0, 0, 0, to_u16_checked(wid),
- to_u16_checked(hei));
- }
- xcb_render_composite(
- ps->c, XCB_RENDER_PICT_OP_OUT_REVERSE,
- clip->pict, XCB_NONE, p_tmp, 0, 0, 0, 0,
- to_i16_checked(clip->x), to_i16_checked(clip->y),
- to_u16_checked(wid), to_u16_checked(hei));
- }
- uint8_t op = ((!argb && !alpha_pict && !clip)
- ? XCB_RENDER_PICT_OP_SRC
- : XCB_RENDER_PICT_OP_OVER);
-
- xcb_render_composite(
- ps->c, op, pict, p_tmp, ps->tgt_buffer.pict,
- to_i16_checked(x), to_i16_checked(y), 0, 0,
- to_i16_checked(dx), to_i16_checked(dy),
- to_u16_checked(wid), to_u16_checked(hei));
- if (clip) {
- xcb_render_free_picture(ps->c, p_tmp);
- }
- }
- }
- break;
- }
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb,
- neg, reg_paint, pprogram);
- ps->psglx->z += 1;
- break;
-#endif
- default: assert(0);
- }
-#ifndef CONFIG_OPENGL
- (void)neg;
- (void)ptex;
- (void)reg_paint;
- (void)pprogram;
-#endif
-}
-
-static inline void
-paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid, int hei,
- double opacity, const region_t *reg_paint, xcb_render_picture_t pict) {
- const int dx = (w ? w->g.x : 0) + x;
- const int dy = (w ? w->g.y : 0) + y;
- const int fullwid = w ? w->widthb : 0;
- const int fullhei = w ? w->heightb : 0;
- const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend));
- const bool neg = (w && w->invert_color);
-
- render(ps, x, y, dx, dy, wid, hei, fullwid, fullhei, opacity, argb, neg,
- w ? w->corner_radius : 0, pict,
- (w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint,
-#ifdef CONFIG_OPENGL
- w ? &ps->glx_prog_win : NULL
-#else
- NULL
-#endif
- ,
- XCB_NONE);
-}
-
-/**
- * Check whether a paint_t contains enough data.
- */
-static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
- // Don't check for presence of Pixmap here, because older X Composite doesn't
- // provide it
- if (!ppaint)
- return false;
-
- if (bkend_use_xrender(ps) && !ppaint->pict)
- return false;
-
-#ifdef CONFIG_OPENGL
- if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, XCB_NONE))
- return false;
-#endif
-
- return true;
-}
-
-/**
- * Paint a window itself and dim it if asked.
- */
-void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) {
- // Fetch Pixmap
- if (!w->paint.pixmap) {
- w->paint.pixmap = x_new_id(ps->c);
- set_ignore_cookie(ps, xcb_composite_name_window_pixmap(ps->c, w->base.id,
- w->paint.pixmap));
- }
-
- xcb_drawable_t draw = w->paint.pixmap;
- if (!draw) {
- log_error("Failed to get pixmap from window %#010x (%s), window won't be "
- "visible",
- w->base.id, w->name);
- return;
- }
-
- // XRender: Build picture
- if (bkend_use_xrender(ps) && !w->paint.pict) {
- xcb_render_create_picture_value_list_t pa = {
- .subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS,
- };
-
- w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(
- ps->c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
- }
-
- // GLX: Build texture
- // Let glx_bind_pixmap() determine pixmap size, because if the user
- // is resizing windows, the width and height we get may not be up-to-date,
- // causing the jittering issue M4he reported in #7.
- if (!paint_bind_tex(ps, &w->paint, 0, 0, false, 0, w->a.visual,
- (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) {
- log_error("Failed to bind texture for window %#010x.", w->base.id);
- }
- w->pixmap_damaged = false;
-
- if (!paint_isvalid(ps, &w->paint)) {
- log_error("Window %#010x is missing painting data.", w->base.id);
- return;
- }
-
- const int x = w->g.x;
- const int y = w->g.y;
- const uint16_t wid = to_u16_checked(w->widthb);
- const uint16_t hei = to_u16_checked(w->heightb);
-
- xcb_render_picture_t pict = w->paint.pict;
-
- // Invert window color, if required
- if (bkend_use_xrender(ps) && w->invert_color) {
- xcb_render_picture_t newpict = x_create_picture_with_pictfmt(
- ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL);
- if (newpict) {
- // Apply clipping region to save some CPU
- if (reg_paint) {
- region_t reg;
- pixman_region32_init(&reg);
- pixman_region32_copy(&reg, (region_t *)reg_paint);
- pixman_region32_translate(&reg, -x, -y);
- // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0,
- // 0, reg);
- pixman_region32_fini(&reg);
- }
-
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE,
- newpict, 0, 0, 0, 0, 0, 0, wid, hei);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
- ps->white_picture, XCB_NONE, newpict, 0, 0,
- 0, 0, 0, 0, wid, hei);
- // We use an extra PictOpInReverse operation to get correct
- // pixel alpha. There could be a better solution.
- if (win_has_alpha(w))
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
- pict, XCB_NONE, newpict, 0, 0, 0, 0,
- 0, 0, wid, hei);
- pict = newpict;
- }
- }
-
- if (w->frame_opacity == 1) {
- paint_region(ps, w, 0, 0, wid, hei, w->opacity, reg_paint, pict);
- } else {
- // Painting parameters
- const margin_t extents = win_calc_frame_extents(w);
- const auto t = extents.top;
- const auto l = extents.left;
- const auto b = extents.bottom;
- const auto r = extents.right;
-
-#define COMP_BDR(cx, cy, cwid, chei) \
- paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \
- reg_paint, pict)
-
- // Sanitize the margins, in case some broken WM makes
- // top_width + bottom_width > height in some cases.
-
- do {
- // top
- int body_height = hei;
- // ctop = checked top
- // Make sure top margin is smaller than height
- int ctop = min2(body_height, t);
- if (ctop > 0)
- COMP_BDR(0, 0, wid, ctop);
-
- body_height -= ctop;
- if (body_height <= 0)
- break;
-
- // bottom
- // cbot = checked bottom
- // Make sure bottom margin is not too large
- int cbot = min2(body_height, b);
- if (cbot > 0)
- COMP_BDR(0, hei - cbot, wid, cbot);
-
- // Height of window exclude the margin
- body_height -= cbot;
- if (body_height <= 0)
- break;
-
- // left
- int body_width = wid;
- int cleft = min2(body_width, l);
- if (cleft > 0)
- COMP_BDR(0, ctop, cleft, body_height);
-
- body_width -= cleft;
- if (body_width <= 0)
- break;
-
- // right
- int cright = min2(body_width, r);
- if (cright > 0)
- COMP_BDR(wid - cright, ctop, cright, body_height);
-
- body_width -= cright;
- if (body_width <= 0)
- break;
-
- // body
- paint_region(ps, w, cleft, ctop, body_width, body_height,
- w->opacity, reg_paint, pict);
- } while (0);
- }
-
-#undef COMP_BDR
-
- if (pict != w->paint.pict)
- free_picture(ps->c, &pict);
-
- // Dimming the window if needed
- if (w->dim) {
- double dim_opacity = ps->o.inactive_dim;
- if (!ps->o.inactive_dim_fixed)
- dim_opacity *= w->opacity;
-
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID: {
- auto cval = (uint16_t)(0xffff * dim_opacity);
-
- // Premultiply color
- xcb_render_color_t color = {
- .red = 0,
- .green = 0,
- .blue = 0,
- .alpha = cval,
- };
-
- xcb_rectangle_t rect = {
- .x = to_i16_checked(x),
- .y = to_i16_checked(y),
- .width = wid,
- .height = hei,
- };
-
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER,
- ps->tgt_buffer.pict, color, 1, &rect);
- } break;
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- glx_dim_dst(ps, x, y, wid, hei, (int)(ps->psglx->z - 0.7),
- (float)dim_opacity, reg_paint);
- break;
-#endif
- default: assert(false);
- }
- }
-}
-
-extern const char *background_props_str[];
-
-static bool get_root_tile(session_t *ps) {
- /*
- if (ps->o.paint_on_overlay) {
- return ps->root_picture;
- } */
-
- assert(!ps->root_tile_paint.pixmap);
- ps->root_tile_fill = false;
-
- bool fill = false;
- xcb_pixmap_t pixmap = x_get_root_back_pixmap(ps->c, ps->root, ps->atoms);
-
- // Make sure the pixmap we got is valid
- if (pixmap && !x_validate_pixmap(ps->c, pixmap))
- pixmap = XCB_NONE;
-
- // Create a pixmap if there isn't any
- if (!pixmap) {
- pixmap = x_create_pixmap(ps->c, (uint8_t)ps->depth, ps->root, 1, 1);
- if (pixmap == XCB_NONE) {
- log_error("Failed to create pixmaps for root tile.");
- return false;
- }
- fill = true;
- }
-
- // Create Picture
- xcb_render_create_picture_value_list_t pa = {
- .repeat = true,
- };
- ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
- ps->c, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa);
-
- // Fill pixmap if needed
- if (fill) {
- xcb_render_color_t col;
- xcb_rectangle_t rect;
-
- col.red = col.green = col.blue = 0x8080;
- col.alpha = 0xffff;
-
- rect.x = rect.y = 0;
- rect.width = rect.height = 1;
-
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
- ps->root_tile_paint.pict, col, 1, &rect);
- }
-
- ps->root_tile_fill = fill;
- ps->root_tile_paint.pixmap = pixmap;
-#ifdef CONFIG_OPENGL
- if (BKEND_GLX == ps->o.backend)
- return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, ps->vis, false);
-#endif
-
- return true;
-}
-
-/**
- * Paint root window content.
- */
-static void paint_root(session_t *ps, const region_t *reg_paint) {
- // If there is no root tile pixmap, try getting one.
- // If that fails, give up.
- if (!ps->root_tile_paint.pixmap && !get_root_tile(ps))
- return;
-
- paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
- ps->root_tile_paint.pict);
-}
-
-/**
- * Generate shadow <code>Picture</code> for a window.
- */
-static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) {
- const int width = w->widthb;
- const int height = w->heightb;
- // log_trace("(): building shadow for %s %d %d", w->name, width, height);
-
- xcb_image_t *shadow_image = NULL;
- xcb_pixmap_t shadow_pixmap = XCB_NONE, shadow_pixmap_argb = XCB_NONE;
- xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
- xcb_gcontext_t gc = XCB_NONE;
-
- shadow_image = make_shadow(ps->c, ps->gaussian_map, opacity, width, height);
- if (!shadow_image) {
- log_error("failed to make shadow");
- return XCB_NONE;
- }
-
- shadow_pixmap =
- x_create_pixmap(ps->c, 8, ps->root, shadow_image->width, shadow_image->height);
- shadow_pixmap_argb =
- x_create_pixmap(ps->c, 32, ps->root, shadow_image->width, shadow_image->height);
-
- if (!shadow_pixmap || !shadow_pixmap_argb) {
- log_error("failed to create shadow pixmaps");
- goto shadow_picture_err;
- }
-
- shadow_picture = x_create_picture_with_standard_and_pixmap(
- ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
- shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
- ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
- if (!shadow_picture || !shadow_picture_argb)
- goto shadow_picture_err;
-
- gc = x_new_id(ps->c);
- xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
-
- xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture,
- shadow_picture, shadow_picture_argb, 0, 0, 0, 0, 0, 0,
- shadow_image->width, shadow_image->height);
-
- assert(!w->shadow_paint.pixmap);
- w->shadow_paint.pixmap = shadow_pixmap_argb;
- assert(!w->shadow_paint.pict);
- w->shadow_paint.pict = shadow_picture_argb;
-
- xcb_free_gc(ps->c, gc);
- xcb_image_destroy(shadow_image);
- xcb_free_pixmap(ps->c, shadow_pixmap);
- xcb_render_free_picture(ps->c, shadow_picture);
-
- return true;
-
-shadow_picture_err:
- if (shadow_image)
- xcb_image_destroy(shadow_image);
- if (shadow_pixmap)
- xcb_free_pixmap(ps->c, shadow_pixmap);
- if (shadow_pixmap_argb)
- xcb_free_pixmap(ps->c, shadow_pixmap_argb);
- if (shadow_picture)
- xcb_render_free_picture(ps->c, shadow_picture);
- if (shadow_picture_argb)
- xcb_render_free_picture(ps->c, shadow_picture_argb);
- if (gc)
- xcb_free_gc(ps->c, gc);
-
- return false;
-}
-
-/**
- * Paint the shadow of a window.
- */
-static inline void
-win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
- // Bind shadow pixmap to GLX texture if needed
- paint_bind_tex(ps, &w->shadow_paint, 0, 0, false, 32, 0, false);
-
- if (!paint_isvalid(ps, &w->shadow_paint)) {
- log_error("Window %#010x is missing shadow data.", w->base.id);
- return;
- }
-
- xcb_render_picture_t td = XCB_NONE;
- bool should_clip =
- (w->corner_radius > 0) && (!ps->o.wintype_option[w->window_type].full_shadow);
- if (should_clip) {
- if (ps->o.backend == BKEND_XRENDER || ps->o.backend == BKEND_XR_GLX_HYBRID) {
- uint32_t max_ntraps = to_u32_checked(w->corner_radius);
- xcb_render_trapezoid_t traps[4 * max_ntraps + 3];
- uint32_t n = make_rounded_window_shape(
- traps, max_ntraps, w->corner_radius, w->widthb, w->heightb);
-
- td = x_create_picture_with_standard(
- ps->c, ps->root, w->widthb, w->heightb,
- XCB_PICT_STANDARD_ARGB_32, 0, 0);
- xcb_render_color_t trans = {
- .red = 0, .blue = 0, .green = 0, .alpha = 0};
- const xcb_rectangle_t rect = {.x = 0,
- .y = 0,
- .width = to_u16_checked(w->widthb),
- .height = to_u16_checked(w->heightb)};
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td,
- trans, 1, &rect);
-
- auto solid = solid_picture(ps->c, ps->root, false, 1, 0, 0, 0);
- xcb_render_trapezoids(
- ps->c, XCB_RENDER_PICT_OP_OVER, solid, td,
- x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0,
- 0, n, traps);
- xcb_render_free_picture(ps->c, solid);
- } else {
- // Not implemented
- }
- }
-
- clip_t clip = {
- .pict = td,
- .x = -(w->shadow_dx),
- .y = -(w->shadow_dy),
- };
- render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width,
- w->shadow_height, w->widthb, w->heightb, w->shadow_opacity, true, false, 0,
- w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL,
- should_clip ? &clip : NULL);
- if (td) {
- xcb_render_free_picture(ps->c, td);
- }
-}
-
-/**
- * @brief Blur an area on a buffer.
- *
- * @param ps current session
- * @param tgt_buffer a buffer as both source and destination
- * @param x x pos
- * @param y y pos
- * @param wid width
- * @param hei height
- * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at
- * least one kernel
- * @param reg_clip a clipping region to be applied on intermediate buffers
- *
- * @return true if successful, false otherwise
- */
-static bool
-xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y,
- uint16_t wid, uint16_t hei, struct x_convolution_kernel **blur_kerns,
- int nkernels, const region_t *reg_clip, xcb_render_picture_t rounded) {
- assert(blur_kerns);
- assert(blur_kerns[0]);
-
- // Directly copying from tgt_buffer to it does not work, so we create a
- // Picture in the middle.
- xcb_render_picture_t tmp_picture =
- x_create_picture_with_visual(ps->c, ps->root, wid, hei, ps->vis, 0, NULL);
-
- if (!tmp_picture) {
- log_error("Failed to build intermediate Picture.");
- return false;
- }
-
- if (reg_clip && tmp_picture)
- x_set_picture_clip_region(ps->c, tmp_picture, 0, 0, reg_clip);
-
- xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture;
- for (int i = 0; i < nkernels; ++i) {
- xcb_render_fixed_t *convolution_blur = blur_kerns[i]->kernel;
- // `x / 65536.0` converts from X fixed point to double
- int kwid = (int)((double)convolution_blur[0] / 65536.0),
- khei = (int)((double)convolution_blur[1] / 65536.0);
- bool rd_from_tgt = (tgt_buffer == src_pict);
-
- // Copy from source picture to destination. The filter must
- // be applied on source picture, to get the nearby pixels outside the
- // window.
- xcb_render_set_picture_filter(
- ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION,
- (uint32_t)(kwid * khei + 2), convolution_blur);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
- dst_pict, (rd_from_tgt ? x : 0),
- (rd_from_tgt ? y : 0), 0, 0, (rd_from_tgt ? 0 : x),
- (rd_from_tgt ? 0 : y), wid, hei);
- xrfilter_reset(ps, src_pict);
-
- {
- xcb_render_picture_t tmp = src_pict;
- src_pict = dst_pict;
- dst_pict = tmp;
- }
- }
-
- if (src_pict != tgt_buffer)
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded,
- tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
-
- free_picture(ps->c, &tmp_picture);
-
- return true;
-}
-
-/**
- * Blur the background of a window.
- */
-static inline void
-win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t tgt_buffer,
- const region_t *reg_paint) {
- const int16_t x = w->g.x;
- const int16_t y = w->g.y;
- const auto wid = to_u16_checked(w->widthb);
- const auto hei = to_u16_checked(w->heightb);
- const int cr = w ? w->corner_radius : 0;
-
- double factor_center = 1.0;
- // Adjust blur strength according to window opacity, to make it appear
- // better during fading
- if (!ps->o.blur_background_fixed) {
- double pct = 1.0 - w->opacity * (1.0 - 1.0 / 9.0);
- factor_center = pct * 8.0 / (1.1 - pct);
- }
-
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID: {
- // Normalize blur kernels
- for (int i = 0; i < ps->o.blur_kernel_count; i++) {
- // Note: `x * 65536` converts double `x` to a X fixed point
- // representation. `x / 65536` is the other way.
- auto kern_src = ps->o.blur_kerns[i];
- auto kern_dst = ps->blur_kerns_cache[i];
-
- assert(!kern_dst || (kern_src->w == kern_dst->kernel[0] / 65536 &&
- kern_src->h == kern_dst->kernel[1] / 65536));
-
- // Skip for fixed factor_center if the cache exists already
- if (ps->o.blur_background_fixed && kern_dst) {
- continue;
- }
-
- x_create_convolution_kernel(kern_src, factor_center,
- &ps->blur_kerns_cache[i]);
- }
-
- xcb_render_picture_t td = XCB_NONE;
- if (cr) {
- uint32_t max_ntraps = to_u32_checked(cr);
- xcb_render_trapezoid_t traps[4 * max_ntraps + 3];
- uint32_t n =
- make_rounded_window_shape(traps, max_ntraps, cr, wid, hei);
-
- td = x_create_picture_with_standard(
- ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
- xcb_render_color_t trans = {
- .red = 0, .blue = 0, .green = 0, .alpha = 0};
- const xcb_rectangle_t rect = {.x = 0,
- .y = 0,
- .width = to_u16_checked(wid),
- .height = to_u16_checked(hei)};
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td,
- trans, 1, &rect);
-
- auto solid = solid_picture(ps->c, ps->root, false, 1, 0, 0, 0);
-
- xcb_render_trapezoids(
- ps->c, XCB_RENDER_PICT_OP_OVER, solid, td,
- x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0,
- 0, n, traps);
- xcb_render_free_picture(ps->c, solid);
- }
-
- // Minimize the region we try to blur, if the window itself is not
- // opaque, only the frame is.
- region_t reg_blur = win_get_bounding_shape_global_by_val(w);
- if (w->mode == WMODE_FRAME_TRANS && !ps->o.force_win_blend) {
- region_t reg_noframe;
- pixman_region32_init(&reg_noframe);
- win_get_region_noframe_local(w, &reg_noframe);
- pixman_region32_translate(&reg_noframe, w->g.x, w->g.y);
- pixman_region32_subtract(&reg_blur, &reg_blur, &reg_noframe);
- pixman_region32_fini(&reg_noframe);
- }
-
- // Translate global coordinates to local ones
- pixman_region32_translate(&reg_blur, -x, -y);
- xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
- ps->o.blur_kernel_count, &reg_blur, td);
- if (td) {
- xcb_render_free_picture(ps->c, td);
- }
- pixman_region32_clear(&reg_blur);
- } break;
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- // TODO(compton) Handle frame opacity
- glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5f,
- (float)factor_center, reg_paint, &w->glx_blur_cache);
- break;
-#endif
- default: assert(0);
- }
-#ifndef CONFIG_OPENGL
- (void)reg_paint;
-#endif
-}
-
-/// paint all windows
-/// region = ??
-/// region_real = the damage region
-void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
- if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) {
- if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
- log_error("x_fence_sync failed, xrender-sync-fence will be "
- "disabled from now on.");
- xcb_sync_destroy_fence(ps->c, ps->sync_fence);
- ps->sync_fence = XCB_NONE;
- ps->o.xrender_sync_fence = false;
- ps->xsync_exists = false;
- }
- }
-
- region_t region;
- pixman_region32_init(&region);
- int buffer_age = get_buffer_age(ps);
- if (buffer_age == -1 || buffer_age > ps->ndamage || ignore_damage) {
- pixman_region32_copy(&region, &ps->screen_reg);
- } else {
- for (int i = 0; i < get_buffer_age(ps); i++) {
- auto curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage;
- pixman_region32_union(&region, &region, &ps->damage_ring[curr]);
- }
- }
-
- if (!pixman_region32_not_empty(&region)) {
- return;
- }
-
-#ifdef DEBUG_REPAINT
- static struct timespec last_paint = {0};
-#endif
-
- if (ps->o.resize_damage > 0) {
- resize_region_in_place(&region, ps->o.resize_damage, ps->o.resize_damage);
- }
-
- // Remove the damaged area out of screen
- pixman_region32_intersect(&region, &region, &ps->screen_reg);
-
- if (!paint_isvalid(ps, &ps->tgt_buffer)) {
- if (!ps->tgt_buffer.pixmap) {
- free_paint(ps, &ps->tgt_buffer);
- ps->tgt_buffer.pixmap =
- x_create_pixmap(ps->c, (uint8_t)ps->depth, ps->root,
- ps->root_width, ps->root_height);
- if (ps->tgt_buffer.pixmap == XCB_NONE) {
- log_fatal("Failed to allocate a screen-sized pixmap for"
- "painting");
- exit(1);
- }
- }
-
- if (BKEND_GLX != ps->o.backend)
- ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap(
- ps->c, ps->vis, ps->tgt_buffer.pixmap, 0, 0);
- }
-
- if (BKEND_XRENDER == ps->o.backend) {
- x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, &region);
- }
-
-#ifdef CONFIG_OPENGL
- if (bkend_use_glx(ps)) {
- ps->psglx->z = 0.0;
- }
-#endif
-
- region_t reg_tmp, *reg_paint;
- pixman_region32_init(&reg_tmp);
- if (t) {
- // Calculate the region upon which the root window is to be
- // painted based on the ignore region of the lowest window, if
- // available
- pixman_region32_subtract(&reg_tmp, &region, t->reg_ignore);
- reg_paint = &reg_tmp;
- } else {
- reg_paint = &region;
- }
-
- // Region on screen we don't want any shadows on
- region_t reg_shadow_clip;
- pixman_region32_init(&reg_shadow_clip);
-
- set_tgt_clip(ps, reg_paint);
- paint_root(ps, reg_paint);
-
- // Windows are sorted from bottom to top
- // Each window has a reg_ignore, which is the region obscured by all the
- // windows on top of that window. This is used to reduce the number of
- // pixels painted.
- //
- // Whether this is beneficial is to be determined XXX
- for (auto w = t; w; w = w->prev_trans) {
- region_t bshape_no_corners =
- win_get_bounding_shape_global_without_corners_by_val(w);
- region_t bshape_corners = win_get_bounding_shape_global_by_val(w);
- // Painting shadow
- if (w->shadow) {
- // Lazy shadow building
- if (!w->shadow_paint.pixmap)
- if (!win_build_shadow(ps, w, 1))
- log_error("build shadow failed");
-
- // Shadow doesn't need to be painted underneath the body
- // of the windows above. Because no one can see it
- pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
-
- // Mask out the region we don't want shadow on
- if (pixman_region32_not_empty(&ps->shadow_exclude_reg))
- pixman_region32_subtract(&reg_tmp, &reg_tmp,
- &ps->shadow_exclude_reg);
- if (pixman_region32_not_empty(&reg_shadow_clip)) {
- pixman_region32_subtract(&reg_tmp, &reg_tmp, &reg_shadow_clip);
- }
-
- // Might be worth while to crop the region to shadow
- // border
- assert(w->shadow_width >= 0 && w->shadow_height >= 0);
- pixman_region32_intersect_rect(
- &reg_tmp, &reg_tmp, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy,
- (uint)w->shadow_width, (uint)w->shadow_height);
-
- // Mask out the body of the window from the shadow if
- // needed Doing it here instead of in make_shadow() for
- // saving GPU power and handling shaped windows (XXX
- // unconfirmed)
- if (!ps->o.wintype_option[w->window_type].full_shadow)
- pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape_no_corners);
-
- if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
- w->xinerama_scr < ps->xinerama_nscrs)
- // There can be a window where number of screens
- // is updated, but the screen number attached to
- // the windows have not.
- //
- // Window screen number will be updated
- // eventually, so here we just check to make sure
- // we don't access out of bounds.
- pixman_region32_intersect(
- &reg_tmp, &reg_tmp,
- &ps->xinerama_scr_regs[w->xinerama_scr]);
-
- // Detect if the region is empty before painting
- if (pixman_region32_not_empty(&reg_tmp)) {
- set_tgt_clip(ps, &reg_tmp);
- win_paint_shadow(ps, w, &reg_tmp);
- }
- }
-
- // Only clip shadows above visible windows
- if (w->opacity * MAX_ALPHA >= 1) {
- if (w->clip_shadow_above) {
- // Add window bounds to shadow-clip region
- pixman_region32_union(&reg_shadow_clip, &reg_shadow_clip,
- &bshape_corners);
- } else {
- // Remove overlapping window bounds from shadow-clip
- // region
- pixman_region32_subtract(
- &reg_shadow_clip, &reg_shadow_clip, &bshape_corners);
- }
- }
-
- // Calculate the paint region based on the reg_ignore of the current
- // window and its bounding region.
- // Remember, reg_ignore is the union of all windows above the current
- // window.
- pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
- pixman_region32_intersect(&reg_tmp, &reg_tmp, &bshape_corners);
- pixman_region32_fini(&bshape_corners);
- pixman_region32_fini(&bshape_no_corners);
-
- if (pixman_region32_not_empty(&reg_tmp)) {
- set_tgt_clip(ps, &reg_tmp);
-
-#ifdef CONFIG_OPENGL
- // If rounded corners backup the region first
- if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) {
- const int16_t x = w->g.x;
- const int16_t y = w->g.y;
- const auto wid = to_u16_checked(w->widthb);
- const auto hei = to_u16_checked(w->heightb);
- glx_bind_texture(ps, &w->glx_texture_bg, x, y, wid, hei);
- }
-#endif
-
- // Blur window background
- if (w->blur_background &&
- (w->mode == WMODE_TRANS ||
- (ps->o.blur_background_frame && w->mode == WMODE_FRAME_TRANS) ||
- ps->o.force_win_blend)) {
- win_blur_background(ps, w, ps->tgt_buffer.pict, &reg_tmp);
- }
-
- // Painting the window
- paint_one(ps, w, &reg_tmp);
-
-#ifdef CONFIG_OPENGL
- // Rounded corners for XRender is implemented inside render()
- // Round window corners
- if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) {
- const auto wid = to_u16_checked(w->widthb);
- const auto hei = to_u16_checked(w->heightb);
- glx_round_corners_dst(ps, w, w->glx_texture_bg, w->g.x,
- w->g.y, wid, hei,
- (float)ps->psglx->z - 0.5F,
- (float)w->corner_radius, &reg_tmp);
- }
-#endif
- }
- }
-
- // Free up all temporary regions
- pixman_region32_fini(&reg_tmp);
- pixman_region32_fini(&reg_shadow_clip);
-
- // Move the head of the damage ring
- ps->damage = ps->damage - 1;
- if (ps->damage < ps->damage_ring) {
- ps->damage = ps->damage_ring + ps->ndamage - 1;
- }
- pixman_region32_clear(ps->damage);
-
- // Do this as early as possible
- set_tgt_clip(ps, &ps->screen_reg);
-
- if (ps->o.vsync) {
- // Make sure all previous requests are processed to achieve best
- // effect
- x_sync(ps->c);
-#ifdef CONFIG_OPENGL
- if (glx_has_context(ps)) {
- if (ps->o.vsync_use_glfinish)
- glFinish();
- else
- glFlush();
- glXWaitX();
- }
-#endif
- }
-
- if (ps->vsync_wait) {
- ps->vsync_wait(ps);
- }
-
- auto rwidth = to_u16_checked(ps->root_width);
- auto rheight = to_u16_checked(ps->root_height);
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- if (ps->o.monitor_repaint) {
- // Copy the screen content to a new picture, and highlight the
- // paint region. This is not very efficient, but since it's for
- // debug only, we don't really care
-
- // First we create a new picture, and copy content from the buffer
- // to it
- auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis);
- xcb_render_picture_t new_pict = x_create_picture_with_pictfmt(
- ps->c, ps->root, rwidth, rheight, pictfmt, 0, NULL);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
- ps->tgt_buffer.pict, XCB_NONE, new_pict, 0,
- 0, 0, 0, 0, 0, rwidth, rheight);
-
- // Next, we set the region of paint and highlight it
- x_set_picture_clip_region(ps->c, new_pict, 0, 0, &region);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
- ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0,
- 0, 0, 0, 0, 0, rwidth, rheight);
-
- // Finally, clear clip regions of new_pict and the screen, and put
- // the whole thing on screen
- x_set_picture_clip_region(ps->c, new_pict, 0, 0, &ps->screen_reg);
- x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, &ps->screen_reg);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, new_pict,
- XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0,
- rwidth, rheight);
- xcb_render_free_picture(ps->c, new_pict);
- } else
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
- ps->tgt_buffer.pict, XCB_NONE, ps->tgt_picture,
- 0, 0, 0, 0, 0, 0, rwidth, rheight);
- break;
-#ifdef CONFIG_OPENGL
- case BKEND_XR_GLX_HYBRID:
- x_sync(ps->c);
- if (ps->o.vsync_use_glfinish)
- glFinish();
- else
- glFlush();
- glXWaitX();
- assert(ps->tgt_buffer.pixmap);
- paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height,
- false, ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap);
- if (ps->o.vsync_use_glfinish)
- glFinish();
- else
- glFlush();
- glXWaitX();
- glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
- ps->root_height, 0, 1.0, false, false, &region, NULL);
- fallthrough();
- case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
-#endif
- default: assert(0);
- }
-
- x_sync(ps->c);
-
-#ifdef CONFIG_OPENGL
- if (glx_has_context(ps)) {
- glFlush();
- glXWaitX();
- }
-#endif
-
-#ifdef DEBUG_REPAINT
- struct timespec now = get_time_timespec();
- struct timespec diff = {0};
- timespec_subtract(&diff, &now, &last_paint);
- log_trace("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec);
- last_paint = now;
- log_trace("paint:");
- for (win *w = t; w; w = w->prev_trans)
- log_trace(" %#010lx", w->id);
-#endif
-
- // Free the paint region
- pixman_region32_fini(&region);
-}
-
-/**
- * Query needed X Render / OpenGL filters to check for their existence.
- */
-static bool xr_init_blur(session_t *ps) {
- // Query filters
- xcb_render_query_filters_reply_t *pf = xcb_render_query_filters_reply(
- ps->c, xcb_render_query_filters(ps->c, get_tgt_window(ps)), NULL);
- if (pf) {
- xcb_str_iterator_t iter = xcb_render_query_filters_filters_iterator(pf);
- for (; iter.rem; xcb_str_next(&iter)) {
- int len = xcb_str_name_length(iter.data);
- char *name = xcb_str_name(iter.data);
- // Check for the convolution filter
- if (strlen(XRFILTER_CONVOLUTION) == len &&
- !memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION)))
- ps->xrfilter_convolution_exists = true;
- }
- free(pf);
- }
-
- // Turn features off if any required filter is not present
- if (!ps->xrfilter_convolution_exists) {
- log_error("Xrender convolution filter "
- "unsupported by your X server. "
- "Background blur is not possible.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Pregenerate alpha pictures.
- */
-static bool init_alpha_picts(session_t *ps) {
- ps->alpha_picts = ccalloc(MAX_ALPHA + 1, xcb_render_picture_t);
-
- for (int i = 0; i <= MAX_ALPHA; ++i) {
- double o = (double)i / MAX_ALPHA;
- ps->alpha_picts[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
- if (ps->alpha_picts[i] == XCB_NONE)
- return false;
- }
- return true;
-}
-
-bool init_render(session_t *ps) {
- if (ps->o.backend == BKEND_DUMMY) {
- return false;
- }
-
- // Initialize OpenGL as early as possible
-#ifdef CONFIG_OPENGL
- glxext_init(ps->dpy, ps->scr);
-#endif
- if (bkend_use_glx(ps)) {
-#ifdef CONFIG_OPENGL
- if (!glx_init(ps, true))
- return false;
-#else
- log_error("GLX backend support not compiled in.");
- return false;
-#endif
- }
-
- // Initialize VSync
- if (!vsync_init(ps)) {
- return false;
- }
-
- // Initialize window GL shader
- if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) {
-#ifdef CONFIG_OPENGL
- if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win))
- return false;
-#else
- log_error("GLSL supported not compiled in, can't load "
- "shader.");
- return false;
-#endif
- }
-
- if (!init_alpha_picts(ps)) {
- log_error("Failed to init alpha pictures.");
- return false;
- }
-
- // Blur filter
- if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL) {
- log_warn("Old backends only support blur method \"kernel\". Your blur "
- "setting will not be applied");
- ps->o.blur_method = BLUR_METHOD_NONE;
- }
-
- if (ps->o.blur_method == BLUR_METHOD_KERNEL) {
- ps->blur_kerns_cache =
- ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *);
-
- bool ret = false;
- if (ps->o.backend == BKEND_GLX) {
-#ifdef CONFIG_OPENGL
- ret = glx_init_blur(ps);
-#else
- assert(false);
-#endif
- } else {
- ret = xr_init_blur(ps);
- }
- if (!ret) {
- return ret;
- }
- }
-
- ps->black_picture = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0);
- ps->white_picture = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1);
-
- if (ps->black_picture == XCB_NONE || ps->white_picture == XCB_NONE) {
- log_error("Failed to create solid xrender pictures.");
- return false;
- }
-
- // Generates another Picture for shadows if the color is modified by
- // user
- if (ps->o.shadow_red == 0 && ps->o.shadow_green == 0 && ps->o.shadow_blue == 0) {
- ps->cshadow_picture = ps->black_picture;
- } else {
- ps->cshadow_picture = solid_picture(ps->c, ps->root, true, 1, ps->o.shadow_red,
- ps->o.shadow_green, ps->o.shadow_blue);
- if (ps->cshadow_picture == XCB_NONE) {
- log_error("Failed to create shadow picture.");
- return false;
- }
- }
-
- // Initialize our rounded corners fragment shader
- if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) {
-#ifdef CONFIG_OPENGL
- if (!glx_init_rounded_corners(ps)) {
- log_error("Failed to init rounded corners shader.");
- return false;
- }
-#else
- assert(false);
-#endif
- }
- return true;
-}
-
-/**
- * Free root tile related things.
- */
-void free_root_tile(session_t *ps) {
- free_picture(ps->c, &ps->root_tile_paint.pict);
-#ifdef CONFIG_OPENGL
- free_texture(ps, &ps->root_tile_paint.ptex);
-#else
- assert(!ps->root_tile_paint.ptex);
-#endif
- if (ps->root_tile_fill) {
- xcb_free_pixmap(ps->c, ps->root_tile_paint.pixmap);
- ps->root_tile_paint.pixmap = XCB_NONE;
- }
- ps->root_tile_paint.pixmap = XCB_NONE;
- ps->root_tile_fill = false;
-}
-
-void deinit_render(session_t *ps) {
- // Free alpha_picts
- for (int i = 0; i <= MAX_ALPHA; ++i)
- free_picture(ps->c, &ps->alpha_picts[i]);
- free(ps->alpha_picts);
- ps->alpha_picts = NULL;
-
- // Free cshadow_picture and black_picture
- if (ps->cshadow_picture == ps->black_picture)
- ps->cshadow_picture = XCB_NONE;
- else
- free_picture(ps->c, &ps->cshadow_picture);
-
- free_picture(ps->c, &ps->black_picture);
- free_picture(ps->c, &ps->white_picture);
-
- // Free other X resources
- free_root_tile(ps);
-
-#ifdef CONFIG_OPENGL
- free(ps->root_tile_paint.fbcfg);
- if (bkend_use_glx(ps)) {
- glx_destroy(ps);
- }
-#endif
-
- if (ps->o.blur_method != BLUR_METHOD_NONE) {
- for (int i = 0; i < ps->o.blur_kernel_count; i++) {
- free(ps->blur_kerns_cache[i]);
- }
- free(ps->blur_kerns_cache);
- }
-}
-
-// vim: set ts=8 sw=8 noet :
diff --git a/src/render.h b/src/render.h
deleted file mode 100644
index 95a46db..0000000
--- a/src/render.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-
-#include <stdbool.h>
-#include <xcb/render.h>
-#include <xcb/xcb.h>
-#ifdef CONFIG_OPENGL
-#include "backend/gl/glx.h"
-#endif
-#include "region.h"
-
-typedef struct _glx_texture glx_texture_t;
-typedef struct glx_prog_main glx_prog_main_t;
-typedef struct session session_t;
-
-struct managed_win;
-
-typedef struct paint {
- xcb_pixmap_t pixmap;
- xcb_render_picture_t pict;
- glx_texture_t *ptex;
-#ifdef CONFIG_OPENGL
- struct glx_fbconfig_info *fbcfg;
-#endif
-} paint_t;
-
-typedef struct clip {
- xcb_render_picture_t pict;
- int x;
- int y;
-} clip_t;
-
-void render(session_t *ps, int x, int y, int dx, int dy, int w, int h, int fullw,
- int fullh, double opacity, bool argb, bool neg, int cr,
- xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint,
- const glx_prog_main_t *pprogram, clip_t *clip);
-void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint);
-
-void paint_all(session_t *ps, struct managed_win *const t, bool ignore_damage);
-
-void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);
-
-void free_paint(session_t *ps, paint_t *ppaint);
-void free_root_tile(session_t *ps);
-
-bool init_render(session_t *ps);
-void deinit_render(session_t *ps);
-
-int maximum_buffer_age(session_t *);
diff --git a/src/string_utils.c b/src/string_utils.c
deleted file mode 100644
index 65af0f2..0000000
--- a/src/string_utils.c
+++ /dev/null
@@ -1,129 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-#include <string.h>
-
-#include <test.h>
-
-#include "compiler.h"
-#include "string_utils.h"
-#include "utils.h"
-
-#pragma GCC diagnostic push
-
-// gcc warns about legitimate strncpy in mstrjoin and mstrextend
-// strncpy(str, src1, len1) intentional truncates the null byte from src1.
-// strncpy(str+len1, src2, len2) uses bound depends on the source argument,
-// but str is allocated with len1+len2+1, so this strncpy can't overflow
-#pragma GCC diagnostic ignored "-Wpragmas"
-#pragma GCC diagnostic ignored "-Wstringop-truncation"
-#pragma GCC diagnostic ignored "-Wstringop-overflow"
-
-/**
- * Allocate the space and join two strings.
- */
-char *mstrjoin(const char *src1, const char *src2) {
- auto len1 = strlen(src1);
- auto len2 = strlen(src2);
- auto len = len1 + len2 + 1;
- auto str = ccalloc(len, char);
-
- strncpy(str, src1, len1);
- strncpy(str + len1, src2, len2);
- str[len - 1] = '\0';
-
- return str;
-}
-
-TEST_CASE(mstrjoin) {
- char *str = mstrjoin("asdf", "qwer");
- TEST_STREQUAL(str, "asdfqwer");
- free(str);
-
- str = mstrjoin("", "qwer");
- TEST_STREQUAL(str, "qwer");
- free(str);
-
- str = mstrjoin("asdf", "");
- TEST_STREQUAL(str, "asdf");
- free(str);
-}
-
-/**
- * Concatenate a string on heap with another string.
- */
-void mstrextend(char **psrc1, const char *src2) {
- if (!*psrc1) {
- *psrc1 = strdup(src2);
- return;
- }
-
- auto len1 = strlen(*psrc1);
- auto len2 = strlen(src2);
- auto len = len1 + len2 + 1;
- *psrc1 = crealloc(*psrc1, len);
-
- strncpy(*psrc1 + len1, src2, len2);
- (*psrc1)[len - 1] = '\0';
-}
-
-TEST_CASE(mstrextend) {
- char *str1 = NULL;
- mstrextend(&str1, "asdf");
- TEST_STREQUAL(str1, "asdf");
-
- mstrextend(&str1, "asd");
- TEST_STREQUAL(str1, "asdfasd");
-
- mstrextend(&str1, "");
- TEST_STREQUAL(str1, "asdfasd");
- free(str1);
-}
-
-#pragma GCC diagnostic pop
-
-/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
-double strtod_simple(const char *src, const char **end) {
- double neg = 1;
- if (*src == '-') {
- neg = -1;
- src++;
- } else if (*src == '+') {
- src++;
- }
-
- double ret = 0;
- while (*src >= '0' && *src <= '9') {
- ret = ret * 10 + (*src - '0');
- src++;
- }
-
- if (*src == '.') {
- double frac = 0, mult = 0.1;
- src++;
- while (*src >= '0' && *src <= '9') {
- frac += mult * (*src - '0');
- mult *= 0.1;
- src++;
- }
- ret += frac;
- }
-
- *end = src;
- return ret * neg;
-}
-
-TEST_CASE(strtod_simple) {
- const char *end;
- double result = strtod_simple("1.0", &end);
- TEST_EQUAL(result, 1);
- TEST_EQUAL(*end, '\0');
-
- result = strtod_simple("-1.0", &end);
- TEST_EQUAL(result, -1);
- TEST_EQUAL(*end, '\0');
-
- result = strtod_simple("+.5", &end);
- TEST_EQUAL(result, 0.5);
- TEST_EQUAL(*end, '\0');
-}
diff --git a/src/string_utils.h b/src/string_utils.h
deleted file mode 100644
index 38febde..0000000
--- a/src/string_utils.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#pragma once
-#include <ctype.h>
-#include <stddef.h>
-
-#include "compiler.h"
-
-#define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
-
-char *mstrjoin(const char *src1, const char *src2);
-char *mstrjoin3(const char *src1, const char *src2, const char *src3);
-void mstrextend(char **psrc1, const char *src2);
-
-/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
-double strtod_simple(const char *, const char **);
-
-static inline int uitostr(unsigned int n, char *buf) {
- int ret = 0;
- unsigned int tmp = n;
- while (tmp > 0) {
- tmp /= 10;
- ret++;
- }
-
- if (ret == 0)
- ret = 1;
-
- int pos = ret;
- while (pos--) {
- buf[pos] = (char)(n % 10 + '0');
- n /= 10;
- }
- return ret;
-}
-
-static inline const char *skip_space_const(const char *src) {
- if (!src)
- return NULL;
- while (*src && isspace((unsigned char)*src))
- src++;
- return src;
-}
-
-static inline char *skip_space_mut(char *src) {
- if (!src)
- return NULL;
- while (*src && isspace((unsigned char)*src))
- src++;
- return src;
-}
-
-#define skip_space(x) \
- _Generic((x), char * : skip_space_mut, const char * : skip_space_const)(x)
diff --git a/src/types.h b/src/types.h
deleted file mode 100644
index c8d747b..0000000
--- a/src/types.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-
-#pragma once
-
-/// Some common types
-
-#include <stdint.h>
-
-/// Enumeration type to represent switches.
-typedef enum {
- OFF = 0, // false
- ON, // true
- UNSET
-} switch_t;
-
-/// A structure representing margins around a rectangle.
-typedef struct {
- int top;
- int left;
- int bottom;
- int right;
-} margin_t;
-
-struct color {
- double red, green, blue, alpha;
-};
-
-typedef uint32_t opacity_t;
-
-#define MARGIN_INIT \
- { 0, 0, 0, 0 }
diff --git a/src/uthash_extra.h b/src/uthash_extra.h
deleted file mode 100644
index cbc1056..0000000
--- a/src/uthash_extra.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-#include <uthash.h>
-
-#define HASH_ITER2(head, el) \
- for (__typeof__(head) el = (head), __tmp = el != NULL ? el->hh.next : NULL; \
- el != NULL; el = __tmp, __tmp = el != NULL ? el->hh.next : NULL)
diff --git a/src/utils.c b/src/utils.c
deleted file mode 100644
index 8a27f39..0000000
--- a/src/utils.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <sys/uio.h>
-
-#include "compiler.h"
-#include "string_utils.h"
-#include "utils.h"
-
-/// Report allocation failure without allocating memory
-void report_allocation_failure(const char *func, const char *file, unsigned int line) {
- // Since memory allocation failed, we try to print this error message without any
- // memory allocation. Since logging framework allocates memory (and might even
- // have not been initialized yet), so we can't use it.
- char buf[11];
- int llen = uitostr(line, buf);
- const char msg1[] = " has failed to allocate memory, ";
- const char msg2[] = ". Aborting...\n";
- const struct iovec v[] = {
- {.iov_base = (void *)func, .iov_len = strlen(func)},
- {.iov_base = "()", .iov_len = 2},
- {.iov_base = (void *)msg1, .iov_len = sizeof(msg1) - 1},
- {.iov_base = "at ", .iov_len = 3},
- {.iov_base = (void *)file, .iov_len = strlen(file)},
- {.iov_base = ":", .iov_len = 1},
- {.iov_base = buf, .iov_len = (size_t)llen},
- {.iov_base = (void *)msg2, .iov_len = sizeof(msg2) - 1},
- };
-
- writev(STDERR_FILENO, v, ARR_SIZE(v));
- abort();
-
- unreachable;
-}
-
-///
-/// Calculates next closest power of two of 32bit integer n
-/// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
-///
-int next_power_of_two(int n)
-{
- n--;
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- n++;
- return n;
-}
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/utils.h b/src/utils.h
deleted file mode 100644
index 6bb8643..0000000
--- a/src/utils.h
+++ /dev/null
@@ -1,294 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-#pragma once
-#include <assert.h>
-#include <ctype.h>
-#include <limits.h>
-#include <math.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <test.h>
-
-#include "compiler.h"
-#include "types.h"
-
-#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
-
-#ifdef __FAST_MATH__
-#warning Use of -ffast-math can cause rendering error or artifacts, \
- therefore it is not recommended.
-#endif
-
-#ifdef __clang__
-__attribute__((optnone))
-#else
-__attribute__((optimize("-fno-fast-math")))
-#endif
-static inline bool
-safe_isnan(double a) {
- return __builtin_isnan(a);
-}
-
-/// Same as assert(false), but make sure we abort _even in release builds_.
-/// Silence compiler warning caused by release builds making some code paths reachable.
-#define BUG() \
- do { \
- assert(false); \
- abort(); \
- } while (0)
-#define CHECK_EXPR(...) ((void)0)
-/// Same as assert, but evaluates the expression even in release builds
-#define CHECK(expr) \
- do { \
- auto _ = (expr); \
- /* make sure the original expression appears in the assertion message */ \
- assert((CHECK_EXPR(expr), _)); \
- (void)_; \
- } while (0)
-
-/// Asserts that var is within [lower, upper]. Silence compiler warning about expressions
-/// being always true or false.
-#define ASSERT_IN_RANGE(var, lower, upper) \
- do { \
- auto __tmp attr_unused = (var); \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
- assert(__tmp >= lower); \
- assert(__tmp <= upper); \
- _Pragma("GCC diagnostic pop"); \
- } while (0)
-
-/// Asserts that var >= lower. Silence compiler warning about expressions
-/// being always true or false.
-#define ASSERT_GEQ(var, lower) \
- do { \
- auto __tmp attr_unused = (var); \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
- assert(__tmp >= lower); \
- _Pragma("GCC diagnostic pop"); \
- } while (0)
-
-// Some macros for checked cast
-// Note these macros are not complete, as in, they won't work for every integer types. But
-// they are good enough for our use cases.
-
-#define to_int_checked(val) \
- ({ \
- int64_t tmp = (val); \
- ASSERT_IN_RANGE(tmp, INT_MIN, INT_MAX); \
- (int)tmp; \
- })
-
-#define to_char_checked(val) \
- ({ \
- int64_t tmp = (val); \
- ASSERT_IN_RANGE(tmp, CHAR_MIN, CHAR_MAX); \
- (char)tmp; \
- })
-
-#define to_u16_checked(val) \
- ({ \
- auto tmp = (val); \
- ASSERT_IN_RANGE(tmp, 0, UINT16_MAX); \
- (uint16_t) tmp; \
- })
-
-#define to_i16_checked(val) \
- ({ \
- int64_t tmp = (val); \
- ASSERT_IN_RANGE(tmp, INT16_MIN, INT16_MAX); \
- (int16_t) tmp; \
- })
-
-#define to_u32_checked(val) \
- ({ \
- auto tmp = (val); \
- int64_t max attr_unused = UINT32_MAX; /* silence clang tautological \
- comparison warning*/ \
- ASSERT_IN_RANGE(tmp, 0, max); \
- (uint32_t) tmp; \
- })
-/**
- * Normalize an int value to a specific range.
- *
- * @param i int value to normalize
- * @param min minimal value
- * @param max maximum value
- * @return normalized value
- */
-static inline int attr_const normalize_i_range(int i, int min, int max) {
- if (i > max)
- return max;
- if (i < min)
- return min;
- return i;
-}
-
-/**
- * Linearly interpolate from a range into another.
- *
- * @param a,b first range
- * @param c,d second range
- * @param value value to interpolate, should be in range [a,b]
- * @return interpolated value in range [c,d]
- */
-static inline int attr_const lerp_range(int a, int b, int c, int d, int value) {
- ASSERT_IN_RANGE(value, a, b);
- return (d-c)*(value-a)/(b-a) + c;
-}
-
-#define min2(a, b) ((a) > (b) ? (b) : (a))
-#define max2(a, b) ((a) > (b) ? (a) : (b))
-
-/// clamp `val` into interval [min, max]
-#define clamp(val, min, max) max2(min2(val, max), min)
-
-/**
- * Normalize a double value to a specific range.
- *
- * @param d double value to normalize
- * @param min minimal value
- * @param max maximum value
- * @return normalized value
- */
-static inline double attr_const normalize_d_range(double d, double min, double max) {
- if (d > max)
- return max;
- if (d < min)
- return min;
- return d;
-}
-
-/**
- * Normalize a double value to 0.\ 0 - 1.\ 0.
- *
- * @param d double value to normalize
- * @return normalized value
- */
-static inline double attr_const normalize_d(double d) {
- return normalize_d_range(d, 0.0, 1.0);
-}
-
-/**
- * Convert a hex RGB string to RGB
- */
-static inline struct color hex_to_rgb(const char *hex) {
- struct color rgb;
- // Ignore the # in front of the string
- const char *sane_hex = hex + 1;
- int hex_color = (int)strtol(sane_hex, NULL, 16);
- rgb.red = (float)(hex_color >> 16) / 256;
- rgb.green = (float)((hex_color & 0x00ff00) >> 8) / 256;
- rgb.blue = (float)(hex_color & 0x0000ff) / 256;
-
- return rgb;
-}
-
-attr_noret void
-report_allocation_failure(const char *func, const char *file, unsigned int line);
-
-/**
- * @brief Quit if the passed-in pointer is empty.
- */
-static inline void *
-allocchk_(const char *func_name, const char *file, unsigned int line, void *ptr) {
- if (unlikely(!ptr)) {
- report_allocation_failure(func_name, file, line);
- }
- return ptr;
-}
-
-/// @brief Wrapper of allocchk_().
-#define allocchk(ptr) allocchk_(__func__, __FILE__, __LINE__, ptr)
-
-/// @brief Wrapper of malloc().
-#define cmalloc(type) ((type *)allocchk(malloc(sizeof(type))))
-
-/// @brief Wrapper of malloc() that takes a size
-#define cvalloc(size) allocchk(malloc(size))
-
-/// @brief Wrapper of calloc().
-#define ccalloc(nmemb, type) \
- ({ \
- auto tmp = (nmemb); \
- ASSERT_GEQ(tmp, 0); \
- ((type *)allocchk(calloc((size_t)tmp, sizeof(type)))); \
- })
-
-/// @brief Wrapper of ealloc().
-#define crealloc(ptr, nmemb) \
- ({ \
- auto tmp = (nmemb); \
- ASSERT_GEQ(tmp, 0); \
- ((__typeof__(ptr))allocchk(realloc((ptr), (size_t)tmp * sizeof(*(ptr))))); \
- })
-
-/// RC_TYPE generates a reference counted type from `type`
-///
-/// parameters:
-/// name = the generated type will be called `name`_t.
-/// ctor = the constructor of `type`, will be called when
-/// a value of `type` is created. should take one
-/// argument of `type *`.
-/// dtor = the destructor. will be called when all reference
-/// is gone. has same signature as ctor
-/// Q = function qualifier. this is the qualifier that
-/// will be put before generated functions
-//
-/// functions generated:
-/// `name`_new: create a new reference counted object of `type`
-/// `name`_ref: increment the reference counter, return a
-/// reference to the object
-/// `name`_unref: decrement the reference counter. take a `type **`
-/// because it needs to nullify the reference.
-#define RC_TYPE(type, name, ctor, dtor, Q) \
- typedef struct { \
- type inner; \
- int ref_count; \
- } name##_internal_t; \
- typedef type name##_t; \
- Q type *name##_new(void) { \
- name##_internal_t *ret = cmalloc(name##_internal_t); \
- ctor((type *)ret); \
- ret->ref_count = 1; \
- return (type *)ret; \
- } \
- Q type *name##_ref(type *a) { \
- __auto_type b = (name##_internal_t *)a; \
- b->ref_count++; \
- return a; \
- } \
- Q void name##_unref(type **a) { \
- __auto_type b = (name##_internal_t *)*a; \
- if (!b) \
- return; \
- b->ref_count--; \
- if (!b->ref_count) { \
- dtor((type *)b); \
- free(b); \
- } \
- *a = NULL; \
- }
-
-/// Generate prototypes for functions generated by RC_TYPE
-#define RC_TYPE_PROTO(type, name) \
- typedef type name##_t; \
- type *name##_new(void); \
- void name##_ref(type *a); \
- void name##_unref(type **a);
-
-
-///
-/// Calculates next closest power of two of 32bit integer n
-/// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
-///
-int next_power_of_two(int n);
-
-
-// vim: set noet sw=8 ts=8 :
diff --git a/src/vsync.c b/src/vsync.c
deleted file mode 100644
index 5980155..0000000
--- a/src/vsync.c
+++ /dev/null
@@ -1,204 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-
-/// Function pointers to init VSync modes.
-
-#include "common.h"
-#include "log.h"
-
-#ifdef CONFIG_OPENGL
-#include "backend/gl/glx.h"
-#include "opengl.h"
-#endif
-
-#ifdef CONFIG_VSYNC_DRM
-#include <drm.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-#include "config.h"
-#include "vsync.h"
-
-#ifdef CONFIG_VSYNC_DRM
-/**
- * Wait for next VSync, DRM method.
- *
- * Stolen from:
- * https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp
- */
-static int vsync_drm_wait(session_t *ps) {
- int ret = -1;
- drm_wait_vblank_t vbl;
-
- vbl.request.type = _DRM_VBLANK_RELATIVE, vbl.request.sequence = 1;
-
- do {
- ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
- vbl.request.type &= ~(uint)_DRM_VBLANK_RELATIVE;
- } while (ret && errno == EINTR);
-
- if (ret)
- log_error("VBlank ioctl did not work, unimplemented in this drmver?");
-
- return ret;
-}
-
-/**
- * Initialize DRM VSync.
- *
- * @return true for success, false otherwise
- */
-static bool vsync_drm_init(session_t *ps) {
- // Should we always open card0?
- if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
- log_error("Failed to open device.");
- return false;
- }
-
- if (vsync_drm_wait(ps))
- return false;
-
- return true;
-}
-#endif
-
-#ifdef CONFIG_OPENGL
-/**
- * Initialize OpenGL VSync.
- *
- * Stolen from:
- * http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e
- * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html
- *
- * @return true for success, false otherwise
- */
-static bool vsync_opengl_init(session_t *ps) {
- if (!ensure_glx_context(ps))
- return false;
-
- return glxext.has_GLX_SGI_video_sync;
-}
-
-static bool vsync_opengl_oml_init(session_t *ps) {
- if (!ensure_glx_context(ps))
- return false;
-
- return glxext.has_GLX_OML_sync_control;
-}
-
-static inline bool vsync_opengl_swc_swap_interval(session_t *ps, int interval) {
- if (glxext.has_GLX_MESA_swap_control)
- return glXSwapIntervalMESA((uint)interval) == 0;
- else if (glxext.has_GLX_SGI_swap_control)
- return glXSwapIntervalSGI(interval) == 0;
- else if (glxext.has_GLX_EXT_swap_control) {
- GLXDrawable d = glXGetCurrentDrawable();
- if (d == None) {
- // We don't have a context??
- return false;
- }
- glXSwapIntervalEXT(ps->dpy, glXGetCurrentDrawable(), interval);
- return true;
- }
- return false;
-}
-
-static bool vsync_opengl_swc_init(session_t *ps) {
- if (!bkend_use_glx(ps)) {
- log_error("OpenGL swap control requires the GLX backend.");
- return false;
- }
-
- if (!vsync_opengl_swc_swap_interval(ps, 1)) {
- log_error("Failed to load a swap control extension.");
- return false;
- }
-
- return true;
-}
-
-/**
- * Wait for next VSync, OpenGL method.
- */
-static int vsync_opengl_wait(session_t *ps attr_unused) {
- unsigned vblank_count = 0;
-
- glXGetVideoSyncSGI(&vblank_count);
- glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
- return 0;
-}
-
-/**
- * Wait for next VSync, OpenGL OML method.
- *
- * https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html
- */
-static int vsync_opengl_oml_wait(session_t *ps) {
- int64_t ust = 0, msc = 0, sbc = 0;
-
- glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
- glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc);
- return 0;
-}
-#endif
-
-/**
- * Initialize current VSync method.
- */
-bool vsync_init(session_t *ps) {
-#ifdef CONFIG_OPENGL
- if (bkend_use_glx(ps)) {
- // Mesa turns on swap control by default, undo that
- vsync_opengl_swc_swap_interval(ps, 0);
- }
-#endif
-#ifdef CONFIG_VSYNC_DRM
- log_warn("The DRM vsync method is deprecated, please don't enable it.");
-#endif
-
- if (!ps->o.vsync) {
- return true;
- }
-
-#ifdef CONFIG_OPENGL
- if (bkend_use_glx(ps)) {
- if (!vsync_opengl_swc_init(ps)) {
- return false;
- }
- ps->vsync_wait = NULL; // glXSwapBuffers will automatically wait
- // for vsync, we don't need to do anything.
- return true;
- }
-#endif
-
- // Oh no, we are not using glx backend.
- // Throwing things at wall.
-#ifdef CONFIG_OPENGL
- if (vsync_opengl_oml_init(ps)) {
- log_info("Using the opengl-oml vsync method");
- ps->vsync_wait = vsync_opengl_oml_wait;
- return true;
- }
-
- if (vsync_opengl_init(ps)) {
- log_info("Using the opengl vsync method");
- ps->vsync_wait = vsync_opengl_wait;
- return true;
- }
-#endif
-
-#ifdef CONFIG_VSYNC_DRM
- if (vsync_drm_init(ps)) {
- log_info("Using the drm vsync method");
- ps->vsync_wait = vsync_drm_wait;
- return true;
- }
-#endif
-
- log_error("No supported vsync method found for this backend");
- return false;
-}
diff --git a/src/vsync.h b/src/vsync.h
deleted file mode 100644
index 076bc26..0000000
--- a/src/vsync.h
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) Yuxuan Shui <[email protected]>
-#include <stdbool.h>
-
-typedef struct session session_t;
-
-bool vsync_init(session_t *ps);
diff --git a/src/win.c b/src/win.c
deleted file mode 100644
index 971bdc9..0000000
--- a/src/win.c
+++ /dev/null
@@ -1,3116 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2011-2013, Christopher Jeffrey
-// Copyright (c) 2013 Richard Grenville <[email protected]>
-
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <inttypes.h>
-#include <math.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xcb/composite.h>
-#include <xcb/damage.h>
-#include <xcb/render.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_renderutil.h>
-#include <xcb/xinerama.h>
-
-#include "atom.h"
-#include "backend/backend.h"
-#include "backend/backend_common.h"
-#include "c2.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "list.h"
-#include "log.h"
-#include "picom.h"
-#include "region.h"
-#include "render.h"
-#include "string_utils.h"
-#include "types.h"
-#include "uthash_extra.h"
-#include "utils.h"
-#include "x.h"
-
-#ifdef CONFIG_DBUS
-#include "dbus.h"
-#endif
-
-#ifdef CONFIG_OPENGL
-// TODO(yshui) Get rid of this include
-#include "opengl.h"
-#endif
-
-#include "win.h"
-
-// TODO(yshui) Make more window states internal
-struct managed_win_internal {
- struct managed_win base;
-};
-
-#define OPAQUE (0xffffffff)
-static const int WIN_GET_LEADER_MAX_RECURSION = 20;
-static const int ROUNDED_PIXELS = 1;
-static const double ROUNDED_PERCENT = 0.05;
-
-/**
- * Retrieve the <code>WM_CLASS</code> of a window and update its
- * <code>win</code> structure.
- */
-static bool win_update_class(session_t *ps, struct managed_win *w);
-static int win_update_role(session_t *ps, struct managed_win *w);
-static void win_update_wintype(session_t *ps, struct managed_win *w);
-static int win_update_name(session_t *ps, struct managed_win *w);
-/**
- * Reread opacity property of a window.
- */
-static void win_update_opacity_prop(session_t *ps, struct managed_win *w);
-static void win_update_opacity_target(session_t *ps, struct managed_win *w);
-/**
- * Retrieve frame extents from a window.
- */
-static void
-win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client);
-static void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
-static void win_update_prop_shadow(session_t *ps, struct managed_win *w);
-/**
- * Update leader of a window.
- */
-static void win_update_leader(session_t *ps, struct managed_win *w);
-
-/// Generate a "no corners" region function, from a function that returns the
-/// region via a region_t pointer argument. Corners of the window will be removed from
-/// the returned region.
-/// Function signature has to be (win *, region_t *)
-#define gen_without_corners(fun) \
- void fun##_without_corners(const struct managed_win *w, region_t *res) { \
- fun(w, res); \
- win_region_remove_corners(w, res); \
- }
-
-/// Generate a "return by value" function, from a function that returns the
-/// region via a region_t pointer argument.
-/// Function signature has to be (win *)
-#define gen_by_val(fun) \
- region_t fun##_by_val(const struct managed_win *w) { \
- region_t ret; \
- pixman_region32_init(&ret); \
- fun(w, &ret); \
- return ret; \
- }
-
-/**
- * Clear leader cache of all windows.
- */
-static inline void clear_cache_win_leaders(session_t *ps) {
- win_stack_foreach_managed(w, &ps->window_stack) {
- w->cache_leader = XCB_NONE;
- }
-}
-
-static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions);
-
-/**
- * Get the leader of a window.
- *
- * This function updates w->cache_leader if necessary.
- */
-static inline xcb_window_t win_get_leader(session_t *ps, struct managed_win *w) {
- return win_get_leader_raw(ps, w, 0);
-}
-
-/**
- * Whether the real content of the window is visible.
- *
- * A window is not considered "real" visible if it's fading out. Because in that case a
- * cached version of the window is displayed.
- */
-static inline bool attr_pure win_is_real_visible(const struct managed_win *w) {
- return w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
- w->state != WSTATE_UNMAPPING;
-}
-
-/**
- * Update focused state of a window.
- */
-static void win_update_focused(session_t *ps, struct managed_win *w) {
- if (UNSET != w->focused_force) {
- w->focused = w->focused_force;
- } else {
- w->focused = win_is_focused_raw(ps, w);
-
- // Use wintype_focus, and treat WM windows and override-redirected
- // windows specially
- if (ps->o.wintype_option[w->window_type].focus ||
- (ps->o.mark_wmwin_focused && w->wmwin) ||
- (ps->o.mark_ovredir_focused && w->base.id == w->client_win && !w->wmwin) ||
- (w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
- c2_match(ps, w, ps->o.focus_blacklist, NULL))) {
- w->focused = true;
- }
-
- // If window grouping detection is enabled, mark the window active if
- // its group is
- if (ps->o.track_leader && ps->active_leader &&
- win_get_leader(ps, w) == ps->active_leader) {
- w->focused = true;
- }
- }
-}
-
-/**
- * Run win_on_factor_change() on all windows with the same leader window.
- *
- * @param leader leader window ID
- */
-static inline void group_on_factor_change(session_t *ps, xcb_window_t leader) {
- if (!leader) {
- return;
- }
-
- HASH_ITER2(ps->windows, w) {
- assert(!w->destroyed);
- if (!w->managed) {
- continue;
- }
- auto mw = (struct managed_win *)w;
- if (win_get_leader(ps, mw) == leader) {
- win_on_factor_change(ps, mw);
- }
- }
-}
-
-static inline const char *win_get_name_if_managed(const struct win *w) {
- if (!w->managed) {
- return "(unmanaged)";
- }
- auto mw = (struct managed_win *)w;
- return mw->name;
-}
-
-/**
- * Return whether a window group is really focused.
- *
- * @param leader leader window ID
- * @return true if the window group is focused, false otherwise
- */
-static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
- if (!leader) {
- return false;
- }
-
- HASH_ITER2(ps->windows, w) {
- assert(!w->destroyed);
- if (!w->managed) {
- continue;
- }
- auto mw = (struct managed_win *)w;
- if (win_get_leader(ps, mw) == leader && win_is_focused_raw(ps, mw)) {
- return true;
- }
- }
-
- return false;
-}
-
-/**
- * Get a rectangular region a window occupies, excluding shadow.
- */
-static void win_get_region_local(const struct managed_win *w, region_t *res) {
- assert(w->widthb >= 0 && w->heightb >= 0);
- pixman_region32_fini(res);
- pixman_region32_init_rect(res, 0, 0, (uint)w->widthb, (uint)w->heightb);
-}
-
-/**
- * Get a rectangular region a window occupies, excluding frame and shadow.
- */
-void win_get_region_noframe_local(const struct managed_win *w, region_t *res) {
- const margin_t extents = win_calc_frame_extents(w);
-
- int x = extents.left;
- int y = extents.top;
- int width = max2(w->widthb - (extents.left + extents.right), 0);
- int height = max2(w->heightb - (extents.top + extents.bottom), 0);
-
- pixman_region32_fini(res);
- if (width > 0 && height > 0) {
- pixman_region32_init_rect(res, x, y, (uint)width, (uint)height);
- } else {
- pixman_region32_init(res);
- }
-}
-
-gen_without_corners(win_get_region_noframe_local);
-
-void win_get_region_frame_local(const struct managed_win *w, region_t *res) {
- const margin_t extents = win_calc_frame_extents(w);
- auto outer_width = w->widthb;
- auto outer_height = w->heightb;
-
- pixman_region32_fini(res);
- pixman_region32_init_rects(
- res,
- (rect_t[]){
- // top
- {.x1 = 0, .y1 = 0, .x2 = outer_width, .y2 = extents.top},
- // bottom
- {.x1 = 0, .y1 = outer_height - extents.bottom, .x2 = outer_width, .y2 = outer_height},
- // left
- {.x1 = 0, .y1 = 0, .x2 = extents.left, .y2 = outer_height},
- // right
- {.x1 = outer_width - extents.right, .y1 = 0, .x2 = outer_width, .y2 = outer_height},
- },
- 4);
-
- // limit the frame region to inside the window
- region_t reg_win;
- pixman_region32_init_rects(&reg_win, (rect_t[]){{0, 0, outer_width, outer_height}}, 1);
- pixman_region32_intersect(res, &reg_win, res);
- pixman_region32_fini(&reg_win);
-}
-
-gen_by_val(win_get_region_frame_local);
-
-/**
- * Add a window to damaged area.
- *
- * @param ps current session
- * @param w struct _win element representing the window
- */
-void add_damage_from_win(session_t *ps, const struct managed_win *w) {
- // XXX there was a cached extents region, investigate
- // if that's better
-
- // TODO(yshui) use the bounding shape when the window is shaped, otherwise the
- // damage would be excessive
- region_t extents;
- pixman_region32_init(&extents);
- win_extents(w, &extents);
- add_damage(ps, &extents);
- pixman_region32_fini(&extents);
-}
-
-/// Release the images attached to this window
-static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
- log_debug("Releasing pixmap of window %#010x (%s)", w->base.id, w->name);
- assert(w->win_image);
- if (w->win_image) {
- base->ops->release_image(base, w->win_image);
- w->win_image = NULL;
- // Bypassing win_set_flags, because `w` might have been destroyed
- w->flags |= WIN_FLAGS_PIXMAP_NONE;
- }
-}
-static inline void win_release_oldpixmap(backend_t *base, struct managed_win *w) {
- log_debug("Releasing old_pixmap of window %#010x (%s)", w->base.id, w->name);
- if (w->old_win_image) {
- base->ops->release_image(base, w->old_win_image);
- w->old_win_image = NULL;
- }
-}
-static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
- log_debug("Releasing shadow of window %#010x (%s)", w->base.id, w->name);
- assert(w->shadow_image);
- if (w->shadow_image) {
- base->ops->release_image(base, w->shadow_image);
- w->shadow_image = NULL;
- // Bypassing win_set_flags, because `w` might have been destroyed
- w->flags |= WIN_FLAGS_SHADOW_NONE;
- }
-}
-
-static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w) {
- assert(!w->win_image);
- auto pixmap = x_new_id(b->c);
- auto e = xcb_request_check(
- b->c, xcb_composite_name_window_pixmap_checked(b->c, w->base.id, pixmap));
- if (e) {
- log_error("Failed to get named pixmap for window %#010x(%s)", w->base.id,
- w->name);
- free(e);
- return false;
- }
- log_debug("New named pixmap for %#010x (%s) : %#010x", w->base.id, w->name, pixmap);
- w->win_image =
- b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual), true);
- if (!w->win_image) {
- log_error("Failed to bind pixmap");
- win_set_flags(w, WIN_FLAGS_IMAGE_ERROR);
- return false;
- }
-
- win_clear_flags(w, WIN_FLAGS_PIXMAP_NONE);
- return true;
-}
-
-bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
- struct conv *kernel) {
- assert(!w->shadow_image);
- assert(w->shadow);
- w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, kernel, c.red,
- c.green, c.blue, c.alpha);
- if (!w->shadow_image) {
- log_error("Failed to bind shadow image, shadow will be disabled for "
- "%#010x (%s)",
- w->base.id, w->name);
- win_set_flags(w, WIN_FLAGS_SHADOW_NONE);
- w->shadow = false;
- return false;
- }
-
- log_debug("New shadow for %#010x (%s)", w->base.id, w->name);
- win_clear_flags(w, WIN_FLAGS_SHADOW_NONE);
- return true;
-}
-
-void win_release_images(struct backend_base *backend, struct managed_win *w) {
- // We don't want to decide what we should do if the image we want to release is
- // stale (do we clear the stale flags or not?)
- // But if we are not releasing any images anyway, we don't care about the stale
- // flags.
-
- if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
- assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
- win_release_pixmap(backend, w);
- win_release_oldpixmap(backend, w);
- }
-
- if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
- assert(!win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE));
- win_release_shadow(backend, w);
- }
-}
-
-/// Returns true if the `prop` property is stale, as well as clears the stale flag.
-static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop);
-/// Returns true if any of the properties are stale, as well as clear all the stale flags.
-static void win_clear_all_properties_stale(struct managed_win *w);
-
-/// Fetch new window properties from the X server, and run appropriate updates. Might set
-/// WIN_FLAGS_FACTOR_CHANGED
-static void win_update_properties(session_t *ps, struct managed_win *w) {
- if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_TYPE)) {
- win_update_wintype(ps, w);
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_OPACITY)) {
- win_update_opacity_prop(ps, w);
- // we cannot receive OPACITY change when window has been destroyed
- assert(w->state != WSTATE_DESTROYING);
- win_update_opacity_target(ps, w);
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_FRAME_EXTENTS)) {
- win_update_frame_extents(ps, w, w->client_win);
- add_damage_from_win(ps, w);
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_NAME) ||
- win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_NAME)) {
- if (win_update_name(ps, w) == 1) {
- win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
- }
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLASS)) {
- if (win_update_class(ps, w)) {
- win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
- }
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_WINDOW_ROLE)) {
- if (win_update_role(ps, w) == 1) {
- win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
- }
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->a_COMPTON_SHADOW)) {
- win_update_prop_shadow(ps, w);
- }
-
- if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLIENT_LEADER) ||
- win_fetch_and_unset_property_stale(w, ps->atoms->aWM_TRANSIENT_FOR)) {
- win_update_leader(ps, w);
- }
-
- win_clear_all_properties_stale(w);
-}
-
-static void init_animation(session_t *ps, struct managed_win *w) {
- enum open_window_animation animation = ps->o.animation_for_open_window;
-
- w->animation_transient = wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR);
-
- if (w->window_type != WINTYPE_TOOLTIP && w->animation_transient)
- animation = ps->o.animation_for_transient_window;
-
- if (ps->o.wintype_option[w->window_type].animation < OPEN_WINDOW_ANIMATION_INVALID)
- animation = ps->o.wintype_option[w->window_type].animation;
-
- if (ps->root_desktop_switch_direction != 0) {
- if (ps->o.animation_for_workspace_switch_in == OPEN_WINDOW_ANIMATION_AUTO)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_IN;
- else
- animation = ps->o.animation_for_workspace_switch_in;
- }
-
- switch (animation) {
- case OPEN_WINDOW_ANIMATION_AUTO:
- case OPEN_WINDOW_ANIMATION_NONE: { // No animation
- w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_w = w->pending_g.width;
- w->animation_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_FLYIN: { // Fly-in from a random point outside the screen
- // Compute random point off screen
- double angle = 2 * M_PI * ((double)rand() / RAND_MAX);
- const double radius =
- sqrt(ps->root_width * ps->root_width + ps->root_height * ps->root_height);
-
- // Set animation
- w->animation_center_x = ps->root_width * 0.5 + radius * cos(angle);
- w->animation_center_y = ps->root_height * 0.5 + radius * sin(angle);
- w->animation_w = 0;
- w->animation_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_ZOOM: { // Zoom-in the image, without changing its location
- w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_w = 0;
- w->animation_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_UP: { // Slide up the image, without changing its location
- w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_center_y = w->pending_g.y + w->pending_g.height;
- w->animation_w = w->pending_g.width;
- w->animation_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_DOWN: { // Slide down the image, without changing its location
- w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_center_y = w->pending_g.y;
- w->animation_w = w->pending_g.width;
- w->animation_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_LEFT: { // Slide left the image, without changing its location
- w->animation_center_x = w->pending_g.x + w->pending_g.width;
- w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_w = 0;
- w->animation_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_RIGHT: { // Slide right the image, without changing its location
- w->animation_center_x = w->pending_g.x;
- w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_w = 0;
- w->animation_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_IN: {
- w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5 -
- ps->root_height *
- ((ps->root_desktop_switch_direction < 0 &&
- ps->root_desktop_switch_direction >= -1) ||
- ps->root_desktop_switch_direction > 1?1:-1);
- w->animation_w = w->pending_g.width;
- w->animation_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_OUT: {
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5 -
- ps->root_height *
- ((ps->root_desktop_switch_direction < 0 &&
- ps->root_desktop_switch_direction >= -1) ||
- ps->root_desktop_switch_direction > 1?-1:1);
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_INVALID: assert(false); break;
- }
-}
-
-static void init_animation_unmap(session_t *ps, struct managed_win *w) {
- enum open_window_animation animation;
-
- if (ps->o.animation_for_unmap_window == OPEN_WINDOW_ANIMATION_AUTO) {
- animation = ps->o.animation_for_open_window;
-
- if (w->window_type != WINTYPE_TOOLTIP && w->animation_transient)
- animation = ps->o.animation_for_transient_window;
-
- if (ps->o.wintype_option[w->window_type].animation < OPEN_WINDOW_ANIMATION_INVALID)
- animation = ps->o.wintype_option[w->window_type].animation;
-
- if (animation == OPEN_WINDOW_ANIMATION_SLIDE_UP)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_DOWN;
- else if (animation == OPEN_WINDOW_ANIMATION_SLIDE_DOWN)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_UP;
- else if (animation == OPEN_WINDOW_ANIMATION_SLIDE_LEFT)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_RIGHT;
- else if (animation == OPEN_WINDOW_ANIMATION_SLIDE_RIGHT)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_LEFT;
- else if (animation == OPEN_WINDOW_ANIMATION_SLIDE_IN)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_OUT;
- else if (animation == OPEN_WINDOW_ANIMATION_SLIDE_OUT)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_IN;
-
- } else {
- animation = ps->o.animation_for_unmap_window;
-
- if (ps->o.wintype_option[w->window_type].animation_unmap < OPEN_WINDOW_ANIMATION_INVALID)
- animation = ps->o.wintype_option[w->window_type].animation_unmap;
- }
-
- if (ps->root_desktop_switch_direction != 0) {
- if (ps->o.animation_for_workspace_switch_out == OPEN_WINDOW_ANIMATION_AUTO)
- animation = OPEN_WINDOW_ANIMATION_SLIDE_OUT;
- else
- animation = ps->o.animation_for_workspace_switch_out;
- }
-
- switch (animation) {
- case OPEN_WINDOW_ANIMATION_AUTO:
- case OPEN_WINDOW_ANIMATION_NONE: { // No animation
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_FLYIN: { // Fly-out from a random point outside the screen
- // Compute random point off screen
- double angle = 2 * M_PI * ((double)rand() / RAND_MAX);
- const double radius =
- sqrt(ps->root_width * ps->root_width + ps->root_height * ps->root_height);
-
- // Set animation
- w->animation_dest_center_x = ps->root_width * 0.5 + radius * cos(angle);
- w->animation_dest_center_y = ps->root_height * 0.5 + radius * sin(angle);
- w->animation_dest_w = 0;
- w->animation_dest_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_ZOOM: { // Zoom-out the image, without changing its location
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_dest_w = 0;
- w->animation_dest_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_UP: { // Slide up the image, without changing its location
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y;
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_DOWN: { // Slide down the image, without changing its location
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height;
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = 0;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_LEFT: { // Slide left the image, without changing its location
- w->animation_dest_center_x = w->pending_g.x;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_dest_w = 0;
- w->animation_dest_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_RIGHT: { // Slide right the image, without changing its location
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_dest_w = 0;
- w->animation_dest_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_IN: {
- w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5 -
- ps->root_height *
- ((ps->root_desktop_switch_direction < 0 &&
- ps->root_desktop_switch_direction >= -1) ||
- ps->root_desktop_switch_direction > 1?1:-1);
- w->animation_w = w->pending_g.width;
- w->animation_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_SLIDE_OUT: {
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5 -
- ps->root_height *
- ((ps->root_desktop_switch_direction < 0 &&
- ps->root_desktop_switch_direction >= -1) ||
- ps->root_desktop_switch_direction > 1?-1:1);
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = w->pending_g.height;
- break;
- }
- case OPEN_WINDOW_ANIMATION_INVALID: assert(false); break;
- }
-}
-
-/// Handle non-image flags. This phase might set IMAGES_STALE flags
-void win_process_update_flags(session_t *ps, struct managed_win *w) {
- // Whether the window was visible before we process the mapped flag. i.e. is the
- // window just mapped.
- bool was_visible = win_is_real_visible(w);
- log_trace("Processing flags for window %#010x (%s), was visible: %d", w->base.id,
- w->name, was_visible);
-
- if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
- map_win_start(ps, w);
- win_clear_flags(w, WIN_FLAGS_MAPPED);
- }
-
- if (!win_is_real_visible(w)) {
- // Flags of invisible windows are processed when they are mapped
- return;
- }
-
- // Check client first, because later property updates need accurate client window
- // information
- if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
- win_recheck_client(ps, w);
- win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
- }
-
- bool damaged = false;
- if (win_check_flags_any(w, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
- if (was_visible) {
- // Mark the old extents of this window as damaged. The new extents
- // will be marked damaged below, after the window extents are
- // updated.
- //
- // If the window is just mapped, we don't need to mark the old
- // extent as damaged. (It's possible that the window was in fading
- // and is interrupted by being mapped. In that case, the fading
- // window will be added to damage by map_win_start, so we don't
- // need to do it here)
- add_damage_from_win(ps, w);
- }
-
- // Ignore animations all together if set to none on window type basis
- if (ps->o.wintype_option[w->window_type].animation == 0) {
- w->g = w->pending_g;
-
- // Update window geometry
- } else if (ps->o.animations) {
- if (!was_visible) {
- // Set window-open animation
- init_animation(ps, w);
-
- w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = w->pending_g.height;
-
- w->g.x = (int16_t)round(w->animation_center_x -
- w->animation_w * 0.5);
- w->g.y = (int16_t)round(w->animation_center_y -
- w->animation_h * 0.5);
- w->g.width = (uint16_t)round(w->animation_w);
- w->g.height = (uint16_t)round(w->animation_h);
-
- } else {
- w->animation_dest_center_x =
- w->pending_g.x + w->pending_g.width * 0.5;
- w->animation_dest_center_y =
- w->pending_g.y + w->pending_g.height * 0.5;
- w->animation_dest_w = w->pending_g.width;
- w->animation_dest_h = w->pending_g.height;
- }
-
- w->g.border_width = w->pending_g.border_width;
-
- 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_inv_og_distance =
- 1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
- w_dist * w_dist + h_dist * h_dist);
-
- if (isinf(w->animation_inv_og_distance))
- w->animation_inv_og_distance = 0;
-
- // We only grab images if w->reg_ignore_valid is true as
- // there's an ev_shape_notify() event fired quickly on new windows
- // for e.g. in case of Firefox main menu and ev_shape_notify()
- // sets the win_set_flags(w, WIN_FLAGS_SIZE_STALE); which
- // brakes the new image captured and because this same event
- // also sets w->reg_ignore_valid = false; too we check for it
- if (w->reg_ignore_valid) {
- if (w->old_win_image) {
- ps->backend_data->ops->release_image(ps->backend_data,
- w->old_win_image);
- w->old_win_image = NULL;
- }
-
- // We only grab
- if (w->win_image) {
- w->old_win_image = ps->backend_data->ops->clone_image(
- ps->backend_data, w->win_image, &w->bounding_shape);
- }
- }
-
- w->animation_progress = 0.0;
-
- } else {
- w->g = w->pending_g;
- }
-
- if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
- win_on_win_size_change(ps, w);
- win_update_bounding_shape(ps, w);
- damaged = true;
- win_clear_flags(w, WIN_FLAGS_SIZE_STALE);
- }
-
- if (win_check_flags_all(w, WIN_FLAGS_POSITION_STALE)) {
- damaged = true;
- win_clear_flags(w, WIN_FLAGS_POSITION_STALE);
- }
-
- win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, w);
- }
-
- if (win_check_flags_all(w, WIN_FLAGS_PROPERTY_STALE)) {
- win_update_properties(ps, w);
- win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE);
- }
-
- // Factor change flags could be set by previous stages, so must be handled last
- if (win_check_flags_all(w, WIN_FLAGS_FACTOR_CHANGED)) {
- win_on_factor_change(ps, w);
- win_clear_flags(w, WIN_FLAGS_FACTOR_CHANGED);
- }
-
- // Add damage, has to be done last so the window has the latest geometry
- // information.
- if (damaged) {
- add_damage_from_win(ps, w);
- }
-}
-
-void win_process_image_flags(session_t *ps, struct managed_win *w) {
- assert(!win_check_flags_all(w, WIN_FLAGS_MAPPED));
-
- if (w->state == WSTATE_UNMAPPED || w->state == WSTATE_DESTROYING ||
- w->state == WSTATE_UNMAPPING) {
- // Flags of invisible windows are processed when they are mapped
- return;
- }
-
- // Not a loop
- while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) &&
- !win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) {
- // Image needs to be updated, update it.
- if (!ps->backend_data) {
- // We are using legacy backend, nothing to do here.
- break;
- }
-
- if (win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)) {
- // Check to make sure the window is still mapped, otherwise we
- // won't be able to rebind pixmap after releasing it, yet we might
- // still need the pixmap for rendering.
- assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
- if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
- // Must release images first, otherwise breaks
- // NVIDIA driver
- win_release_pixmap(ps->backend_data, w);
- }
- win_bind_pixmap(ps->backend_data, w);
- }
-
- if (win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE)) {
- if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
- win_release_shadow(ps->backend_data, w);
- }
- if (w->shadow) {
- win_bind_shadow(ps->backend_data, w,
- (struct color){.red = ps->o.shadow_red,
- .green = ps->o.shadow_green,
- .blue = ps->o.shadow_blue,
- .alpha = ps->o.shadow_opacity},
- ps->gaussian_map);
- }
- }
-
- // break here, loop always run only once
- break;
- }
-
- // Clear stale image flags
- if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) {
- win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
- }
-}
-
-/**
- * Check if a window has rounded corners.
- * XXX This is really dumb
- */
-static bool attr_pure win_has_rounded_corners(const struct managed_win *w) {
- if (!w->bounding_shaped) {
- return false;
- }
-
- // Quit if border_size() returns XCB_NONE
- if (!pixman_region32_not_empty((region_t *)&w->bounding_shape)) {
- return false;
- }
-
- // Determine the minimum width/height of a rectangle that could mark
- // a window as having rounded corners
- auto minwidth =
- (uint16_t)max2(w->widthb * (1 - ROUNDED_PERCENT), w->widthb - ROUNDED_PIXELS);
- auto minheight =
- (uint16_t)max2(w->heightb * (1 - ROUNDED_PERCENT), w->heightb - ROUNDED_PIXELS);
-
- // Get the rectangles in the bounding region
- int nrects = 0;
- const rect_t *rects =
- pixman_region32_rectangles((region_t *)&w->bounding_shape, &nrects);
-
- // Look for a rectangle large enough for this window be considered
- // having rounded corners
- for (int i = 0; i < nrects; ++i) {
- if (rects[i].x2 - rects[i].x1 >= minwidth &&
- rects[i].y2 - rects[i].y1 >= minheight) {
- return true;
- }
- }
- return false;
-}
-
-int win_update_name(session_t *ps, struct managed_win *w) {
- char **strlst = NULL;
- int nstr = 0;
-
- if (!w->client_win) {
- return 0;
- }
-
- if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
- log_debug("(%#010x): _NET_WM_NAME unset, falling back to WM_NAME.",
- w->client_win);
-
- if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_NAME, &strlst, &nstr)) {
- log_debug("Unsetting window name for %#010x", w->client_win);
- free(w->name);
- w->name = NULL;
- return -1;
- }
- }
-
- int ret = 0;
- if (!w->name || strcmp(w->name, strlst[0]) != 0) {
- ret = 1;
- free(w->name);
- w->name = strdup(strlst[0]);
- }
-
- free(strlst);
-
- log_debug("(%#010x): client = %#010x, name = \"%s\", "
- "ret = %d",
- w->base.id, w->client_win, w->name, ret);
- return ret;
-}
-
-static int win_update_role(session_t *ps, struct managed_win *w) {
- char **strlst = NULL;
- int nstr = 0;
-
- if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
- return -1;
- }
-
- int ret = 0;
- if (!w->role || strcmp(w->role, strlst[0]) != 0) {
- ret = 1;
- free(w->role);
- w->role = strdup(strlst[0]);
- }
-
- free(strlst);
-
- log_trace("(%#010x): client = %#010x, role = \"%s\", "
- "ret = %d",
- w->base.id, w->client_win, w->role, ret);
- return ret;
-}
-
-/**
- * Check if a window is bounding-shaped.
- */
-static inline bool win_bounding_shaped(const session_t *ps, xcb_window_t wid) {
- if (ps->shape_exists) {
- xcb_shape_query_extents_reply_t *reply;
- Bool bounding_shaped;
-
- reply = xcb_shape_query_extents_reply(
- ps->c, xcb_shape_query_extents(ps->c, wid), NULL);
- bounding_shaped = reply && reply->bounding_shaped;
- free(reply);
-
- return bounding_shaped;
- }
-
- return false;
-}
-
-static wintype_t wid_get_prop_wintype(session_t *ps, xcb_window_t wid) {
- winprop_t prop =
- x_get_prop(ps->c, wid, ps->atoms->a_NET_WM_WINDOW_TYPE, 32L, XCB_ATOM_ATOM, 32);
-
- for (unsigned i = 0; i < prop.nitems; ++i) {
- for (wintype_t j = 1; j < NUM_WINTYPES; ++j) {
- if (ps->atoms_wintypes[j] == (xcb_atom_t)prop.p32[i]) {
- free_winprop(&prop);
- return j;
- }
- }
- }
-
- free_winprop(&prop);
-
- return WINTYPE_UNKNOWN;
-}
-
-static bool
-wid_get_opacity_prop(session_t *ps, xcb_window_t wid, opacity_t def, opacity_t *out) {
- bool ret = false;
- *out = def;
-
- winprop_t prop = x_get_prop(ps->c, wid, ps->atoms->a_NET_WM_WINDOW_OPACITY, 1L,
- XCB_ATOM_CARDINAL, 32);
-
- if (prop.nitems) {
- *out = *prop.c32;
- ret = true;
- }
-
- free_winprop(&prop);
-
- return ret;
-}
-
-// XXX should distinguish between frame has alpha and window body has alpha
-bool win_has_alpha(const struct managed_win *w) {
- return w->pictfmt && w->pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
- w->pictfmt->direct.alpha_mask;
-}
-
-bool win_client_has_alpha(const struct managed_win *w) {
- return w->client_pictfmt && w->client_pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
- w->client_pictfmt->direct.alpha_mask;
-}
-
-winmode_t win_calc_mode(const struct managed_win *w) {
- if (w->opacity < 1.0) {
- return WMODE_TRANS;
- }
-
- if (win_has_alpha(w)) {
- if (w->client_win == XCB_NONE) {
- // This is a window not managed by the WM, and it has alpha,
- // so it's transparent. No need to check WM frame.
- return WMODE_TRANS;
- }
- // The WM window has alpha
- if (win_client_has_alpha(w)) {
- // The client window also has alpha, the entire window is
- // transparent
- return WMODE_TRANS;
- }
- if (win_has_frame(w)) {
- // The client window doesn't have alpha, but we have a WM frame
- // window, which has alpha.
- return WMODE_FRAME_TRANS;
- }
- // Although the WM window has alpha, the frame window has 0 size, so
- // consider the window solid
- }
-
- if (w->frame_opacity != 1.0 && win_has_frame(w)) {
- return WMODE_FRAME_TRANS;
- }
-
- // log_trace("Window %#010x(%s) is solid", w->client_win, w->name);
- return WMODE_SOLID;
-}
-
-/**
- * Calculate and return the opacity target of a window.
- *
- * The priority of opacity settings are:
- *
- * inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if set) >
- * opacity-rules (if matched) > window type default opacity > active/inactive opacity
- *
- * @param ps current session
- * @param w struct _win object representing the window
- *
- * @return target opacity
- */
-double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
- double opacity = 1;
-
- if (w->state == WSTATE_UNMAPPED) {
- // be consistent
- return 0;
- }
- if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
- if (ps->root_desktop_switch_direction)
- return w->opacity;
-
- return 0;
- }
- // Try obeying opacity property and window type opacity firstly
- if (w->has_opacity_prop) {
- opacity = ((double)w->opacity_prop) / OPAQUE;
- } else if (w->opacity_is_set) {
- opacity = w->opacity_set;
- } else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) {
- opacity = ps->o.wintype_option[w->window_type].opacity;
- } else {
- // Respect active_opacity only when the window is physically focused
- if (win_is_focused_raw(ps, w))
- opacity = ps->o.active_opacity;
- else if (!w->focused)
- // Respect inactive_opacity in some cases
- opacity = ps->o.inactive_opacity;
- }
-
- // respect inactive override
- if (ps->o.inactive_opacity_override && !w->focused) {
- opacity = ps->o.inactive_opacity;
- }
-
- return opacity;
-}
-
-/**
- * Determine whether a window is to be dimmed.
- */
-bool win_should_dim(session_t *ps, const struct managed_win *w) {
- // Make sure we do nothing if the window is unmapped / being destroyed
- if (w->state == WSTATE_UNMAPPED) {
- return false;
- }
-
- if (ps->o.inactive_dim > 0 && !(w->focused)) {
- return true;
- } else {
- return false;
- }
-}
-
-/**
- * Determine if a window should fade on opacity change.
- */
-bool win_should_fade(session_t *ps, const struct managed_win *w) {
- // To prevent it from being overwritten by last-paint value if the window is
- if (w->fade_force != UNSET) {
- return w->fade_force;
- }
- if (ps->o.no_fading_openclose && w->in_openclose) {
- return false;
- }
- if (ps->o.no_fading_destroyed_argb && w->state == WSTATE_DESTROYING &&
- win_has_alpha(w) && w->client_win && w->client_win != w->base.id) {
- // deprecated
- return false;
- }
- if (w->fade_excluded) {
- return false;
- }
- return ps->o.wintype_option[w->window_type].fade;
-}
-
-/**
- * Reread _COMPTON_SHADOW property from a window.
- *
- * The property must be set on the outermost window, usually the WM frame.
- */
-void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w) {
- winprop_t prop = x_get_prop(ps->c, w->base.id, ps->atoms->a_COMPTON_SHADOW, 1,
- XCB_ATOM_CARDINAL, 32);
-
- if (!prop.nitems) {
- w->prop_shadow = -1;
- } else {
- w->prop_shadow = *prop.c32;
- }
-
- free_winprop(&prop);
-}
-
-static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new) {
- if (w->shadow == shadow_new) {
- return;
- }
-
- log_debug("Updating shadow property of window %#010x (%s) to %d", w->base.id,
- w->name, shadow_new);
-
- // We don't handle property updates of non-visible windows until they are mapped.
- assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
- w->state != WSTATE_UNMAPPING);
-
- // Keep a copy of window extent before the shadow change. Will be used for
- // calculation of damaged region
- region_t extents;
- pixman_region32_init(&extents);
- win_extents(w, &extents);
-
- // Apply the shadow change
- w->shadow = shadow_new;
-
- if (ps->redirected) {
- // Add damage for shadow change
-
- // Window extents need update on shadow state change
- // Shadow geometry currently doesn't change on shadow state change
- // calc_shadow_geometry(ps, w);
-
- // Note: because the release and creation of the shadow images are
- // delayed. When multiple shadow changes happen in a row, without
- // rendering phase between them, there could be a stale shadow image
- // attached to the window even if w->shadow was previously false. And vice
- // versa. So we check the STALE flag before asserting the existence of the
- // shadow image.
- if (w->shadow) {
- // Mark the new extents as damaged if the shadow is added
- assert(!w->shadow_image ||
- win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
- !ps->o.experimental_backends);
- pixman_region32_clear(&extents);
- win_extents(w, &extents);
- add_damage_from_win(ps, w);
- } else {
- // Mark the old extents as damaged if the shadow is removed
- assert(w->shadow_image ||
- win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
- !ps->o.experimental_backends);
- add_damage(ps, &extents);
- }
-
- // Delayed update of shadow image
- // By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to
- // re-create or release the shaodw in based on whether w->shadow is set.
- win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
-
- // Only set pending_updates if we are redirected. Otherwise change of a
- // shadow won't have influence on whether we should redirect.
- ps->pending_updates = true;
- }
-
- pixman_region32_fini(&extents);
-}
-
-/**
- * Determine if a window should have shadow, and update things depending
- * on shadow state.
- */
-static void win_determine_shadow(session_t *ps, struct managed_win *w) {
- log_debug("Determining shadow of window %#010x (%s)", w->base.id, w->name);
- bool shadow_new = w->shadow;
-
- if (w->shadow_force != UNSET) {
- shadow_new = w->shadow_force;
- } else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
- shadow_new = true;
- if (!ps->o.wintype_option[w->window_type].shadow) {
- log_debug("Shadow disabled by wintypes");
- shadow_new = false;
- } else if (c2_match(ps, w, ps->o.shadow_blacklist, NULL)) {
- log_debug("Shadow disabled by shadow-exclude");
- shadow_new = false;
- } else if (ps->o.shadow_ignore_shaped && w->bounding_shaped &&
- !w->rounded_corners) {
- log_debug("Shadow disabled by shadow-ignore-shaped");
- shadow_new = false;
- } else if (w->prop_shadow == 0) {
- log_debug("Shadow disabled by shadow property");
- shadow_new = false;
- }
- }
-
- win_set_shadow(ps, w, shadow_new);
-}
-
-/**
- * Reread _COMPTON_SHADOW property from a window and update related
- * things.
- */
-void win_update_prop_shadow(session_t *ps, struct managed_win *w) {
- long attr_shadow_old = w->prop_shadow;
-
- win_update_prop_shadow_raw(ps, w);
-
- if (w->prop_shadow != attr_shadow_old) {
- win_determine_shadow(ps, w);
- }
-}
-
-static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) {
- bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
- c2_match(ps, w, ps->o.shadow_clip_list, NULL));
- w->clip_shadow_above = should_crop;
-}
-
-static void win_set_invert_color(session_t *ps, struct managed_win *w, bool invert_color_new) {
- if (w->invert_color == invert_color_new) {
- return;
- }
-
- w->invert_color = invert_color_new;
-
- add_damage_from_win(ps, w);
-}
-
-/**
- * Determine if a window should have color inverted.
- */
-static void win_determine_invert_color(session_t *ps, struct managed_win *w) {
- bool invert_color_new = w->invert_color;
-
- if (UNSET != w->invert_color_force) {
- invert_color_new = w->invert_color_force;
- } else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
- invert_color_new = c2_match(ps, w, ps->o.invert_color_list, NULL);
- }
-
- win_set_invert_color(ps, w, invert_color_new);
-}
-
-/**
- * Set w->invert_color_force of a window.
- */
-void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t val) {
- if (val != w->invert_color_force) {
- w->invert_color_force = val;
- win_determine_invert_color(ps, w);
- queue_redraw(ps);
- }
-}
-
-/**
- * Set w->fade_force of a window.
- *
- * Doesn't affect fading already in progress
- */
-void win_set_fade_force(struct managed_win *w, switch_t val) {
- w->fade_force = val;
-}
-
-/**
- * Set w->focused_force of a window.
- */
-void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val) {
- if (val != w->focused_force) {
- w->focused_force = val;
- win_on_factor_change(ps, w);
- queue_redraw(ps);
- }
-}
-
-/**
- * Set w->shadow_force of a window.
- */
-void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val) {
- if (val != w->shadow_force) {
- w->shadow_force = val;
- win_determine_shadow(ps, w);
- queue_redraw(ps);
- }
-}
-
-static void
-win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_background_new) {
- if (w->blur_background == blur_background_new)
- return;
-
- w->blur_background = blur_background_new;
-
- // This damage might not be absolutely necessary (e.g. when the window is opaque),
- // but blur_background changes should be rare, so this should be fine.
- add_damage_from_win(ps, w);
-}
-
-/**
- * Determine if a window should have background blurred.
- */
-static void win_determine_blur_background(session_t *ps, struct managed_win *w) {
- log_debug("Determining blur-background of window %#010x (%s)", w->base.id, w->name);
- if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
- return;
- }
-
- bool blur_background_new = ps->o.blur_method != BLUR_METHOD_NONE;
- if (blur_background_new) {
- if (!ps->o.wintype_option[w->window_type].blur_background) {
- log_debug("Blur background disabled by wintypes");
- blur_background_new = false;
- } else if (c2_match(ps, w, ps->o.blur_background_blacklist, NULL)) {
- log_debug("Blur background disabled by blur-background-exclude");
- blur_background_new = false;
- }
- }
-
- win_set_blur_background(ps, w, blur_background_new);
-}
-
-/**
- * Determine if a window should have rounded corners.
- */
-static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
- if (ps->o.corner_radius == 0) {
- w->corner_radius = 0;
- return;
- }
-
- // Don't round full screen windows & excluded windows
- if ((w && win_is_fullscreen(ps, w)) ||
- c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL)) {
- w->corner_radius = 0;
- log_debug("Not rounding corners for window %#010x", w->base.id);
- } else {
- w->corner_radius = ps->o.corner_radius;
- log_debug("Rounding corners for window %#010x", w->base.id);
- // Initialize the border color to an invalid value
- w->border_col[0] = w->border_col[1] = w->border_col[2] =
- w->border_col[3] = -1.0F;
- }
-}
-
-/**
- * Update window opacity according to opacity rules.
- */
-void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
- if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
- return;
- }
-
- double opacity = 1.0;
- bool is_set = false;
- void *val = NULL;
- if (c2_match(ps, w, ps->o.opacity_rules, &val)) {
- opacity = ((double)(long)val) / 100.0;
- is_set = true;
- }
-
- w->opacity_set = opacity;
- w->opacity_is_set = is_set;
-}
-
-/**
- * Function to be called on window data changes.
- *
- * TODO(yshui) need better name
- */
-void win_on_factor_change(session_t *ps, struct managed_win *w) {
- log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
- // Focus needs to be updated first, as other rules might depend on the focused
- // state of the window
- win_update_focused(ps, w);
-
- win_determine_shadow(ps, w);
- win_determine_clip_shadow_above(ps, w);
- win_determine_invert_color(ps, w);
- win_determine_blur_background(ps, w);
- win_determine_rounded_corners(ps, w);
- w->mode = win_calc_mode(w);
- log_debug("Window mode changed to %d", w->mode);
- win_update_opacity_rule(ps, w);
- if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
- w->paint_excluded = c2_match(ps, w, ps->o.paint_blacklist, NULL);
- }
- if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
- w->unredir_if_possible_excluded =
- c2_match(ps, w, ps->o.unredir_if_possible_blacklist, NULL);
- }
-
- w->fade_excluded = c2_match(ps, w, ps->o.fade_blacklist, NULL);
-
- win_update_opacity_target(ps, w);
-
- w->reg_ignore_valid = false;
-}
-
-/**
- * Update cache data in struct _win that depends on window size.
- */
-void win_on_win_size_change(session_t *ps, struct managed_win *w) {
- w->widthb = w->g.width + w->g.border_width * 2;
- w->heightb = w->g.height + w->g.border_width * 2;
- w->shadow_dx = ps->o.shadow_offset_x;
- w->shadow_dy = ps->o.shadow_offset_y;
- w->shadow_width = w->widthb + ps->o.shadow_radius * 2;
- w->shadow_height = w->heightb + ps->o.shadow_radius * 2;
-
- // We don't handle property updates of non-visible windows until they are mapped.
- assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
- w->state != WSTATE_UNMAPPING);
-
- // Invalidate the shadow we built
- if (w->state != WSTATE_DESTROYING)
- win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
-
- ps->pending_updates = true;
- free_paint(ps, &w->shadow_paint);
-}
-
-/**
- * Update window type.
- */
-void win_update_wintype(session_t *ps, struct managed_win *w) {
- const wintype_t wtype_old = w->window_type;
-
- // Detect window type here
- w->window_type = wid_get_prop_wintype(ps, w->client_win);
-
- // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take
- // override-redirect windows or windows without WM_TRANSIENT_FOR as
- // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
- if (WINTYPE_UNKNOWN == w->window_type) {
- if (w->a.override_redirect ||
- !wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR))
- w->window_type = WINTYPE_NORMAL;
- else
- w->window_type = WINTYPE_DIALOG;
- }
-
- if (w->window_type != wtype_old) {
- win_on_factor_change(ps, w);
- }
-}
-
-/**
- * Mark a window as the client window of another.
- *
- * @param ps current session
- * @param w struct _win of the parent window
- * @param client window ID of the client window
- */
-void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client) {
- w->client_win = client;
-
- // If the window isn't mapped yet, stop here, as the function will be
- // called in map_win()
- if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
- return;
- }
-
- auto e = xcb_request_check(
- ps->c, xcb_change_window_attributes(
- ps->c, client, XCB_CW_EVENT_MASK,
- (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)}));
- if (e) {
- log_error("Failed to change event mask of window %#010x", client);
- free(e);
- }
-
- win_update_wintype(ps, w);
-
- // Get frame widths. The window is in damaged area already.
- win_update_frame_extents(ps, w, client);
-
- // Get window group
- if (ps->o.track_leader) {
- win_update_leader(ps, w);
- }
-
- // Get window name and class if we are tracking them
- win_update_name(ps, w);
- win_update_class(ps, w);
- win_update_role(ps, w);
-
- // Update everything related to conditions
- win_on_factor_change(ps, w);
-
- auto r = xcb_get_window_attributes_reply(
- ps->c, xcb_get_window_attributes(ps->c, w->client_win), &e);
- if (!r) {
- log_error_x_error(e, "Failed to get client window attributes");
- return;
- }
-
- w->client_pictfmt = x_get_pictform_for_visual(ps->c, r->visual);
- free(r);
-}
-
-/**
- * Unmark current client window of a window.
- *
- * @param ps current session
- * @param w struct _win of the parent window
- */
-void win_unmark_client(session_t *ps, struct managed_win *w) {
- xcb_window_t client = w->client_win;
- log_debug("Detaching client window %#010x from frame %#010x (%s)", client,
- w->base.id, w->name);
-
- w->client_win = XCB_NONE;
-
- // Recheck event mask
- xcb_change_window_attributes(
- ps->c, client, XCB_CW_EVENT_MASK,
- (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
-}
-
-/**
- * Look for the client window of a particular window.
- */
-static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
- if (wid_has_prop(ps, w, ps->atoms->aWM_STATE)) {
- return w;
- }
-
- xcb_query_tree_reply_t *reply =
- xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
- if (!reply) {
- return 0;
- }
-
- xcb_window_t *children = xcb_query_tree_children(reply);
- int nchildren = xcb_query_tree_children_length(reply);
- int i;
- xcb_window_t ret = 0;
-
- for (i = 0; i < nchildren; ++i) {
- if ((ret = find_client_win(ps, children[i]))) {
- break;
- }
- }
-
- free(reply);
-
- return ret;
-}
-
-/**
- * Recheck client window of a window.
- *
- * @param ps current session
- * @param w struct _win of the parent window
- */
-void win_recheck_client(session_t *ps, struct managed_win *w) {
- assert(ps->server_grabbed);
- // Initialize wmwin to false
- w->wmwin = false;
-
- // Look for the client window
-
- // Always recursively look for a window with WM_STATE, as Fluxbox
- // sets override-redirect flags on all frame windows.
- xcb_window_t cw = find_client_win(ps, w->base.id);
- if (cw) {
- log_debug("(%#010x): client %#010x", w->base.id, cw);
- }
- // Set a window's client window to itself if we couldn't find a
- // client window
- if (!cw) {
- cw = w->base.id;
- w->wmwin = !w->a.override_redirect;
- log_debug("(%#010x): client self (%s)", w->base.id,
- (w->wmwin ? "wmwin" : "override-redirected"));
- }
-
- // Unmark the old one
- if (w->client_win && w->client_win != cw) {
- win_unmark_client(ps, w);
- }
-
- // Mark the new one
- win_mark_client(ps, w, cw);
-}
-
-/**
- * Free all resources in a <code>struct _win</code>.
- */
-void free_win_res(session_t *ps, struct managed_win *w) {
- // No need to call backend release_image here because
- // finish_unmap_win should've done that for us.
- // XXX unless we are called by session_destroy
- // assert(w->win_data == NULL);
- free_win_res_glx(ps, w);
- free_paint(ps, &w->paint);
- free_paint(ps, &w->shadow_paint);
- // Above should be done during unmapping
- // Except when we are called by session_destroy
-
- pixman_region32_fini(&w->bounding_shape);
- // BadDamage may be thrown if the window is destroyed
- set_ignore_cookie(ps, xcb_damage_destroy(ps->c, w->damage));
- rc_region_unref(&w->reg_ignore);
- free(w->name);
- free(w->class_instance);
- free(w->class_general);
- free(w->role);
-
- free(w->stale_props);
- w->stale_props = NULL;
- w->stale_props_capacity = 0;
-}
-
-/// Insert a new window after list_node `prev`
-/// New window will be in unmapped state
-static struct win *add_win(session_t *ps, xcb_window_t id, struct list_node *prev) {
- log_debug("Adding window %#010x", id);
- struct win *old_w = NULL;
- HASH_FIND_INT(ps->windows, &id, old_w);
- assert(old_w == NULL);
-
- auto new_w = cmalloc(struct win);
- list_insert_after(prev, &new_w->stack_neighbour);
- new_w->id = id;
- new_w->managed = false;
- new_w->is_new = true;
- new_w->destroyed = false;
-
- HASH_ADD_INT(ps->windows, id, new_w);
- ps->pending_updates = true;
- return new_w;
-}
-
-/// Insert a new win entry at the top of the stack
-struct win *add_win_top(session_t *ps, xcb_window_t id) {
- return add_win(ps, id, &ps->window_stack);
-}
-
-/// Insert a new window above window with id `below`, if there is no window, add to top
-/// New window will be in unmapped state
-struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below) {
- struct win *w = NULL;
- HASH_FIND_INT(ps->windows, &below, w);
- if (!w) {
- if (!list_is_empty(&ps->window_stack)) {
- // `below` window is not found even if the window stack is not
- // empty
- return NULL;
- }
- return add_win_top(ps, id);
- } else {
- // we found something from the hash table, so if the stack is empty,
- // we are in an inconsistent state.
- assert(!list_is_empty(&ps->window_stack));
- return add_win(ps, id, w->stack_neighbour.prev);
- }
-}
-
-/// Query the Xorg for information about window `win`
-/// `win` pointer might become invalid after this function returns
-/// Returns the pointer to the window, might be different from `w`
-struct win *fill_win(session_t *ps, struct win *w) {
- static const struct managed_win win_def = {
- // No need to initialize. (or, you can think that
- // they are initialized right here).
- // The following ones are updated during paint or paint preprocess
- .shadow_opacity = 0.0,
- .to_paint = false,
- .frame_opacity = 1.0,
- .dim = false,
- .invert_color = false,
- .blur_background = false,
- .reg_ignore = NULL,
- // The following ones are updated for other reasons
- .pixmap_damaged = false, // updated by damage events
- .state = WSTATE_UNMAPPED, // updated by window state changes
- .in_openclose = true, // set to false after first map is done,
- // true here because window is just created
- .animation_velocity_x = 0.0, // updated by window geometry changes
- .animation_velocity_y = 0.0, // updated by window geometry changes
- .animation_velocity_w = 0.0, // updated by window geometry changes
- .animation_velocity_h = 0.0, // updated by window geometry changes
- .animation_progress = 1.0, // updated by window geometry changes
- .animation_inv_og_distance = NAN, // updated by window geometry changes
- .reg_ignore_valid = false, // set to true when damaged
- .flags = WIN_FLAGS_IMAGES_NONE, // updated by property/attributes/etc
- // change
- .stale_props = NULL,
- .stale_props_capacity = 0,
-
- // Runtime variables, updated by dbus
- .fade_force = UNSET,
- .shadow_force = UNSET,
- .focused_force = UNSET,
- .invert_color_force = UNSET,
-
- // Initialized in this function
- .a = {0},
- .pictfmt = NULL,
- .client_pictfmt = NULL,
- .widthb = 0,
- .heightb = 0,
- .shadow_dx = 0,
- .shadow_dy = 0,
- .shadow_width = 0,
- .shadow_height = 0,
- .damage = XCB_NONE,
-
- // Not initialized until mapped, this variables
- // have no meaning or have no use until the window
- // is mapped
- .win_image = NULL,
- .old_win_image = NULL,
- .shadow_image = NULL,
- .prev_trans = NULL,
- .shadow = false,
- .clip_shadow_above = false,
- .xinerama_scr = -1,
- .mode = WMODE_TRANS,
- .ever_damaged = false,
- .client_win = XCB_NONE,
- .leader = XCB_NONE,
- .cache_leader = XCB_NONE,
- .window_type = WINTYPE_UNKNOWN,
- .wmwin = false,
- .focused = false,
- .opacity = 0,
- .opacity_target = 0,
- .has_opacity_prop = false,
- .opacity_prop = OPAQUE,
- .opacity_is_set = false,
- .opacity_set = 1,
- .frame_extents = MARGIN_INIT, // in win_mark_client
- .bounding_shaped = false,
- .bounding_shape = {0},
- .rounded_corners = false,
- .paint_excluded = false,
- .fade_excluded = false,
- .unredir_if_possible_excluded = false,
- .prop_shadow = -1,
- // following 4 are set in win_mark_client
- .name = NULL,
- .class_instance = NULL,
- .class_general = NULL,
- .role = NULL,
-
- // Initialized during paint
- .paint = PAINT_INIT,
- .shadow_paint = PAINT_INIT,
-
- .corner_radius = 0,
- };
-
- assert(!w->destroyed);
- assert(w->is_new);
-
- w->is_new = false;
-
- // Reject overlay window and already added windows
- if (w->id == ps->overlay) {
- return w;
- }
-
- auto duplicated_win = find_managed_win(ps, w->id);
- if (duplicated_win) {
- log_debug("Window %#010x (recorded name: %s) added multiple times", w->id,
- duplicated_win->name);
- return &duplicated_win->base;
- }
-
- log_debug("Managing window %#010x", w->id);
- xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(ps->c, w->id);
- xcb_get_window_attributes_reply_t *a =
- xcb_get_window_attributes_reply(ps->c, acookie, NULL);
- if (!a || a->map_state == XCB_MAP_STATE_UNVIEWABLE) {
- // Failed to get window attributes or geometry probably means
- // the window is gone already. Unviewable means the window is
- // already reparented elsewhere.
- // BTW, we don't care about Input Only windows, except for stacking
- // proposes, so we need to keep track of them still.
- free(a);
- return w;
- }
-
- if (a->_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
- // No need to manage this window, but we still keep it on the window stack
- w->managed = false;
- free(a);
- return w;
- }
-
- // Allocate and initialize the new win structure
- auto new_internal = cmalloc(struct managed_win_internal);
- auto new = (struct managed_win *)new_internal;
-
- // Fill structure
- // We only need to initialize the part that are not initialized
- // by map_win
- *new = win_def;
- new->base = *w;
- new->base.managed = true;
- new->a = *a;
- pixman_region32_init(&new->bounding_shape);
-
- free(a);
-
- xcb_generic_error_t *e;
- auto g = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, w->id), &e);
- if (!g) {
- log_error_x_error(e, "Failed to get geometry of window %#010x", w->id);
- free(e);
- free(new);
- return w;
- }
- new->pending_g = (struct win_geometry){
- .x = g->x,
- .y = g->y,
- .width = g->width,
- .height = g->height,
- .border_width = g->border_width,
- };
-
- free(g);
-
- // Create Damage for window (if not Input Only)
- new->damage = x_new_id(ps->c);
- e = xcb_request_check(
- ps->c, xcb_damage_create_checked(ps->c, new->damage, w->id,
- XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
- if (e) {
- log_error_x_error(e, "Failed to create damage");
- free(e);
- free(new);
- return w;
- }
-
- // Set window event mask
- xcb_change_window_attributes(
- ps->c, new->base.id, XCB_CW_EVENT_MASK,
- (const uint32_t[]){determine_evmask(ps, new->base.id, WIN_EVMODE_FRAME)});
-
- // Get notification when the shape of a window changes
- if (ps->shape_exists) {
- xcb_shape_select_input(ps->c, new->base.id, 1);
- }
-
- new->pictfmt = x_get_pictform_for_visual(ps->c, new->a.visual);
- new->client_pictfmt = NULL;
-
- list_replace(&w->stack_neighbour, &new->base.stack_neighbour);
- struct win *replaced = NULL;
- HASH_REPLACE_INT(ps->windows, id, &new->base, replaced);
- assert(replaced == w);
- free(w);
-
- // Set all the stale flags on this new window, so it's properties will get updated
- // when it's mapped
- win_set_flags(new, WIN_FLAGS_CLIENT_STALE | WIN_FLAGS_SIZE_STALE |
- WIN_FLAGS_POSITION_STALE | WIN_FLAGS_PROPERTY_STALE |
- WIN_FLAGS_FACTOR_CHANGED);
- xcb_atom_t init_stale_props[] = {
- ps->atoms->a_NET_WM_WINDOW_TYPE, ps->atoms->a_NET_WM_WINDOW_OPACITY,
- ps->atoms->a_NET_FRAME_EXTENTS, ps->atoms->aWM_NAME,
- ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
- ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
- ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
- };
- win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
-
-#ifdef CONFIG_DBUS
- // Send D-Bus signal
- if (ps->o.dbus) {
- cdbus_ev_win_added(ps, &new->base);
- }
-#endif
- return &new->base;
-}
-
-/**
- * Set leader of a window.
- */
-static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_window_t nleader) {
- // If the leader changes
- if (w->leader != nleader) {
- xcb_window_t cache_leader_old = win_get_leader(ps, w);
-
- w->leader = nleader;
-
- // Forcefully do this to deal with the case when a child window
- // gets mapped before parent, or when the window is a waypoint
- clear_cache_win_leaders(ps);
-
- // Update the old and new window group and active_leader if the window
- // could affect their state.
- xcb_window_t cache_leader = win_get_leader(ps, w);
- if (win_is_focused_raw(ps, w) && cache_leader_old != cache_leader) {
- ps->active_leader = cache_leader;
-
- group_on_factor_change(ps, cache_leader_old);
- group_on_factor_change(ps, cache_leader);
- }
-
- // Update everything related to conditions
- win_on_factor_change(ps, w);
- }
-}
-
-/**
- * Update leader of a window.
- */
-void win_update_leader(session_t *ps, struct managed_win *w) {
- xcb_window_t leader = XCB_NONE;
-
- // Read the leader properties
- if (ps->o.detect_transient && !leader) {
- leader =
- wid_get_prop_window(ps->c, w->client_win, ps->atoms->aWM_TRANSIENT_FOR);
- }
-
- if (ps->o.detect_client_leader && !leader) {
- leader =
- wid_get_prop_window(ps->c, w->client_win, ps->atoms->aWM_CLIENT_LEADER);
- }
-
- win_set_leader(ps, w, leader);
-
- log_trace("(%#010x): client %#010x, leader %#010x, cache %#010x", w->base.id,
- w->client_win, w->leader, win_get_leader(ps, w));
-}
-
-/**
- * Internal function of win_get_leader().
- */
-static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions) {
- // Rebuild the cache if needed
- if (!w->cache_leader && (w->client_win || w->leader)) {
- // Leader defaults to client window
- if (!(w->cache_leader = w->leader))
- w->cache_leader = w->client_win;
-
- // If the leader of this window isn't itself, look for its ancestors
- if (w->cache_leader && w->cache_leader != w->client_win) {
- auto wp = find_toplevel(ps, w->cache_leader);
- if (wp) {
- // Dead loop?
- if (recursions > WIN_GET_LEADER_MAX_RECURSION)
- return XCB_NONE;
-
- w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1);
- }
- }
- }
-
- return w->cache_leader;
-}
-
-/**
- * Retrieve the <code>WM_CLASS</code> of a window and update its
- * <code>win</code> structure.
- */
-bool win_update_class(session_t *ps, struct managed_win *w) {
- char **strlst = NULL;
- int nstr = 0;
-
- // Can't do anything if there's no client window
- if (!w->client_win)
- return false;
-
- // Free and reset old strings
- free(w->class_instance);
- free(w->class_general);
- w->class_instance = NULL;
- w->class_general = NULL;
-
- // Retrieve the property string list
- if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr)) {
- return false;
- }
-
- // Copy the strings if successful
- w->class_instance = strdup(strlst[0]);
-
- if (nstr > 1) {
- w->class_general = strdup(strlst[1]);
- }
-
- free(strlst);
-
- log_trace("(%#010x): client = %#010x, "
- "instance = \"%s\", general = \"%s\"",
- w->base.id, w->client_win, w->class_instance, w->class_general);
-
- return true;
-}
-
-/**
- * Handle window focus change.
- */
-static void win_on_focus_change(session_t *ps, struct managed_win *w) {
- // If window grouping detection is enabled
- if (ps->o.track_leader) {
- xcb_window_t leader = win_get_leader(ps, w);
-
- // If the window gets focused, replace the old active_leader
- if (win_is_focused_raw(ps, w) && leader != ps->active_leader) {
- xcb_window_t active_leader_old = ps->active_leader;
-
- ps->active_leader = leader;
-
- group_on_factor_change(ps, active_leader_old);
- group_on_factor_change(ps, leader);
- }
- // If the group get unfocused, remove it from active_leader
- else if (!win_is_focused_raw(ps, w) && leader &&
- leader == ps->active_leader && !group_is_focused(ps, leader)) {
- ps->active_leader = XCB_NONE;
- group_on_factor_change(ps, leader);
- }
- }
-
- // Update everything related to conditions
- win_on_factor_change(ps, w);
-
-#ifdef CONFIG_DBUS
- // Send D-Bus signal
- if (ps->o.dbus) {
- if (win_is_focused_raw(ps, w)) {
- cdbus_ev_win_focusin(ps, &w->base);
- } else {
- cdbus_ev_win_focusout(ps, &w->base);
- }
- }
-#endif
-}
-
-/**
- * Set real focused state of a window.
- */
-void win_set_focused(session_t *ps, struct managed_win *w) {
- // Unmapped windows will have their focused state reset on map
- if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
- return;
- }
-
- if (win_is_focused_raw(ps, w)) {
- return;
- }
-
- auto old_active_win = ps->active_win;
- ps->active_win = w;
- assert(win_is_focused_raw(ps, w));
-
- if (old_active_win) {
- win_on_focus_change(ps, old_active_win);
- }
- win_on_focus_change(ps, w);
-}
-
-/**
- * Get a rectangular region a window (and possibly its shadow) occupies.
- *
- * Note w->shadow and shadow geometry must be correct before calling this
- * function.
- */
-void win_extents(const struct managed_win *w, region_t *res) {
- pixman_region32_clear(res);
- pixman_region32_union_rect(res, res, w->g.x, w->g.y, (uint)w->widthb, (uint)w->heightb);
-
- if (w->shadow) {
- assert(w->shadow_width >= 0 && w->shadow_height >= 0);
- pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx,
- w->g.y + w->shadow_dy, (uint)w->shadow_width,
- (uint)w->shadow_height);
- }
-}
-
-gen_by_val(win_extents);
-
-/**
- * Update the out-dated bounding shape of a window.
- *
- * Mark the window shape as updated
- */
-void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
- if (ps->shape_exists) {
- w->bounding_shaped = win_bounding_shaped(ps, w->base.id);
- }
-
- // We don't handle property updates of non-visible windows until they are mapped.
- assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
- w->state != WSTATE_UNMAPPING);
-
- pixman_region32_clear(&w->bounding_shape);
- // Start with the window rectangular region
- win_get_region_local(w, &w->bounding_shape);
-
- // Only request for a bounding region if the window is shaped
- // (while loop is used to avoid goto, not an actual loop)
- while (w->bounding_shaped) {
- /*
- * if window doesn't exist anymore, this will generate an error
- * as well as not generate a region.
- */
-
- xcb_shape_get_rectangles_reply_t *r = xcb_shape_get_rectangles_reply(
- ps->c,
- xcb_shape_get_rectangles(ps->c, w->base.id, XCB_SHAPE_SK_BOUNDING), NULL);
-
- if (!r) {
- break;
- }
-
- xcb_rectangle_t *xrects = xcb_shape_get_rectangles_rectangles(r);
- int nrects = xcb_shape_get_rectangles_rectangles_length(r);
- rect_t *rects = from_x_rects(nrects, xrects);
- free(r);
-
- region_t br;
- pixman_region32_init_rects(&br, rects, nrects);
- free(rects);
-
- // Add border width because we are using a different origin.
- // X thinks the top left of the inner window is the origin
- // (for the bounding shape, althought xcb_get_geometry thinks
- // the outer top left (outer means outside of the window
- // border) is the origin),
- // We think the top left of the border is the origin
- pixman_region32_translate(&br, w->g.border_width, w->g.border_width);
-
- // Intersect the bounding region we got with the window rectangle, to
- // make sure the bounding region is not bigger than the window
- // rectangle
- pixman_region32_intersect(&w->bounding_shape, &w->bounding_shape, &br);
- pixman_region32_fini(&br);
- break;
- }
-
- if (w->bounding_shaped && ps->o.detect_rounded_corners) {
- w->rounded_corners = win_has_rounded_corners(w);
- }
-
- // Window shape changed, we should free old wpaint and shadow pict
- // log_trace("free out dated pict");
- win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
- ps->pending_updates = true;
-
- free_paint(ps, &w->paint);
- free_paint(ps, &w->shadow_paint);
-
- win_on_factor_change(ps, w);
-}
-
-/**
- * Reread opacity property of a window.
- */
-void win_update_opacity_prop(session_t *ps, struct managed_win *w) {
- // get frame opacity first
- w->has_opacity_prop = wid_get_opacity_prop(ps, w->base.id, OPAQUE, &w->opacity_prop);
-
- if (w->has_opacity_prop) {
- // opacity found
- return;
- }
-
- if (ps->o.detect_client_opacity && w->client_win && w->base.id == w->client_win) {
- // checking client opacity not allowed
- return;
- }
-
- // get client opacity
- w->has_opacity_prop =
- wid_get_opacity_prop(ps, w->client_win, OPAQUE, &w->opacity_prop);
-}
-
-/**
- * Retrieve frame extents from a window.
- */
-void win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client) {
- winprop_t prop = x_get_prop(ps->c, client, ps->atoms->a_NET_FRAME_EXTENTS, 4L,
- XCB_ATOM_CARDINAL, 32);
-
- if (prop.nitems == 4) {
- int extents[4];
- for (int i = 0; i < 4; i++) {
- if (prop.c32[i] > (uint32_t)INT_MAX) {
- log_warn("Your window manager sets a absurd "
- "_NET_FRAME_EXTENTS value (%u), ignoring it.",
- prop.c32[i]);
- memset(extents, 0, sizeof(extents));
- break;
- }
- extents[i] = (int)prop.c32[i];
- }
-
- const bool changed = w->frame_extents.left != extents[0] ||
- w->frame_extents.right != extents[1] ||
- w->frame_extents.top != extents[2] ||
- w->frame_extents.bottom != extents[3];
- w->frame_extents.left = extents[0];
- w->frame_extents.right = extents[1];
- w->frame_extents.top = extents[2];
- w->frame_extents.bottom = extents[3];
-
- // If frame_opacity != 1, then frame of this window
- // is not included in reg_ignore of underneath windows
- if (ps->o.frame_opacity == 1 && changed) {
- w->reg_ignore_valid = false;
- }
- }
-
- log_trace("(%#010x): %d, %d, %d, %d", w->base.id, w->frame_extents.left,
- w->frame_extents.right, w->frame_extents.top, w->frame_extents.bottom);
-
- free_winprop(&prop);
-}
-
-bool win_is_region_ignore_valid(session_t *ps, const struct managed_win *w) {
- win_stack_foreach_managed(i, &ps->window_stack) {
- if (i == w) {
- break;
- }
- if (!i->reg_ignore_valid) {
- return false;
- }
- }
- return true;
-}
-
-/**
- * Stop listening for events on a particular window.
- */
-void win_ev_stop(session_t *ps, const struct win *w) {
- xcb_change_window_attributes(ps->c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0});
-
- if (!w->managed) {
- return;
- }
-
- auto mw = (struct managed_win *)w;
- if (mw->client_win) {
- xcb_change_window_attributes(ps->c, mw->client_win, XCB_CW_EVENT_MASK,
- (const uint32_t[]){0});
- }
-
- if (ps->shape_exists) {
- xcb_shape_select_input(ps->c, w->id, 0);
- }
-}
-
-/// Finish the unmapping of a window (e.g. after fading has finished).
-/// Doesn't free `w`
-static void unmap_win_finish(session_t *ps, struct managed_win *w) {
- w->reg_ignore_valid = false;
- w->state = WSTATE_UNMAPPED;
-
- // We are in unmap_win, this window definitely was viewable
- if (ps->backend_data) {
- // Only the pixmap needs to be freed and reacquired when mapping.
- // Shadow image can be preserved.
- if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
- win_release_pixmap(ps->backend_data, w);
- win_release_oldpixmap(ps->backend_data, w);
- }
- } else {
- assert(!w->win_image);
- assert(!w->old_win_image);
- assert(!w->shadow_image);
- }
-
- // Force animation to completed position
- w->animation_velocity_x = 0;
- w->animation_velocity_y = 0;
- w->animation_velocity_w = 0;
- w->animation_velocity_h = 0;
- w->animation_progress = 1.0;
-
- free_paint(ps, &w->paint);
- free_paint(ps, &w->shadow_paint);
-
- // Try again at binding images when the window is mapped next time
- win_clear_flags(w, WIN_FLAGS_IMAGE_ERROR);
-
- // Flag window so that it gets animated when it reapears
- // in case it wasn't destroyed
- win_set_flags(w, WIN_FLAGS_POSITION_STALE);
- win_set_flags(w, WIN_FLAGS_SIZE_STALE);
-}
-
-/// Finish the destruction of a window (e.g. after fading has finished).
-/// Frees `w`
-static void destroy_win_finish(session_t *ps, struct win *w) {
- log_trace("Trying to finish destroying (%#010x)", w->id);
-
- auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
- list_remove(&w->stack_neighbour);
-
- if (w->managed) {
- auto mw = (struct managed_win *)w;
-
- if (mw->state != WSTATE_UNMAPPED) {
- // Only UNMAPPED state has window resources freed, otherwise
- // we need to call unmap_win_finish to free them.
- // XXX actually we unmap_win_finish only frees the rendering
- // resources, we still need to call free_win_res. will fix
- // later.
- unmap_win_finish(ps, mw);
- }
-
- // Unmapping preserves the shadow image, so free it here
- if (!win_check_flags_all(mw, WIN_FLAGS_SHADOW_NONE)) {
- assert(mw->shadow_image != NULL);
- win_release_shadow(ps->backend_data, mw);
- }
-
- // Invalidate reg_ignore of windows below this one
- // TODO(yshui) what if next_w is not mapped??
- /* TODO(yshui) seriously figure out how reg_ignore behaves.
- * I think if `w` is unmapped, and destroyed after
- * paint happened at least once, w->reg_ignore_valid would
- * be true, and there is no need to invalid w->next->reg_ignore
- * when w is destroyed. */
- if (next_w) {
- rc_region_unref(&next_w->reg_ignore);
- next_w->reg_ignore_valid = false;
- }
-
- if (mw == ps->active_win) {
- // Usually, the window cannot be the focused at destruction.
- // FocusOut should be generated before the window is destroyed. We
- // do this check just to be completely sure we don't have dangling
- // references.
- log_debug("window %#010x (%s) is destroyed while being focused",
- w->id, mw->name);
- ps->active_win = NULL;
- }
-
- free_win_res(ps, mw);
-
- // Drop w from all prev_trans to avoid accessing freed memory in
- // repair_win()
- // TODO(yshui) there can only be one prev_trans pointing to w
- win_stack_foreach_managed(w2, &ps->window_stack) {
- if (mw == w2->prev_trans) {
- w2->prev_trans = NULL;
- }
- }
- }
-
- free(w);
-}
-
-static void map_win_finish(struct managed_win *w) {
- w->in_openclose = false;
- w->state = WSTATE_MAPPED;
-}
-
-/// Move window `w` so it's before `next` in the list
-static inline void restack_win(session_t *ps, struct win *w, struct list_node *next) {
- struct managed_win *mw = NULL;
- if (w->managed) {
- mw = (struct managed_win *)w;
- }
-
- if (mw) {
- // This invalidates all reg_ignore below the new stack position of `w`
- mw->reg_ignore_valid = false;
- rc_region_unref(&mw->reg_ignore);
-
- // This invalidates all reg_ignore below the old stack position of `w`
- auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
- if (next_w) {
- next_w->reg_ignore_valid = false;
- rc_region_unref(&next_w->reg_ignore);
- }
- }
-
- list_move_before(&w->stack_neighbour, next);
-
- // add damage for this window
- if (mw) {
- add_damage_from_win(ps, mw);
- }
-
-#ifdef DEBUG_RESTACK
- log_trace("Window stack modified. Current stack:");
- for (auto c = ps->list; c; c = c->next) {
- const char *desc = "";
- if (c->state == WSTATE_DESTROYING) {
- desc = "(D) ";
- }
- log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
- }
-#endif
-}
-
-/// Move window `w` so it's right above `below`
-void restack_above(session_t *ps, struct win *w, xcb_window_t below) {
- xcb_window_t old_below;
-
- if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) {
- old_below = list_next_entry(w, stack_neighbour)->id;
- } else {
- old_below = XCB_NONE;
- }
- log_debug("Restack %#010x (%s), old_below: %#010x, new_below: %#010x", w->id,
- win_get_name_if_managed(w), old_below, below);
-
- if (old_below != below) {
- struct list_node *new_next;
- if (!below) {
- new_next = &ps->window_stack;
- } else {
- struct win *tmp_w = NULL;
- HASH_FIND_INT(ps->windows, &below, tmp_w);
-
- if (!tmp_w) {
- log_error("Failed to found new below window %#010x.", below);
- return;
- }
-
- new_next = &tmp_w->stack_neighbour;
- }
- restack_win(ps, w, new_next);
- }
-}
-
-void restack_bottom(session_t *ps, struct win *w) {
- restack_above(ps, w, 0);
-}
-
-void restack_top(session_t *ps, struct win *w) {
- log_debug("Restack %#010x (%s) to top", w->id, win_get_name_if_managed(w));
- if (&w->stack_neighbour == ps->window_stack.next) {
- // already at top
- return;
- }
- restack_win(ps, w, ps->window_stack.next);
-}
-
-/// Start destroying a window. Windows cannot always be destroyed immediately
-/// because of fading and such.
-///
-/// @return whether the window has finished destroying and is freed
-bool destroy_win_start(session_t *ps, struct win *w) {
- auto mw = (struct managed_win *)w;
- assert(w);
-
- log_debug("Destroying %#010x \"%s\", managed = %d", w->id,
- (w->managed ? mw->name : NULL), w->managed);
-
- // Delete destroyed window from the hash table, even though the window might still
- // be rendered for a while. We need to make sure future window with the same
- // window id won't confuse us. Keep the window in the window stack if it's managed
- // and mapped, since we might still need to render it (e.g. fading out). Window
- // will be removed from the stack when it finishes destroying.
- HASH_DEL(ps->windows, w);
-
- if (!w->managed || mw->state == WSTATE_UNMAPPED) {
- // Window is already unmapped, or is an unmanged window, just destroy it
- destroy_win_finish(ps, w);
- return true;
- }
-
- if (w->managed) {
- // Clear IMAGES_STALE flags since the window is destroyed: Clear
- // PIXMAP_STALE as there is no pixmap available anymore, so STALE doesn't
- // make sense.
- // XXX Clear SHADOW_STALE as setting/clearing flags on a destroyed window
- // doesn't work leading to an inconsistent state where the shadow is
- // refreshed but the flags are stuck in STALE.
- // Do this before changing the window state to destroying
- win_clear_flags(mw, WIN_FLAGS_IMAGES_STALE);
-
- // If size/shape/position information is stale, win_process_update_flags
- // will update them and add the new window extents to damage. Since the
- // window has been destroyed, we cannot get the complete information at
- // this point, so we just add what we currently have to the damage.
- if (win_check_flags_any(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
- add_damage_from_win(ps, mw);
- }
-
- // Clear some flags about stale window information. Because now the window
- // is destroyed, we can't update them anyway.
- win_clear_flags(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE |
- WIN_FLAGS_PROPERTY_STALE |
- WIN_FLAGS_FACTOR_CHANGED | WIN_FLAGS_CLIENT_STALE);
-
- // Update state flags of a managed window
- mw->state = WSTATE_DESTROYING;
- mw->a.map_state = XCB_MAP_STATE_UNMAPPED;
- mw->in_openclose = true;
- }
-
- // don't need win_ev_stop because the window is gone anyway
-#ifdef CONFIG_DBUS
- // Send D-Bus signal
- if (ps->o.dbus) {
- cdbus_ev_win_destroyed(ps, w);
- }
-#endif
-
- if (!ps->redirected) {
- // Skip transition if we are not rendering
- return win_skip_fading(ps, mw);
- }
-
- return false;
-}
-
-void unmap_win_start(session_t *ps, struct managed_win *w) {
- assert(w);
- assert(w->base.managed);
- assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY);
-
- log_debug("Unmapping %#010x \"%s\"", w->base.id, w->name);
-
- if (unlikely(w->state == WSTATE_DESTROYING)) {
- log_warn("Trying to undestroy a window?");
- assert(false);
- }
-
- bool was_damaged = w->ever_damaged;
- w->ever_damaged = false;
-
- if (unlikely(w->state == WSTATE_UNMAPPING || w->state == WSTATE_UNMAPPED)) {
- if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
- // Clear the pending map as this window is now unmapped
- win_clear_flags(w, WIN_FLAGS_MAPPED);
- } else {
- log_warn("Trying to unmapping an already unmapped window %#010x "
- "\"%s\"",
- w->base.id, w->name);
- assert(false);
- }
- return;
- }
-
- // Note we don't update focused window here. This will either be
- // triggered by subsequence Focus{In, Out} event, or by recheck_focus
-
- w->a.map_state = XCB_MAP_STATE_UNMAPPED;
- w->state = WSTATE_UNMAPPING;
- w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
- w->opacity_target = win_calc_opacity_target(ps, w);
-
- if (ps->o.animations &&
- ps->o.animation_for_unmap_window != OPEN_WINDOW_ANIMATION_NONE &&
- ps->o.wintype_option[w->window_type].animation != 0)
- {
- init_animation_unmap(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_inv_og_distance =
- 1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
- w_dist * w_dist + h_dist * h_dist);
-
- if (isinf(w->animation_inv_og_distance))
- w->animation_inv_og_distance = 0;
-
- w->animation_progress = 0.0;
-
- if (w->old_win_image) {
- ps->backend_data->ops->release_image(ps->backend_data,
- w->old_win_image);
- w->old_win_image = NULL;
- }
- }
-
-#ifdef CONFIG_DBUS
- // Send D-Bus signal
- if (ps->o.dbus) {
- cdbus_ev_win_unmapped(ps, &w->base);
- }
-#endif
-
- if (!ps->redirected || !was_damaged) {
- // If we are not redirected, we skip fading because we aren't rendering
- // anything anyway.
- // If the window wasn't ever damaged, it shouldn't be painted either. But
- // a fading out window is always painted, so we have to skip fading here.
- CHECK(!win_skip_fading(ps, w));
- }
-}
-
-/**
- * Execute fade callback of a window if fading finished.
- *
- * @return whether the window is destroyed and freed
- */
-bool win_check_fade_finished(session_t *ps, struct managed_win *w) {
- if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
- // No fading in progress
- assert(w->opacity_target == w->opacity);
- return false;
- }
-
- if (w->opacity == w->opacity_target) {
- switch (w->state) {
- case WSTATE_UNMAPPING: unmap_win_finish(ps, w); return false;
- case WSTATE_DESTROYING: destroy_win_finish(ps, &w->base); return true;
- case WSTATE_MAPPING: map_win_finish(w); return false;
- case WSTATE_FADING: w->state = WSTATE_MAPPED; break;
- default: unreachable;
- }
- }
-
- return false;
-}
-
-/// Skip the current in progress fading of window,
-/// transition the window straight to its end state
-///
-/// @return whether the window is destroyed and freed
-bool win_skip_fading(session_t *ps, struct managed_win *w) {
- if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
- assert(w->opacity_target == w->opacity);
- return false;
- }
- log_debug("Skipping fading process of window %#010x (%s)", w->base.id, w->name);
- w->opacity = w->opacity_target;
-
- if (w->animation_progress < 1) {
- w->animation_progress = 1;
- w->g.x = w->pending_g.x;
- w->g.y = w->pending_g.y;
- w->g.width = w->pending_g.width;
- w->g.height = w->pending_g.height;
- }
-
- return win_check_fade_finished(ps, w);
-}
-
-/**
- * Get the Xinerama screen a window is on.
- *
- * Return an index >= 0, or -1 if not found.
- *
- * TODO(yshui) move to x.c
- * TODO(yshui) use xrandr
- */
-void win_update_screen(int nscreens, region_t *screens, struct managed_win *w) {
- w->xinerama_scr = -1;
-
- for (int i = 0; i < nscreens; i++) {
- auto e = pixman_region32_extents(&screens[i]);
- if (e->x1 <= w->g.x && e->y1 <= w->g.y && e->x2 >= w->g.x + w->widthb &&
- e->y2 >= w->g.y + w->heightb) {
- w->xinerama_scr = i;
- log_debug("Window %#010x (%s), %dx%d+%dx%d, is on screen %d "
- "(%dx%d+%dx%d)",
- w->base.id, w->name, w->g.x, w->g.y, w->widthb, w->heightb,
- i, e->x1, e->y1, e->x2 - e->x1, e->y2 - e->y1);
- return;
- }
- }
- log_debug("Window %#010x (%s), %dx%d+%dx%d, is not contained by any screen",
- w->base.id, w->name, w->g.x, w->g.y, w->g.width, w->g.height);
-}
-
-/// Map an already registered window
-void map_win_start(session_t *ps, struct managed_win *w) {
- assert(ps->server_grabbed);
- assert(w);
-
- // Don't care about window mapping if it's an InputOnly window
- // Also, try avoiding mapping a window twice
- if (w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
- return;
- }
-
- log_debug("Mapping (%#010x \"%s\")", w->base.id, w->name);
-
- assert(w->state != WSTATE_DESTROYING);
- if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_UNMAPPING) {
- log_warn("Mapping an already mapped window");
- return;
- }
-
- if (w->state == WSTATE_UNMAPPING) {
- CHECK(!win_skip_fading(ps, w));
- // We skipped the unmapping process, the window was rendered, now it is
- // not anymore. So we need to mark the then unmapping window as damaged.
- //
- // Solves problem when, for example, a window is unmapped then mapped in a
- // different location
- add_damage_from_win(ps, w);
- assert(w);
- }
-
- assert(w->state == WSTATE_UNMAPPED);
-
- // Rant: window size could change after we queried its geometry here and before
- // we get its pixmap. Later, when we get back to the event processing loop, we
- // will get the notification about size change from Xserver and try to refresh the
- // pixmap, while the pixmap is actually already up-to-date (i.e. the notification
- // is stale). There is basically no real way to prevent this, aside from grabbing
- // the server.
-
- // XXX Can we assume map_state is always viewable?
- w->a.map_state = XCB_MAP_STATE_VIEWABLE;
-
- // Update window mode here to check for ARGB windows
- w->mode = win_calc_mode(w);
-
- log_debug("Window (%#010x) has type %s", w->base.id, WINTYPES[w->window_type]);
-
- // XXX We need to make sure that win_data is available
- // iff `state` is MAPPED
- w->state = WSTATE_MAPPING;
- w->opacity_target_old = 0;
- w->opacity_target = win_calc_opacity_target(ps, w);
-
- log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
- w->opacity, w->opacity_target);
-
- // Cannot set w->ever_damaged = false here, since window mapping could be
- // delayed, so a damage event might have already arrived before this function
- // is called. But this should be unnecessary in the first place, since
- // ever_damaged is set to false in unmap_win_finish anyway.
-
- // Sets the WIN_FLAGS_IMAGES_STALE flag so later in the critical section
- // the window's image will be bound
-
- win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
-
-#ifdef CONFIG_DBUS
- // Send D-Bus signal
- if (ps->o.dbus) {
- cdbus_ev_win_mapped(ps, &w->base);
- }
-#endif
-
- if (!ps->redirected) {
- CHECK(!win_skip_fading(ps, w));
- }
-}
-
-/**
- * Update target window opacity depending on the current state.
- */
-void win_update_opacity_target(session_t *ps, struct managed_win *w) {
- auto opacity_target_old = w->opacity_target;
- w->opacity_target = win_calc_opacity_target(ps, w);
-
- if (opacity_target_old == w->opacity_target) {
- return;
- }
-
- if (w->state == WSTATE_MAPPED) {
- // Opacity target changed while MAPPED. Transition to FADING.
- assert(w->opacity == opacity_target_old);
- w->opacity_target_old = opacity_target_old;
- w->state = WSTATE_FADING;
- log_debug("Window %#010x (%s) opacity %f, opacity target %f, set "
- "old target %f",
- w->base.id, w->name, w->opacity, w->opacity_target,
- w->opacity_target_old);
- } else if (w->state == WSTATE_MAPPING) {
- // Opacity target changed while fading in.
- if (w->opacity >= w->opacity_target) {
- // Already reached new target opacity. Transition to
- // FADING.
- map_win_finish(w);
- w->opacity_target_old = fmax(opacity_target_old, w->opacity);
- w->state = WSTATE_FADING;
- log_debug("Window %#010x (%s) opacity %f already reached "
- "new opacity target %f while mapping, set old "
- "target %f",
- w->base.id, w->name, w->opacity, w->opacity_target,
- w->opacity_target_old);
- }
- } else if (w->state == WSTATE_FADING) {
- // Opacity target changed while FADING.
- if ((w->opacity < opacity_target_old && w->opacity > w->opacity_target) ||
- (w->opacity > opacity_target_old && w->opacity < w->opacity_target)) {
- // Changed while fading in and will fade out or while
- // fading out and will fade in.
- w->opacity_target_old = opacity_target_old;
- log_debug("Window %#010x (%s) opacity %f already reached "
- "new opacity target %f while fading, set "
- "old target %f",
- w->base.id, w->name, w->opacity, w->opacity_target,
- w->opacity_target_old);
- }
- }
-
- if (!ps->redirected) {
- CHECK(!win_skip_fading(ps, w));
- }
-}
-
-/**
- * Find a managed window from window id in window linked list of the session.
- */
-struct win *find_win(session_t *ps, xcb_window_t id) {
- if (!id) {
- return NULL;
- }
-
- struct win *w = NULL;
- HASH_FIND_INT(ps->windows, &id, w);
- assert(w == NULL || !w->destroyed);
- return w;
-}
-
-/**
- * Find a managed window from window id in window linked list of the session.
- */
-struct managed_win *find_managed_win(session_t *ps, xcb_window_t id) {
- struct win *w = find_win(ps, id);
- if (!w || !w->managed) {
- return NULL;
- }
-
- auto mw = (struct managed_win *)w;
- assert(mw->state != WSTATE_DESTROYING);
- return mw;
-}
-
-/**
- * Find out the WM frame of a client window using existing data.
- *
- * @param id window ID
- * @return struct win object of the found window, NULL if not found
- */
-struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) {
- if (!id) {
- return NULL;
- }
-
- HASH_ITER2(ps->windows, w) {
- assert(!w->destroyed);
- if (!w->managed) {
- continue;
- }
-
- auto mw = (struct managed_win *)w;
- if (mw->client_win == id) {
- return mw;
- }
- }
-
- return NULL;
-}
-
-/**
- * Find a managed window that is, or is a parent of `wid`.
- *
- * @param ps current session
- * @param wid window ID
- * @return struct _win object of the found window, NULL if not found
- */
-struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid) {
- // TODO(yshui) this should probably be an "update tree", then find_toplevel.
- // current approach is a bit more "racy", as the server state might be ahead of
- // our state
- struct win *w = NULL;
-
- // We traverse through its ancestors to find out the frame
- // Using find_win here because if we found a unmanaged window we know about, we
- // can stop early.
- while (wid && wid != ps->root && !(w = find_win(ps, wid))) {
- // xcb_query_tree probably fails if you run picom when X is somehow
- // initializing (like add it in .xinitrc). In this case
- // just leave it alone.
- auto reply = xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, wid), NULL);
- if (reply == NULL) {
- break;
- }
-
- wid = reply->parent;
- free(reply);
- }
-
- if (w == NULL || !w->managed) {
- return NULL;
- }
-
- return (struct managed_win *)w;
-}
-
-/**
- * Check if a rectangle includes the whole screen.
- */
-static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid, int hei) {
- return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height);
-}
-
-/**
- * Check if a window is fulscreen using EWMH
- *
- * TODO(yshui) cache this property
- */
-static inline bool
-win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) {
- xcb_get_property_cookie_t prop =
- xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL);
- if (!reply) {
- return false;
- }
-
- if (reply->length) {
- xcb_atom_t *val = xcb_get_property_value(reply);
- for (uint32_t i = 0; i < reply->length; i++) {
- if (val[i] != a->a_NET_WM_STATE_FULLSCREEN) {
- continue;
- }
- free(reply);
- return true;
- }
- }
- free(reply);
- return false;
-}
-
-/// Set flags on a window. Some sanity checks are performed
-void win_set_flags(struct managed_win *w, uint64_t flags) {
- log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
- if (unlikely(w->state == WSTATE_DESTROYING)) {
- log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
- return;
- }
-
- w->flags |= flags;
-}
-
-/// Clear flags on a window. Some sanity checks are performed
-void win_clear_flags(struct managed_win *w, uint64_t flags) {
- log_debug("Clear flags %" PRIu64 " from window %#010x (%s)", flags, w->base.id,
- w->name);
- if (unlikely(w->state == WSTATE_DESTROYING)) {
- log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
- w->name);
- return;
- }
-
- w->flags = w->flags & (~flags);
-}
-
-void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *props, int nprops) {
- const auto bits_per_element = sizeof(*w->stale_props) * 8;
- size_t new_capacity = w->stale_props_capacity;
-
- // Calculate the new capacity of the properties array
- for (int i = 0; i < nprops; i++) {
- if (props[i] >= new_capacity * bits_per_element) {
- new_capacity = props[i] / bits_per_element + 1;
- }
- }
-
- // Reallocate if necessary
- if (new_capacity > w->stale_props_capacity) {
- w->stale_props =
- realloc(w->stale_props, new_capacity * sizeof(*w->stale_props));
-
- // Clear the content of the newly allocated bytes
- memset(w->stale_props + w->stale_props_capacity, 0,
- (new_capacity - w->stale_props_capacity) * sizeof(*w->stale_props));
- w->stale_props_capacity = new_capacity;
- }
-
- // Set the property bits
- for (int i = 0; i < nprops; i++) {
- w->stale_props[props[i] / bits_per_element] |=
- 1UL << (props[i] % bits_per_element);
- }
- win_set_flags(w, WIN_FLAGS_PROPERTY_STALE);
-}
-
-static void win_clear_all_properties_stale(struct managed_win *w) {
- memset(w->stale_props, 0, w->stale_props_capacity * sizeof(*w->stale_props));
- win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE);
-}
-
-static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop) {
- const auto bits_per_element = sizeof(*w->stale_props) * 8;
- if (prop >= w->stale_props_capacity * bits_per_element) {
- return false;
- }
-
- const auto mask = 1UL << (prop % bits_per_element);
- bool ret = w->stale_props[prop / bits_per_element] & mask;
- w->stale_props[prop / bits_per_element] &= ~mask;
- return ret;
-}
-
-bool win_check_flags_any(struct managed_win *w, uint64_t flags) {
- return (w->flags & flags) != 0;
-}
-
-bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
- return (w->flags & flags) == flags;
-}
-
-/**
- * Check if a window is a fullscreen window.
- *
- * It's not using w->border_size for performance measures.
- */
-bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
- if (!ps->o.no_ewmh_fullscreen &&
- win_is_fullscreen_xcb(ps->c, ps->atoms, w->client_win)) {
- return true;
- }
- return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) &&
- (!w->bounding_shaped || w->rounded_corners);
-}
-
-/**
- * Check if a window has BYPASS_COMPOSITOR property set
- *
- * TODO(yshui) cache this property
- */
-bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w) {
- bool ret = false;
-
- auto prop = x_get_prop(ps->c, w->client_win, ps->atoms->a_NET_WM_BYPASS_COMPOSITOR,
- 1L, XCB_ATOM_CARDINAL, 32);
-
- if (prop.nitems && *prop.c32 == 1) {
- ret = true;
- }
-
- free_winprop(&prop);
- return ret;
-}
-
-/**
- * Check if a window is focused, without using any focus rules or forced focus settings
- */
-bool win_is_focused_raw(const session_t *ps, const struct managed_win *w) {
- return w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->active_win == w;
-}
-
-// Find the managed window immediately below `i` in the window stack
-struct managed_win *
-win_stack_find_next_managed(const session_t *ps, const struct list_node *i) {
- while (!list_node_is_last(&ps->window_stack, i)) {
- auto next = list_entry(i->next, struct win, stack_neighbour);
- if (next->managed) {
- return (struct managed_win *)next;
- }
- i = &next->stack_neighbour;
- }
- return NULL;
-}
-
-/// Return whether this window is mapped on the X server side
-bool win_is_mapped_in_x(const struct managed_win *w) {
- return w->state == WSTATE_MAPPING || w->state == WSTATE_FADING ||
- w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPING ||
- w->state == WSTATE_DESTROYING || (w->flags & WIN_FLAGS_MAPPED);
-}
diff --git a/src/win.h b/src/win.h
deleted file mode 100644
index d3a74f9..0000000
--- a/src/win.h
+++ /dev/null
@@ -1,531 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2011-2013, Christopher Jeffrey
-// Copyright (c) 2013 Richard Grenville <[email protected]>
-#pragma once
-#include <stdbool.h>
-#include <xcb/damage.h>
-#include <xcb/render.h>
-#include <xcb/xcb.h>
-
-#include "uthash_extra.h"
-
-// FIXME shouldn't need this
-#ifdef CONFIG_OPENGL
-#include <GL/gl.h>
-#endif
-
-#include "c2.h"
-#include "compiler.h"
-#include "list.h"
-#include "region.h"
-#include "render.h"
-#include "types.h"
-#include "utils.h"
-#include "win_defs.h"
-#include "x.h"
-
-struct backend_base;
-typedef struct session session_t;
-typedef struct _glx_texture glx_texture_t;
-
-#define win_stack_foreach_managed(w, win_stack) \
- list_foreach(struct managed_win, w, win_stack, base.stack_neighbour) if (w->base.managed)
-
-#define win_stack_foreach_managed_safe(w, win_stack) \
- list_foreach_safe(struct managed_win, w, win_stack, \
- base.stack_neighbour) if (w->base.managed)
-
-#ifdef CONFIG_OPENGL
-// FIXME this type should be in opengl.h
-// it is very unideal for it to be here
-typedef struct {
- /// Framebuffer used for blurring.
- GLuint fbo;
- /// Textures used for blurring.
- GLuint textures[2];
- /// Width of the textures.
- int width;
- /// Height of the textures.
- int height;
-} glx_blur_cache_t;
-#endif
-
-/// An entry in the window stack. May or may not correspond to a window we know about.
-struct window_stack_entry {
- struct list_node stack_neighbour;
- /// The actual window correspond to this stack entry. NULL if we didn't know about
- /// this window (e.g. an InputOnly window, or we haven't handled the window
- /// creation yet)
- struct win *win;
- /// The window id. Might not be unique in the stack, because there might be
- /// destroyed window still fading out in the stack.
- xcb_window_t id;
-};
-
-/**
- * About coordinate systems
- *
- * In general, X is the horizontal axis, Y is the vertical axis.
- * X goes from left to right, Y goes downwards.
- *
- * Global: the origin is the top left corner of the Xorg screen.
- * Local: the origin is the top left corner of the window, border is
- * considered part of the window.
- */
-
-/// Structure representing a top-level managed window.
-typedef struct win win;
-struct win {
- UT_hash_handle hh;
- struct list_node stack_neighbour;
- /// ID of the top-level frame window.
- xcb_window_t id;
- /// Whether the window is destroyed from Xorg's perspective
- bool destroyed : 1;
- /// True if we just received CreateNotify, and haven't queried X for any info
- /// about the window
- bool is_new : 1;
- /// True if this window is managed, i.e. this struct is actually a `managed_win`.
- /// Always false if `is_new` is true.
- bool managed : 1;
-};
-
-struct win_geometry {
- int16_t x;
- int16_t y;
- uint16_t width;
- uint16_t height;
- uint16_t border_width;
-};
-
-struct managed_win {
- struct win base;
- /// backend data attached to this window. Only available when
- /// `state` is not UNMAPPED
- void *win_image;
- void *old_win_image; // Old window image for interpolating window contents during animations
- void *shadow_image;
- /// Pointer to the next higher window to paint.
- struct managed_win *prev_trans;
- /// Number of windows above this window
- int stacking_rank;
- // TODO(yshui) rethink reg_ignore
-
- // Core members
- /// The "mapped state" of this window, doesn't necessary
- /// match X mapped state, because of fading.
- winstate_t state;
- /// Window attributes.
- xcb_get_window_attributes_reply_t a;
- /// The geometry of the window body, excluding the window border region.
- struct win_geometry g;
- /// Updated geometry received in events
- struct win_geometry pending_g;
- /// Xinerama screen this window is on.
- int xinerama_scr;
- /// Window visual pict format
- const xcb_render_pictforminfo_t *pictfmt;
- /// Client window visual pict format
- const xcb_render_pictforminfo_t *client_pictfmt;
- /// Window painting mode.
- winmode_t mode;
- /// Whether the window has been damaged at least once.
- bool ever_damaged;
- /// Whether the window was damaged after last paint.
- bool pixmap_damaged;
- /// Damage of the window.
- xcb_damage_damage_t damage;
- /// Paint info of the window.
- paint_t paint;
- /// bitmap for properties which needs to be updated
- uint64_t *stale_props;
- /// number of uint64_ts that has been allocated for stale_props
- uint64_t stale_props_capacity;
-
- /// Bounding shape of the window. In local coordinates.
- /// See above about coordinate systems.
- region_t bounding_shape;
- /// Window flags. Definitions above.
- uint64_t flags;
- /// The region of screen that will be obscured when windows above is painted,
- /// in global coordinates.
- /// We use this to reduce the pixels that needed to be paint when painting
- /// this window and anything underneath. Depends on window frame
- /// opacity state, window geometry, window mapped/unmapped state,
- /// window mode of the windows above. DOES NOT INCLUDE the body of THIS WINDOW.
- /// NULL means reg_ignore has not been calculated for this window.
- rc_region_t *reg_ignore;
- /// Whether the reg_ignore of all windows beneath this window are valid
- bool reg_ignore_valid;
- /// Cached width/height of the window including border.
- int widthb, heightb;
- /// Whether the window is bounding-shaped.
- bool bounding_shaped;
- /// Whether the window just have rounded corners.
- bool rounded_corners;
- /// Whether this window is to be painted.
- bool to_paint;
- /// Whether the window is painting excluded.
- bool paint_excluded;
- /// Whether the window is unredirect-if-possible excluded.
- bool unredir_if_possible_excluded;
- /// Whether this window is in open/close state.
- bool in_openclose;
- /// Whether this window was transient when animated on open
- bool animation_transient;
- /// Current position and destination, for animation
- double animation_center_x, animation_center_y;
- double animation_dest_center_x, animation_dest_center_y;
- double animation_w, animation_h;
- double animation_dest_w, animation_dest_h;
- /// Spring animation velocity
- double animation_velocity_x, animation_velocity_y;
- double animation_velocity_w, animation_velocity_h;
- /// Track animation progress; goes from 0 to 1
- double animation_progress;
- /// Inverse of the window distance at the start of animation, for
- /// tracking animation progress
- double animation_inv_og_distance;
-
- // Client window related members
- /// ID of the top-level client window of the window.
- xcb_window_t client_win;
- /// Type of the window.
- wintype_t window_type;
- /// Whether it looks like a WM window. We consider a window WM window if
- /// it does not have a decedent with WM_STATE and it is not override-
- /// redirected itself.
- bool wmwin;
- /// Leader window ID of the window.
- xcb_window_t leader;
- /// Cached topmost window ID of the window.
- xcb_window_t cache_leader;
-
- // Focus-related members
- /// Whether the window is to be considered focused.
- bool focused;
- /// Override value of window focus state. Set by D-Bus method calls.
- switch_t focused_force;
-
- // Blacklist related members
- /// Name of the window.
- char *name;
- /// Window instance class of the window.
- char *class_instance;
- /// Window general class of the window.
- char *class_general;
- /// <code>WM_WINDOW_ROLE</code> value of the window.
- char *role;
-
- // Opacity-related members
- /// Current window opacity.
- double opacity;
- /// Target window opacity.
- double opacity_target;
- /// Previous window opacity.
- double opacity_target_old;
- /// true if window (or client window, for broken window managers
- /// not transferring client window's _NET_WM_OPACITY value) has opacity prop
- bool has_opacity_prop;
- /// Cached value of opacity window attribute.
- opacity_t opacity_prop;
- /// true if opacity is set by some rules
- bool opacity_is_set;
- /// Last window opacity value set by the rules.
- double opacity_set;
-
- /// Radius of rounded window corners
- int corner_radius;
- float border_col[4];
-
- // Fading-related members
- /// Override value of window fade state. Set by D-Bus method calls.
- switch_t fade_force;
- /// Whether fading is excluded by the rules. Calculated.
- bool fade_excluded;
-
- // Frame-opacity-related members
- /// Current window frame opacity. Affected by window opacity.
- double frame_opacity;
- /// Frame extents. Acquired from _NET_FRAME_EXTENTS.
- margin_t frame_extents;
-
- // Shadow-related members
- /// Whether a window has shadow. Calculated.
- bool shadow;
- /// Override value of window shadow state. Set by D-Bus method calls.
- switch_t shadow_force;
- /// Opacity of the shadow. Affected by window opacity and frame opacity.
- double shadow_opacity;
- /// X offset of shadow. Affected by commandline argument.
- int shadow_dx;
- /// Y offset of shadow. Affected by commandline argument.
- int shadow_dy;
- /// Width of shadow. Affected by window size and commandline argument.
- int shadow_width;
- /// Height of shadow. Affected by window size and commandline argument.
- int shadow_height;
- /// Picture to render shadow. Affected by window size.
- paint_t shadow_paint;
- /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
- /// none.
- long prop_shadow;
- /// Do not paint shadow over this window.
- bool clip_shadow_above;
-
- // Dim-related members
- /// Whether the window is to be dimmed.
- bool dim;
-
- /// Whether to invert window color.
- bool invert_color;
- /// Override value of window color inversion state. Set by D-Bus method
- /// calls.
- switch_t invert_color_force;
-
- /// Whether to blur window background.
- bool blur_background;
-
-#ifdef CONFIG_OPENGL
- /// Textures and FBO background blur use.
- glx_blur_cache_t glx_blur_cache;
- /// Background texture of the window
- glx_texture_t *glx_texture_bg;
-#endif
-};
-
-/// Process pending updates/images flags on a window. Has to be called in X critical
-/// section
-void win_process_update_flags(session_t *ps, struct managed_win *w);
-void win_process_image_flags(session_t *ps, struct managed_win *w);
-/// Bind a shadow to the window, with color `c` and shadow kernel `kernel`
-bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
- struct conv *kernel);
-
-/// Start the unmap of a window. We cannot unmap immediately since we might need to fade
-/// the window out.
-void unmap_win_start(struct session *, struct managed_win *);
-
-/// Start the mapping of a window. We cannot map immediately since we might need to fade
-/// the window in.
-void map_win_start(struct session *, struct managed_win *);
-
-/// Start the destroying of a window. Windows cannot always be destroyed immediately
-/// because of fading and such.
-bool must_use destroy_win_start(session_t *ps, struct win *w);
-
-/// Release images bound with a window, set the *_NONE flags on the window. Only to be
-/// used when de-initializing the backend outside of win.c
-void win_release_images(struct backend_base *base, struct managed_win *w);
-winmode_t attr_pure win_calc_mode(const struct managed_win *w);
-void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val);
-void win_set_fade_force(struct managed_win *w, switch_t val);
-void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val);
-void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t val);
-/**
- * Set real focused state of a window.
- */
-void win_set_focused(session_t *ps, struct managed_win *w);
-bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w);
-void win_on_factor_change(session_t *ps, struct managed_win *w);
-/**
- * Update cache data in struct _win that depends on window size.
- */
-void win_on_win_size_change(session_t *ps, struct managed_win *w);
-void win_unmark_client(session_t *ps, struct managed_win *w);
-void win_recheck_client(session_t *ps, struct managed_win *w);
-
-/**
- * Calculate and return the opacity target of a window.
- *
- * The priority of opacity settings are:
- *
- * inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if set) >
- * opacity-rules (if matched) > window type default opacity > active/inactive opacity
- *
- * @param ps current session
- * @param w struct _win object representing the window
- *
- * @return target opacity
- */
-double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w);
-bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
-void win_update_screen(int nscreens, region_t *screens, struct managed_win *w);
-/**
- * Retrieve the bounding shape of a window.
- */
-// XXX was win_border_size
-void win_update_bounding_shape(session_t *ps, struct managed_win *w);
-/**
- * Check if a window has BYPASS_COMPOSITOR property set
- */
-bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w);
-/**
- * Get a rectangular region in global coordinates a window (and possibly
- * its shadow) occupies.
- *
- * Note w->shadow and shadow geometry must be correct before calling this
- * function.
- */
-void win_extents(const struct managed_win *w, region_t *res);
-region_t win_extents_by_val(const struct managed_win *w);
-/**
- * Add a window to damaged area.
- *
- * @param ps current session
- * @param w struct _win element representing the window
- */
-void add_damage_from_win(session_t *ps, const struct managed_win *w);
-/**
- * Get a rectangular region a window occupies, excluding frame and shadow.
- *
- * Return region in global coordinates.
- */
-void win_get_region_noframe_local(const struct managed_win *w, region_t *);
-void win_get_region_noframe_local_without_corners(const struct managed_win *w, region_t *);
-
-/// Get the region for the frame of the window
-void win_get_region_frame_local(const struct managed_win *w, region_t *res);
-/// Get the region for the frame of the window, by value
-region_t win_get_region_frame_local_by_val(const struct managed_win *w);
-/// Insert a new window above window with id `below`, if there is no window, add to top
-/// New window will be in unmapped state
-struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below);
-/// Insert a new win entry at the top of the stack
-struct win *add_win_top(session_t *ps, xcb_window_t id);
-/// Query the Xorg for information about window `win`
-/// `win` pointer might become invalid after this function returns
-struct win *fill_win(session_t *ps, struct win *win);
-/// Move window `w` to be right above `below`
-void restack_above(session_t *ps, struct win *w, xcb_window_t below);
-/// Move window `w` to the bottom of the stack
-void restack_bottom(session_t *ps, struct win *w);
-/// Move window `w` to the top of the stack
-void restack_top(session_t *ps, struct win *w);
-
-/**
- * Execute fade callback of a window if fading finished.
- */
-bool must_use win_check_fade_finished(session_t *ps, struct managed_win *w);
-
-// Stop receiving events (except ConfigureNotify, XXX why?) from a window
-void win_ev_stop(session_t *ps, const struct win *w);
-
-/// Skip the current in progress fading of window,
-/// transition the window straight to its end state
-///
-/// @return whether the window is destroyed and freed
-bool must_use win_skip_fading(session_t *ps, struct managed_win *w);
-/**
- * Find a managed window from window id in window linked list of the session.
- */
-struct managed_win *find_managed_win(session_t *ps, xcb_window_t id);
-struct win *find_win(session_t *ps, xcb_window_t id);
-struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
-/**
- * Find a managed window that is, or is a parent of `wid`.
- *
- * @param ps current session
- * @param wid window ID
- * @return struct _win object of the found window, NULL if not found
- */
-struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid);
-
-/**
- * Check if a window is a fullscreen window.
- *
- * It's not using w->border_size for performance measures.
- */
-bool attr_pure win_is_fullscreen(const session_t *ps, const struct managed_win *w);
-
-/**
- * Check if a window is focused, without using any focus rules or forced focus settings
- */
-bool attr_pure win_is_focused_raw(const session_t *ps, const struct managed_win *w);
-
-/// check if window has ARGB visual
-bool attr_pure win_has_alpha(const struct managed_win *w);
-
-/// check if reg_ignore_valid is true for all windows above us
-bool attr_pure win_is_region_ignore_valid(session_t *ps, const struct managed_win *w);
-
-/// Whether a given window is mapped on the X server side
-bool win_is_mapped_in_x(const struct managed_win *w);
-
-// Find the managed window immediately below `w` in the window stack
-struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps,
- const struct list_node *w);
-/// Set flags on a window. Some sanity checks are performed
-void win_set_flags(struct managed_win *w, uint64_t flags);
-/// Clear flags on a window. Some sanity checks are performed
-void win_clear_flags(struct managed_win *w, uint64_t flags);
-/// Returns true if any of the flags in `flags` is set
-bool win_check_flags_any(struct managed_win *w, uint64_t flags);
-/// Returns true if all of the flags in `flags` are set
-bool win_check_flags_all(struct managed_win *w, uint64_t flags);
-/// Mark properties as stale for a window
-void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *prop, int nprops);
-
-static inline attr_unused void win_set_property_stale(struct managed_win *w, xcb_atom_t prop) {
- return win_set_properties_stale(w, (xcb_atom_t[]){prop}, 1);
-}
-
-/// Free all resources in a struct win
-void free_win_res(session_t *ps, struct managed_win *w);
-
-static inline void win_region_remove_corners(const struct managed_win *w, region_t *res) {
- region_t corners;
- pixman_region32_init_rects(
- &corners,
- (rect_t[]){
- {.x1 = 0, .y1 = 0, .x2 = w->corner_radius, .y2 = w->corner_radius},
- {.x1 = 0, .y1 = w->heightb - w->corner_radius, .x2 = w->corner_radius, .y2 = w->heightb},
- {.x1 = w->widthb - w->corner_radius, .y1 = 0, .x2 = w->widthb, .y2 = w->corner_radius},
- {.x1 = w->widthb - w->corner_radius,
- .y1 = w->heightb - w->corner_radius,
- .x2 = w->widthb,
- .y2 = w->heightb},
- },
- 4);
- pixman_region32_subtract(res, res, &corners);
- pixman_region32_fini(&corners);
-}
-
-static inline region_t attr_unused win_get_bounding_shape_global_by_val(struct managed_win *w) {
- region_t ret;
- pixman_region32_init(&ret);
- pixman_region32_copy(&ret, &w->bounding_shape);
- pixman_region32_translate(&ret, w->g.x, w->g.y);
- return ret;
-}
-
-static inline region_t
-win_get_bounding_shape_global_without_corners_by_val(struct managed_win *w) {
- region_t ret;
- pixman_region32_init(&ret);
- pixman_region32_copy(&ret, &w->bounding_shape);
- win_region_remove_corners(w, &ret);
- pixman_region32_translate(&ret, w->g.x, w->g.y);
- return ret;
-}
-
-/**
- * Calculate the extents of the frame of the given window based on EWMH
- * _NET_FRAME_EXTENTS and the X window border width.
- */
-static inline margin_t attr_pure attr_unused win_calc_frame_extents(const struct managed_win *w) {
- margin_t result = w->frame_extents;
- result.top = max2(result.top, w->g.border_width);
- result.left = max2(result.left, w->g.border_width);
- result.bottom = max2(result.bottom, w->g.border_width);
- result.right = max2(result.right, w->g.border_width);
- return result;
-}
-
-/**
- * Check whether a window has WM frames.
- */
-static inline bool attr_pure attr_unused win_has_frame(const struct managed_win *w) {
- return w->g.border_width || w->frame_extents.top || w->frame_extents.left ||
- w->frame_extents.right || w->frame_extents.bottom;
-}
diff --git a/src/win_defs.h b/src/win_defs.h
deleted file mode 100644
index e032bc7..0000000
--- a/src/win_defs.h
+++ /dev/null
@@ -1,102 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-typedef enum {
- WINTYPE_UNKNOWN,
- WINTYPE_DESKTOP,
- WINTYPE_DOCK,
- WINTYPE_TOOLBAR,
- WINTYPE_MENU,
- WINTYPE_UTILITY,
- WINTYPE_SPLASH,
- WINTYPE_DIALOG,
- WINTYPE_NORMAL,
- WINTYPE_DROPDOWN_MENU,
- WINTYPE_POPUP_MENU,
- WINTYPE_TOOLTIP,
- WINTYPE_NOTIFICATION,
- WINTYPE_COMBO,
- WINTYPE_DND,
- NUM_WINTYPES
-} wintype_t;
-
-/// Enumeration type of window painting mode.
-typedef enum {
- WMODE_TRANS, // The window body is (potentially) transparent
- WMODE_FRAME_TRANS, // The window body is opaque, but the frame is not
- WMODE_SOLID, // The window is opaque including the frame
-} winmode_t;
-
-/// Transition table:
-/// (DESTROYED is when the win struct is destroyed and freed)
-/// ('o' means in all other cases)
-/// (Window is created in the UNMAPPED state)
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | |UNMAPPING|DESTROYING|MAPPING|FADING |UNMAPPED| MAPPED |DESTROYED|
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | UNMAPPING | o | Window |Window | - | Fading | - | - |
-/// | | |destroyed |mapped | |finished| | |
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | DESTROYING | - | o | - | - | - | - | Fading |
-/// | | | | | | | |finished |
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | MAPPING | Window | Window | o |Opacity| - | Fading | - |
-/// | |unmapped |destroyed | |change | |finished| |
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | FADING | Window | Window | - | o | - | Fading | - |
-/// | |unmapped |destroyed | | | |finished| |
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | UNMAPPED | - | - |Window | - | o | - | Window |
-/// | | | |mapped | | | |destroyed|
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-/// | MAPPED | Window | Window | - |Opacity| - | o | - |
-/// | |unmapped |destroyed | |change | | | |
-/// +-------------+---------+----------+-------+-------+--------+--------+---------+
-typedef enum {
- // The window is being faded out because it's unmapped.
- WSTATE_UNMAPPING,
- // The window is being faded out because it's destroyed,
- WSTATE_DESTROYING,
- // The window is being faded in
- WSTATE_MAPPING,
- // Window opacity is not at the target level
- WSTATE_FADING,
- // The window is mapped, no fading is in progress.
- WSTATE_MAPPED,
- // The window is unmapped, no fading is in progress.
- WSTATE_UNMAPPED,
-} winstate_t;
-
-enum win_flags {
- // Note: *_NONE flags are mostly redudant and meant for detecting logical errors
- // in the code
-
- /// pixmap is out of date, will be update in win_process_flags
- WIN_FLAGS_PIXMAP_STALE = 1,
- /// window does not have pixmap bound
- WIN_FLAGS_PIXMAP_NONE = 2,
- /// there was an error trying to bind the images
- WIN_FLAGS_IMAGE_ERROR = 4,
- /// shadow is out of date, will be updated in win_process_flags
- WIN_FLAGS_SHADOW_STALE = 8,
- /// shadow has not been generated
- WIN_FLAGS_SHADOW_NONE = 16,
- /// the client window needs to be updated
- WIN_FLAGS_CLIENT_STALE = 32,
- /// the window is mapped by X, we need to call map_win_start for it
- WIN_FLAGS_MAPPED = 64,
- /// this window has properties which needs to be updated
- WIN_FLAGS_PROPERTY_STALE = 128,
- // TODO(yshui) _maybe_ split SIZE_STALE into SIZE_STALE and SHAPE_STALE
- /// this window has an unhandled size/shape change
- WIN_FLAGS_SIZE_STALE = 256,
- /// this window has an unhandled position (i.e. x and y) change
- WIN_FLAGS_POSITION_STALE = 512,
- /// need better name for this, is set when some aspects of the window changed
- WIN_FLAGS_FACTOR_CHANGED = 1024,
-};
-
-static const uint64_t WIN_FLAGS_IMAGES_STALE =
- WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_SHADOW_STALE;
-
-#define WIN_FLAGS_IMAGES_NONE (WIN_FLAGS_PIXMAP_NONE | WIN_FLAGS_SHADOW_NONE)
diff --git a/src/x.c b/src/x.c
deleted file mode 100644
index c146f48..0000000
--- a/src/x.c
+++ /dev/null
@@ -1,748 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-#include <stdalign.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-#include <X11/Xutil.h>
-#include <pixman.h>
-#include <xcb/composite.h>
-#include <xcb/damage.h>
-#include <xcb/glx.h>
-#include <xcb/render.h>
-#include <xcb/sync.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_renderutil.h>
-#include <xcb/xfixes.h>
-
-#include "atom.h"
-#ifdef CONFIG_OPENGL
-#include "backend/gl/glx.h"
-#endif
-#include "common.h"
-#include "compiler.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-#include "utils.h"
-#include "x.h"
-
-/**
- * Get a specific attribute of a window.
- *
- * Returns a blank structure if the returned type and format does not
- * match the requested type and format.
- *
- * @param ps current session
- * @param w window
- * @param atom atom of attribute to fetch
- * @param length length to read
- * @param rtype atom of the requested type
- * @param rformat requested format
- * @return a <code>winprop_t</code> structure containing the attribute
- * and number of items. A blank one on failure.
- */
-winprop_t x_get_prop_with_offset(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom,
- int offset, int length, xcb_atom_t rtype, int rformat) {
- xcb_get_property_reply_t *r = xcb_get_property_reply(
- c,
- xcb_get_property(c, 0, w, atom, rtype, to_u32_checked(offset),
- to_u32_checked(length)),
- NULL);
-
- if (r && xcb_get_property_value_length(r) &&
- (rtype == XCB_GET_PROPERTY_TYPE_ANY || r->type == rtype) &&
- (!rformat || r->format == rformat) &&
- (r->format == 8 || r->format == 16 || r->format == 32)) {
- auto len = xcb_get_property_value_length(r);
- return (winprop_t){
- .ptr = xcb_get_property_value(r),
- .nitems = (ulong)(len / (r->format / 8)),
- .type = r->type,
- .format = r->format,
- .r = r,
- };
- }
-
- free(r);
- return (winprop_t){
- .ptr = NULL, .nitems = 0, .type = XCB_GET_PROPERTY_TYPE_ANY, .format = 0};
-}
-
-/// Get the type, format and size in bytes of a window's specific attribute.
-winprop_info_t x_get_prop_info(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom) {
- xcb_generic_error_t *e = NULL;
- auto r = xcb_get_property_reply(
- c, xcb_get_property(c, 0, w, atom, XCB_ATOM_ANY, 0, 0), &e);
- if (!r) {
- log_debug_x_error(e, "Failed to get property info for window %#010x", w);
- free(e);
- return (winprop_info_t){
- .type = XCB_GET_PROPERTY_TYPE_ANY, .format = 0, .length = 0};
- }
-
- winprop_info_t winprop_info = {
- .type = r->type, .format = r->format, .length = r->bytes_after};
- free(r);
-
- return winprop_info;
-}
-
-/**
- * Get the value of a type-<code>xcb_window_t</code> property of a window.
- *
- * @return the value if successful, 0 otherwise
- */
-xcb_window_t wid_get_prop_window(xcb_connection_t *c, xcb_window_t wid, xcb_atom_t aprop) {
- // Get the attribute
- xcb_window_t p = XCB_NONE;
- winprop_t prop = x_get_prop(c, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
-
- // Return it
- if (prop.nitems) {
- p = (xcb_window_t)*prop.p32;
- }
-
- free_winprop(&prop);
-
- return p;
-}
-
-/**
- * Get the value of a text property of a window.
- */
-bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
- int *pnstr) {
- assert(ps->server_grabbed);
- auto prop_info = x_get_prop_info(ps->c, wid, prop);
- auto type = prop_info.type;
- auto format = prop_info.format;
- auto length = prop_info.length;
-
- if (type == XCB_ATOM_NONE) {
- return false;
- }
-
- if (type != XCB_ATOM_STRING && type != ps->atoms->aUTF8_STRING &&
- type != ps->atoms->aC_STRING) {
- log_warn("Text property %d of window %#010x has unsupported type: %d",
- prop, wid, type);
- return false;
- }
-
- if (format != 8) {
- log_warn("Text property %d of window %#010x has unexpected format: %d",
- prop, wid, format);
- return false;
- }
-
- xcb_generic_error_t *e = NULL;
- auto word_count = (length + 4 - 1) / 4;
- auto r = xcb_get_property_reply(
- ps->c, xcb_get_property(ps->c, 0, wid, prop, type, 0, word_count), &e);
- if (!r) {
- log_debug_x_error(e, "Failed to get window property for %#010x", wid);
- free(e);
- return false;
- }
-
- assert(length == (uint32_t)xcb_get_property_value_length(r));
-
- void *data = xcb_get_property_value(r);
- unsigned int nstr = 0;
- uint32_t current_offset = 0;
- while (current_offset < length) {
- current_offset +=
- (uint32_t)strnlen(data + current_offset, length - current_offset) + 1;
- nstr += 1;
- }
-
- if (nstr == 0) {
- // The property is set to an empty string, in that case, we return one
- // string
- char **strlst = malloc(sizeof(char *));
- strlst[0] = "";
- *pnstr = 1;
- *pstrlst = strlst;
- free(r);
- return true;
- }
-
- // Allocate the pointers and the strings together
- void *buf = NULL;
- if (posix_memalign(&buf, alignof(char *), length + sizeof(char *) * nstr + 1) != 0) {
- abort();
- }
-
- char *strlst = buf + sizeof(char *) * nstr;
- memcpy(strlst, xcb_get_property_value(r), length);
- strlst[length] = '\0'; // X strings aren't guaranteed to be null terminated
-
- char **ret = buf;
- current_offset = 0;
- nstr = 0;
- while (current_offset < length) {
- ret[nstr] = strlst + current_offset;
- current_offset += (uint32_t)strlen(strlst + current_offset) + 1;
- nstr += 1;
- }
-
- *pnstr = to_int_checked(nstr);
- *pstrlst = ret;
- free(r);
- return true;
-}
-
-// A cache of pict formats. We assume they don't change during the lifetime
-// of this program
-static thread_local xcb_render_query_pict_formats_reply_t *g_pictfmts = NULL;
-
-static inline void x_get_server_pictfmts(xcb_connection_t *c) {
- if (g_pictfmts) {
- return;
- }
- xcb_generic_error_t *e = NULL;
- // Get window picture format
- g_pictfmts =
- xcb_render_query_pict_formats_reply(c, xcb_render_query_pict_formats(c), &e);
- if (e || !g_pictfmts) {
- log_fatal("failed to get pict formats\n");
- abort();
- }
-}
-
-const xcb_render_pictforminfo_t *
-x_get_pictform_for_visual(xcb_connection_t *c, xcb_visualid_t visual) {
- x_get_server_pictfmts(c);
-
- xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(g_pictfmts, visual);
- for (xcb_render_pictforminfo_iterator_t i =
- xcb_render_query_pict_formats_formats_iterator(g_pictfmts);
- i.rem; xcb_render_pictforminfo_next(&i)) {
- if (i.data->id == pv->format) {
- return i.data;
- }
- }
- return NULL;
-}
-
-static xcb_visualid_t attr_pure x_get_visual_for_pictfmt(xcb_render_query_pict_formats_reply_t *r,
- xcb_render_pictformat_t fmt) {
- for (auto screen = xcb_render_query_pict_formats_screens_iterator(r); screen.rem;
- xcb_render_pictscreen_next(&screen)) {
- for (auto depth = xcb_render_pictscreen_depths_iterator(screen.data);
- depth.rem; xcb_render_pictdepth_next(&depth)) {
- for (auto pv = xcb_render_pictdepth_visuals_iterator(depth.data);
- pv.rem; xcb_render_pictvisual_next(&pv)) {
- if (pv.data->format == fmt) {
- return pv.data->visual;
- }
- }
- }
- }
- return XCB_NONE;
-}
-
-xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
- x_get_server_pictfmts(c);
-
- auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
-
- return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
-}
-
-xcb_render_pictformat_t
-x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
- x_get_server_pictfmts(c);
-
- auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
-
- return pictfmt->id;
-}
-
-int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
- auto setup = xcb_get_setup(c);
- for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
- xcb_screen_next(&screen)) {
- for (auto depth = xcb_screen_allowed_depths_iterator(screen.data);
- depth.rem; xcb_depth_next(&depth)) {
- const int len = xcb_depth_visuals_length(depth.data);
- const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
- for (int i = 0; i < len; i++) {
- if (visual == visuals[i].visual_id) {
- return depth.data->depth;
- }
- }
- }
- }
- return -1;
-}
-
-xcb_render_picture_t
-x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *c,
- const xcb_render_pictforminfo_t *pictfmt,
- xcb_pixmap_t pixmap, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr) {
- void *buf = NULL;
- if (attr) {
- xcb_render_create_picture_value_list_serialize(&buf, valuemask, attr);
- if (!buf) {
- log_error("failed to serialize picture attributes");
- return XCB_NONE;
- }
- }
-
- xcb_render_picture_t tmp_picture = x_new_id(c);
- xcb_generic_error_t *e =
- xcb_request_check(c, xcb_render_create_picture_checked(
- c, tmp_picture, pixmap, pictfmt->id, valuemask, buf));
- free(buf);
- if (e) {
- log_error_x_error(e, "failed to create picture");
- return XCB_NONE;
- }
- return tmp_picture;
-}
-
-xcb_render_picture_t
-x_create_picture_with_visual_and_pixmap(xcb_connection_t *c, xcb_visualid_t visual,
- xcb_pixmap_t pixmap, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr) {
- const xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(c, visual);
- return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
-}
-
-xcb_render_picture_t
-x_create_picture_with_standard_and_pixmap(xcb_connection_t *c, xcb_pict_standard_t standard,
- xcb_pixmap_t pixmap, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr) {
- x_get_server_pictfmts(c);
-
- auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
- assert(pictfmt);
- return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
-}
-
-xcb_render_picture_t
-x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
- xcb_pict_standard_t standard, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr) {
- x_get_server_pictfmts(c);
-
- auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
- assert(pictfmt);
- return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
-}
-
-/**
- * Create an picture.
- */
-xcb_render_picture_t
-x_create_picture_with_pictfmt(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
- const xcb_render_pictforminfo_t *pictfmt, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr) {
- uint8_t depth = pictfmt->depth;
-
- xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, d, w, h);
- if (!tmp_pixmap) {
- return XCB_NONE;
- }
-
- xcb_render_picture_t picture = x_create_picture_with_pictfmt_and_pixmap(
- c, pictfmt, tmp_pixmap, valuemask, attr);
-
- xcb_free_pixmap(c, tmp_pixmap);
-
- return picture;
-}
-
-xcb_render_picture_t
-x_create_picture_with_visual(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
- xcb_visualid_t visual, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr) {
- auto pictfmt = x_get_pictform_for_visual(c, visual);
- return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
-}
-
-bool x_fetch_region(xcb_connection_t *c, xcb_xfixes_region_t r, pixman_region32_t *res) {
- xcb_generic_error_t *e = NULL;
- xcb_xfixes_fetch_region_reply_t *xr =
- xcb_xfixes_fetch_region_reply(c, xcb_xfixes_fetch_region(c, r), &e);
- if (!xr) {
- log_error_x_error(e, "Failed to fetch rectangles");
- return false;
- }
-
- int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
- auto b = ccalloc(nrect, pixman_box32_t);
- xcb_rectangle_t *xrect = xcb_xfixes_fetch_region_rectangles(xr);
- for (int i = 0; i < nrect; i++) {
- b[i] = (pixman_box32_t){.x1 = xrect[i].x,
- .y1 = xrect[i].y,
- .x2 = xrect[i].x + xrect[i].width,
- .y2 = xrect[i].y + xrect[i].height};
- }
- bool ret = pixman_region32_init_rects(res, b, nrect);
- free(b);
- free(xr);
- return ret;
-}
-
-void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict,
- int16_t clip_x_origin, int16_t clip_y_origin,
- const region_t *reg) {
- int nrects;
- const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
- auto xrects = ccalloc(nrects, xcb_rectangle_t);
- for (int i = 0; i < nrects; i++) {
- xrects[i] = (xcb_rectangle_t){
- .x = to_i16_checked(rects[i].x1),
- .y = to_i16_checked(rects[i].y1),
- .width = to_u16_checked(rects[i].x2 - rects[i].x1),
- .height = to_u16_checked(rects[i].y2 - rects[i].y1),
- };
- }
-
- xcb_generic_error_t *e = xcb_request_check(
- c, xcb_render_set_picture_clip_rectangles_checked(
- c, pict, clip_x_origin, clip_y_origin, to_u32_checked(nrects), xrects));
- if (e) {
- log_error_x_error(e, "Failed to set clip region");
- free(e);
- }
- free(xrects);
-}
-
-void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) {
- xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE};
- xcb_generic_error_t *e = xcb_request_check(
- c, xcb_render_change_picture(c, pict, XCB_RENDER_CP_CLIP_MASK, &v));
- if (e) {
- log_error_x_error(e, "failed to clear clip region");
- free(e);
- }
-}
-
-enum {
- XSyncBadCounter = 0,
- XSyncBadAlarm = 1,
- XSyncBadFence = 2,
-};
-
-/**
- * Convert a X11 error to string
- *
- * @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
- * for multiple calls to this function,
- */
-static const char *
-_x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
- session_t *const ps = ps_g;
-
- int o = 0;
- const char *name = "Unknown";
-
-#define CASESTRRET(s) \
- case s: \
- name = #s; \
- break
-
-#define CASESTRRET2(s) \
- case XCB_##s: name = #s; break
-
- // TODO(yshui) separate error code out from session_t
- o = error_code - ps->xfixes_error;
- switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
-
- o = error_code - ps->damage_error;
- switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
-
- o = error_code - ps->render_error;
- switch (o) {
- CASESTRRET2(RENDER_PICT_FORMAT);
- CASESTRRET2(RENDER_PICTURE);
- CASESTRRET2(RENDER_PICT_OP);
- CASESTRRET2(RENDER_GLYPH_SET);
- CASESTRRET2(RENDER_GLYPH);
- }
-
- if (ps->glx_exists) {
- o = error_code - ps->glx_error;
- switch (o) {
- CASESTRRET2(GLX_BAD_CONTEXT);
- CASESTRRET2(GLX_BAD_CONTEXT_STATE);
- CASESTRRET2(GLX_BAD_DRAWABLE);
- CASESTRRET2(GLX_BAD_PIXMAP);
- CASESTRRET2(GLX_BAD_CONTEXT_TAG);
- CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
- CASESTRRET2(GLX_BAD_RENDER_REQUEST);
- CASESTRRET2(GLX_BAD_LARGE_REQUEST);
- CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
- CASESTRRET2(GLX_BAD_FB_CONFIG);
- CASESTRRET2(GLX_BAD_PBUFFER);
- CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
- CASESTRRET2(GLX_BAD_WINDOW);
- CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
- }
- }
-
- if (ps->xsync_exists) {
- o = error_code - ps->xsync_error;
- switch (o) {
- CASESTRRET(XSyncBadCounter);
- CASESTRRET(XSyncBadAlarm);
- CASESTRRET(XSyncBadFence);
- }
- }
-
- switch (error_code) {
- CASESTRRET2(ACCESS);
- CASESTRRET2(ALLOC);
- CASESTRRET2(ATOM);
- CASESTRRET2(COLORMAP);
- CASESTRRET2(CURSOR);
- CASESTRRET2(DRAWABLE);
- CASESTRRET2(FONT);
- CASESTRRET2(G_CONTEXT);
- CASESTRRET2(ID_CHOICE);
- CASESTRRET2(IMPLEMENTATION);
- CASESTRRET2(LENGTH);
- CASESTRRET2(MATCH);
- CASESTRRET2(NAME);
- CASESTRRET2(PIXMAP);
- CASESTRRET2(REQUEST);
- CASESTRRET2(VALUE);
- CASESTRRET2(WINDOW);
- }
-
-#undef CASESTRRET
-#undef CASESTRRET2
-
- thread_local static char buffer[256];
- snprintf(buffer, sizeof(buffer), "X error %d %s request %d minor %d serial %lu",
- error_code, name, major, minor, serial);
- return buffer;
-}
-
-/**
- * Log a X11 error
- */
-void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
- log_debug("%s", _x_strerror(serial, major, minor, error_code));
-}
-
-/*
- * Convert a xcb_generic_error_t to a string that describes the error
- *
- * @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
- * for multiple calls to this function,
- */
-const char *x_strerror(xcb_generic_error_t *e) {
- if (!e) {
- return "No error";
- }
- return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code);
-}
-
-/**
- * Create a pixmap and check that creation succeeded.
- */
-xcb_pixmap_t x_create_pixmap(xcb_connection_t *c, uint8_t depth, xcb_drawable_t drawable,
- int width, int height) {
- xcb_pixmap_t pix = x_new_id(c);
- xcb_void_cookie_t cookie = xcb_create_pixmap_checked(
- c, depth, pix, drawable, to_u16_checked(width), to_u16_checked(height));
- xcb_generic_error_t *err = xcb_request_check(c, cookie);
- if (err == NULL) {
- return pix;
- }
-
- log_error_x_error(err, "Failed to create pixmap");
- free(err);
- return XCB_NONE;
-}
-
-/**
- * Validate a pixmap.
- *
- * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
- * are better ways.
- */
-bool x_validate_pixmap(xcb_connection_t *c, xcb_pixmap_t pixmap) {
- if (pixmap == XCB_NONE) {
- return false;
- }
-
- auto r = xcb_get_geometry_reply(c, xcb_get_geometry(c, pixmap), NULL);
- if (!r) {
- return false;
- }
-
- bool ret = r->width && r->height;
- free(r);
- return ret;
-}
-/// Names of root window properties that could point to a pixmap of
-/// background.
-static const char *background_props_str[] = {
- "_XROOTPMAP_ID",
- "_XSETROOT_ID",
- 0,
-};
-
-xcb_pixmap_t
-x_get_root_back_pixmap(xcb_connection_t *c, xcb_window_t root, struct atom *atoms) {
- xcb_pixmap_t pixmap = XCB_NONE;
-
- // Get the values of background attributes
- for (int p = 0; background_props_str[p]; p++) {
- xcb_atom_t prop_atom = get_atom(atoms, background_props_str[p]);
- winprop_t prop = x_get_prop(c, root, prop_atom, 1, XCB_ATOM_PIXMAP, 32);
- if (prop.nitems) {
- pixmap = (xcb_pixmap_t)*prop.p32;
- free_winprop(&prop);
- break;
- }
- free_winprop(&prop);
- }
-
- return pixmap;
-}
-
-bool x_is_root_back_pixmap_atom(struct atom *atoms, xcb_atom_t atom) {
- for (int p = 0; background_props_str[p]; p++) {
- xcb_atom_t prop_atom = get_atom(atoms, background_props_str[p]);
- if (prop_atom == atom) {
- return true;
- }
- }
- return false;
-}
-
-/**
- * Synchronizes a X Render drawable to ensure all pending painting requests
- * are completed.
- */
-bool x_fence_sync(xcb_connection_t *c, xcb_sync_fence_t f) {
- // TODO(richardgv): If everybody just follows the rules stated in X Sync
- // prototype, we need only one fence per screen, but let's stay a bit
- // cautious right now
-
- auto e = xcb_request_check(c, xcb_sync_trigger_fence_checked(c, f));
- if (e) {
- log_error_x_error(e, "Failed to trigger the fence");
- goto err;
- }
-
- e = xcb_request_check(c, xcb_sync_await_fence_checked(c, 1, &f));
- if (e) {
- log_error_x_error(e, "Failed to await on a fence");
- goto err;
- }
-
- e = xcb_request_check(c, xcb_sync_reset_fence_checked(c, f));
- if (e) {
- log_error_x_error(e, "Failed to reset the fence");
- goto err;
- }
- return true;
-
-err:
- free(e);
- return false;
-}
-
-// xcb-render specific macros
-#define XFIXED_TO_DOUBLE(value) (((double)(value)) / 65536)
-#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536))
-
-/**
- * Convert a struct conv to a X picture convolution filter, normalizing the kernel
- * in the process. Allow the caller to specify the element at the center of the kernel,
- * for compatibility with legacy code.
- *
- * @param[in] kernel the convolution kernel
- * @param[in] center the element to put at the center of the matrix
- * @param[inout] ret pointer to an array of `size`, if `size` is too small, more space
- * will be allocated, and `*ret` will be updated
- * @param[inout] size size of the array pointed to by `ret`, in number of elements
- * @return number of elements filled into `*ret`
- */
-void x_create_convolution_kernel(const conv *kernel, double center,
- struct x_convolution_kernel **ret) {
- assert(ret);
- if (!*ret || (*ret)->capacity < kernel->w * kernel->h + 2) {
- free(*ret);
- *ret =
- cvalloc(sizeof(struct x_convolution_kernel) +
- (size_t)(kernel->w * kernel->h + 2) * sizeof(xcb_render_fixed_t));
- (*ret)->capacity = kernel->w * kernel->h + 2;
- }
-
- (*ret)->size = kernel->w * kernel->h + 2;
-
- auto buf = (*ret)->kernel;
- buf[0] = DOUBLE_TO_XFIXED(kernel->w);
- buf[1] = DOUBLE_TO_XFIXED(kernel->h);
-
- double sum = center;
- for (int i = 0; i < kernel->w * kernel->h; i++) {
- if (i == kernel->w * kernel->h / 2) {
- continue;
- }
- sum += kernel->data[i];
- }
-
- // Note for floating points a / b != a * (1 / b), but this shouldn't have any real
- // impact on the result
- double factor = sum != 0 ? 1.0 / sum : 1;
- for (int i = 0; i < kernel->w * kernel->h; i++) {
- buf[i + 2] = DOUBLE_TO_XFIXED(kernel->data[i] * factor);
- }
-
- buf[kernel->h / 2 * kernel->w + kernel->w / 2 + 2] =
- DOUBLE_TO_XFIXED(center * factor);
-}
-
-/// Generate a search criteria for fbconfig from a X visual.
-/// Returns {-1, -1, -1, -1, -1, 0} on failure
-struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual) {
- auto pictfmt = x_get_pictform_for_visual(c, visual);
- auto depth = x_get_visual_depth(c, visual);
- if (!pictfmt || depth == -1) {
- log_error("Invalid visual %#03x", visual);
- return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
- }
- if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) {
- log_error("We cannot handle non-DirectColor visuals. Report an "
- "issue if you see this error message.");
- return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
- }
-
- int red_size = popcntul(pictfmt->direct.red_mask),
- blue_size = popcntul(pictfmt->direct.blue_mask),
- green_size = popcntul(pictfmt->direct.green_mask),
- alpha_size = popcntul(pictfmt->direct.alpha_mask);
-
- return (struct xvisual_info){
- .red_size = red_size,
- .green_size = green_size,
- .blue_size = blue_size,
- .alpha_size = alpha_size,
- .visual_depth = depth,
- .visual = visual,
- };
-}
-
-xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen) {
- xcb_screen_iterator_t iter;
-
- iter = xcb_setup_roots_iterator(xcb_get_setup(c));
- for (; iter.rem; --screen, xcb_screen_next(&iter)) {
- if (screen == 0) {
- return iter.data;
- }
- }
-
- return NULL;
-}
diff --git a/src/x.h b/src/x.h
deleted file mode 100644
index e01aa0a..0000000
--- a/src/x.h
+++ /dev/null
@@ -1,293 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// Copyright (c) 2018 Yuxuan Shui <[email protected]>
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <xcb/render.h>
-#include <xcb/sync.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_renderutil.h>
-#include <xcb/xfixes.h>
-
-#include "compiler.h"
-#include "kernel.h"
-#include "log.h"
-#include "region.h"
-
-typedef struct session session_t;
-struct atom;
-
-/// Structure representing Window property value.
-typedef struct winprop {
- union {
- void *ptr;
- int8_t *p8;
- int16_t *p16;
- int32_t *p32;
- uint32_t *c32; // 32bit cardinal
- };
- unsigned long nitems;
- xcb_atom_t type;
- int format;
-
- xcb_get_property_reply_t *r;
-} winprop_t;
-
-typedef struct winprop_info {
- xcb_atom_t type;
- uint8_t format;
- uint32_t length;
-} winprop_info_t;
-
-struct xvisual_info {
- /// Bit depth of the red component
- int red_size;
- /// Bit depth of the green component
- int green_size;
- /// Bit depth of the blue component
- int blue_size;
- /// Bit depth of the alpha component
- int alpha_size;
- /// The depth of X visual
- int visual_depth;
-
- xcb_visualid_t visual;
-};
-
-#define XCB_AWAIT_VOID(func, c, ...) \
- ({ \
- bool __success = true; \
- __auto_type __e = xcb_request_check(c, func##_checked(c, __VA_ARGS__)); \
- if (__e) { \
- x_print_error(__e->sequence, __e->major_code, __e->minor_code, \
- __e->error_code); \
- free(__e); \
- __success = false; \
- } \
- __success; \
- })
-
-#define XCB_AWAIT(func, c, ...) \
- ({ \
- xcb_generic_error_t *__e = NULL; \
- __auto_type __r = func##_reply(c, func(c, __VA_ARGS__), &__e); \
- if (__e) { \
- x_print_error(__e->sequence, __e->major_code, __e->minor_code, \
- __e->error_code); \
- free(__e); \
- } \
- __r; \
- })
-
-#define log_debug_x_error(e, fmt, ...) \
- LOG(DEBUG, fmt " (%s)", ##__VA_ARGS__, x_strerror(e))
-#define log_error_x_error(e, fmt, ...) \
- LOG(ERROR, fmt " (%s)", ##__VA_ARGS__, x_strerror(e))
-#define log_fatal_x_error(e, fmt, ...) \
- LOG(FATAL, fmt " (%s)", ##__VA_ARGS__, x_strerror(e))
-
-/// Wraps x_new_id. abort the program if x_new_id returns error
-static inline uint32_t x_new_id(xcb_connection_t *c) {
- auto ret = xcb_generate_id(c);
- if (ret == (uint32_t)-1) {
- log_fatal("We seems to have run of XIDs. This is either a bug in the X "
- "server, or a resource leakage in the compositor. Please open "
- "an issue about this problem. The compositor will die.");
- abort();
- }
- return ret;
-}
-
-/**
- * Send a request to X server and get the reply to make sure all previous
- * requests are processed, and their replies received
- *
- * xcb_get_input_focus is used here because it is the same request used by
- * libX11
- */
-static inline void x_sync(xcb_connection_t *c) {
- free(xcb_get_input_focus_reply(c, xcb_get_input_focus(c), NULL));
-}
-
-/**
- * Get a specific attribute of a window.
- *
- * Returns a blank structure if the returned type and format does not
- * match the requested type and format.
- *
- * @param ps current session
- * @param w window
- * @param atom atom of attribute to fetch
- * @param length length to read
- * @param rtype atom of the requested type
- * @param rformat requested format
- * @return a <code>winprop_t</code> structure containing the attribute
- * and number of items. A blank one on failure.
- */
-winprop_t x_get_prop_with_offset(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom,
- int offset, int length, xcb_atom_t rtype, int rformat);
-
-/**
- * Wrapper of wid_get_prop_adv().
- */
-static inline winprop_t x_get_prop(xcb_connection_t *c, xcb_window_t wid, xcb_atom_t atom,
- int length, xcb_atom_t rtype, int rformat) {
- return x_get_prop_with_offset(c, wid, atom, 0L, length, rtype, rformat);
-}
-
-/// Get the type, format and size in bytes of a window's specific attribute.
-winprop_info_t x_get_prop_info(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom);
-
-/// Discard all X events in queue or in flight. Should only be used when the server is
-/// grabbed
-static inline void x_discard_events(xcb_connection_t *c) {
- xcb_generic_event_t *e;
- while ((e = xcb_poll_for_event(c))) {
- free(e);
- }
-}
-
-/**
- * Get the value of a type-<code>xcb_window_t</code> property of a window.
- *
- * @return the value if successful, 0 otherwise
- */
-xcb_window_t wid_get_prop_window(xcb_connection_t *c, xcb_window_t wid, xcb_atom_t aprop);
-
-/**
- * Get the value of a text property of a window.
- *
- * @param[out] pstrlst Out parameter for an array of strings, caller needs to free this
- * array
- * @param[out] pnstr Number of strings in the array
- */
-bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
- int *pnstr);
-
-const xcb_render_pictforminfo_t *
-x_get_pictform_for_visual(xcb_connection_t *, xcb_visualid_t);
-int x_get_visual_depth(xcb_connection_t *, xcb_visualid_t);
-
-xcb_render_picture_t
-x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *,
- const xcb_render_pictforminfo_t *pictfmt,
- xcb_pixmap_t pixmap, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr)
- attr_nonnull(1, 2);
-
-xcb_render_picture_t
-x_create_picture_with_visual_and_pixmap(xcb_connection_t *, xcb_visualid_t visual,
- xcb_pixmap_t pixmap, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr)
- attr_nonnull(1);
-
-xcb_render_picture_t
-x_create_picture_with_standard_and_pixmap(xcb_connection_t *, xcb_pict_standard_t standard,
- xcb_pixmap_t pixmap, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr)
- attr_nonnull(1);
-
-xcb_render_picture_t
-x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
- xcb_pict_standard_t standard, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr)
- attr_nonnull(1);
-
-/**
- * Create an picture.
- */
-xcb_render_picture_t
-x_create_picture_with_pictfmt(xcb_connection_t *, xcb_drawable_t, int w, int h,
- const xcb_render_pictforminfo_t *pictfmt, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr)
- attr_nonnull(1, 5);
-
-xcb_render_picture_t
-x_create_picture_with_visual(xcb_connection_t *, xcb_drawable_t, int w, int h,
- xcb_visualid_t visual, uint32_t valuemask,
- const xcb_render_create_picture_value_list_t *attr)
- attr_nonnull(1);
-
-/// Fetch a X region and store it in a pixman region
-bool x_fetch_region(xcb_connection_t *, xcb_xfixes_region_t r, region_t *res);
-
-void x_set_picture_clip_region(xcb_connection_t *, xcb_render_picture_t, int16_t clip_x_origin,
- int16_t clip_y_origin, const region_t *);
-
-void x_clear_picture_clip_region(xcb_connection_t *, xcb_render_picture_t pict);
-
-/**
- * Log a X11 error
- */
-void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code);
-
-/*
- * Convert a xcb_generic_error_t to a string that describes the error
- *
- * @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
- * for multiple calls to this function,
- */
-const char *x_strerror(xcb_generic_error_t *e);
-
-xcb_pixmap_t x_create_pixmap(xcb_connection_t *, uint8_t depth, xcb_drawable_t drawable,
- int width, int height);
-
-bool x_validate_pixmap(xcb_connection_t *, xcb_pixmap_t pxmap);
-
-/**
- * Free a <code>winprop_t</code>.
- *
- * @param pprop pointer to the <code>winprop_t</code> to free.
- */
-static inline void free_winprop(winprop_t *pprop) {
- // Empty the whole structure to avoid possible issues
- if (pprop->r)
- free(pprop->r);
- pprop->ptr = NULL;
- pprop->r = NULL;
- pprop->nitems = 0;
-}
-
-/// Get the back pixmap of the root window
-xcb_pixmap_t
-x_get_root_back_pixmap(xcb_connection_t *c, xcb_window_t root, struct atom *atoms);
-
-/// Return true if the atom refers to a property name that is used for the
-/// root window background pixmap
-bool x_is_root_back_pixmap_atom(struct atom *atoms, xcb_atom_t atom);
-
-bool x_fence_sync(xcb_connection_t *, xcb_sync_fence_t);
-
-struct x_convolution_kernel {
- int size;
- int capacity;
- xcb_render_fixed_t kernel[];
-};
-
-/**
- * Convert a struct conv to a X picture convolution filter, normalizing the kernel
- * in the process. Allow the caller to specify the element at the center of the kernel,
- * for compatibility with legacy code.
- *
- * @param[in] kernel the convolution kernel
- * @param[in] center the element to put at the center of the matrix
- * @param[inout] ret pointer to an array of `size`, if `size` is too small, more space
- * will be allocated, and `*ret` will be updated.
- * @param[inout] size size of the array pointed to by `ret`.
- */
-void attr_nonnull(1, 3) x_create_convolution_kernel(const conv *kernel, double center,
- struct x_convolution_kernel **ret);
-
-/// Generate a search criteria for fbconfig from a X visual.
-/// Returns {-1, -1, -1, -1, -1, -1} on failure
-struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual);
-
-xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
-
-xcb_render_pictformat_t
-x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
-
-xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen);
-
-uint32_t attr_deprecated xcb_generate_id(xcb_connection_t *c);
diff --git a/src/xrescheck.c b/src/xrescheck.c
deleted file mode 100644
index 1785fc8..0000000
--- a/src/xrescheck.c
+++ /dev/null
@@ -1,65 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2014 Richard Grenville <[email protected]>
-
-#include "compiler.h"
-#include "log.h"
-
-#include "xrescheck.h"
-
-static xrc_xid_record_t *gs_xid_records = NULL;
-
-#define HASH_ADD_XID(head, xidfield, add) HASH_ADD(hh, head, xidfield, sizeof(xid), add)
-
-#define HASH_FIND_XID(head, findxid, out) HASH_FIND(hh, head, findxid, sizeof(xid), out)
-
-#define M_CPY_POS_DATA(prec) \
- prec->file = file; \
- prec->func = func; \
- prec->line = line;
-
-/**
- * @brief Add a record of given XID to the allocation table.
- */
-void xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) {
- auto prec = ccalloc(1, xrc_xid_record_t);
- prec->xid = xid;
- prec->type = type;
- M_CPY_POS_DATA(prec);
-
- HASH_ADD_XID(gs_xid_records, xid, prec);
-}
-
-/**
- * @brief Delete a record of given XID in the allocation table.
- */
-void xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) {
- xrc_xid_record_t *prec = NULL;
- HASH_FIND_XID(gs_xid_records, &xid, prec);
- if (!prec) {
- log_error("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.",
- file, line, func, xid);
- return;
- }
- HASH_DEL(gs_xid_records, prec);
- free(prec);
-}
-
-/**
- * @brief Report about issues found in the XID allocation table.
- */
-void xrc_report_xid(void) {
- for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next)
- log_trace("XRC: %s:%d %s(): %#010lx (%s) not freed.\n", prec->file,
- prec->line, prec->func, prec->xid, prec->type);
-}
-
-/**
- * @brief Clear the XID allocation table.
- */
-void xrc_clear_xid(void) {
- xrc_xid_record_t *prec = NULL, *ptmp = NULL;
- HASH_ITER(hh, gs_xid_records, prec, ptmp) {
- HASH_DEL(gs_xid_records, prec);
- free(prec);
- }
-}
diff --git a/src/xrescheck.h b/src/xrescheck.h
deleted file mode 100644
index 5ad5c46..0000000
--- a/src/xrescheck.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2014 Richard Grenville <[email protected]>
-#pragma once
-
-#include "common.h"
-#include "uthash.h"
-
-typedef struct {
- XID xid;
- const char *type;
- const char *file;
- const char *func;
- int line;
- UT_hash_handle hh;
-} xrc_xid_record_t;
-
-#define M_POS_DATA_PARAMS const char *file, int line, const char *func
-#define M_POS_DATA_PASSTHROUGH file, line, func
-#define M_POS_DATA __FILE__, __LINE__, __func__
-
-void xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS);
-
-#define xrc_add_xid(xid, type) xrc_add_xid_(xid, type, M_POS_DATA)
-
-void xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS);
-
-#define xrc_delete_xid(xid) xrc_delete_xid_(xid, M_POS_DATA)
-
-void xrc_report_xid(void);
-
-void xrc_clear_xid(void);
-
-// Pixmap
-
-static inline void xcb_create_pixmap_(xcb_connection_t *c, uint8_t depth,
- xcb_pixmap_t pixmap, xcb_drawable_t drawable,
- uint16_t width, uint16_t height, M_POS_DATA_PARAMS) {
- xcb_create_pixmap(c, depth, pixmap, drawable, width, height);
- xrc_add_xid_(pixmap, "Pixmap", M_POS_DATA_PASSTHROUGH);
-}
-
-#define xcb_create_pixmap(c, depth, pixmap, drawable, width, height) \
- xcb_create_pixmap_(c, depth, pixmap, drawable, width, height, M_POS_DATA)
-
-static inline xcb_void_cookie_t
-xcb_composite_name_window_pixmap_(xcb_connection_t *c, xcb_window_t window,
- xcb_pixmap_t pixmap, M_POS_DATA_PARAMS) {
- xcb_void_cookie_t ret = xcb_composite_name_window_pixmap(c, window, pixmap);
- xrc_add_xid_(pixmap, "PixmapC", M_POS_DATA_PASSTHROUGH);
- return ret;
-}
-
-#define xcb_composite_name_window_pixmap(dpy, window, pixmap) \
- xcb_composite_name_window_pixmap_(dpy, window, pixmap, M_POS_DATA)
-
-static inline void
-xcb_free_pixmap_(xcb_connection_t *c, xcb_pixmap_t pixmap, M_POS_DATA_PARAMS) {
- xcb_free_pixmap(c, pixmap);
- xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH);
-}
-
-#define xcb_free_pixmap(c, pixmap) xcb_free_pixmap_(c, pixmap, M_POS_DATA);