From 959f400ae144c613ec63e1f4ad228889583bd20e Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 24 Mar 2024 02:02:14 +0000 Subject: [PATCH] WIP core: struct sesion is now opaque --- src/backend/backend.c | 249 ++++++----- src/backend/backend_common.c | 5 +- src/backend/driver.c | 2 +- src/backend/dummy/dummy.c | 4 +- src/backend/gl/egl.c | 9 +- src/backend/gl/gl_common.c | 9 +- src/backend/gl/glx.c | 31 +- src/backend/xrender/xrender.c | 45 +- src/common.h | 257 +---------- src/config.c | 11 +- src/dbus.c | 165 ++++---- src/diagnostic.c | 12 +- src/event.c | 243 ++++++----- src/opengl.c | 204 ++++----- src/opengl.h | 13 +- src/picom.c | 776 +++++++++++++++++++++++++++++++++- src/picom.h | 148 +++++-- src/render.c | 618 ++++++++++++++------------- src/vsync.c | 21 +- src/win.c | 662 +++++++++++------------------ src/win.h | 30 +- src/x.c | 17 +- 22 files changed, 1983 insertions(+), 1548 deletions(-) diff --git a/src/backend/backend.c b/src/backend/backend.c index 34e83e07..f93c49d7 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -9,6 +9,7 @@ #include "compiler.h" #include "config.h" #include "log.h" +#include "picom.h" #include "region.h" #include "transition.h" #include "types.h" @@ -35,15 +36,17 @@ struct backend_operations *backend_list[NUM_BKEND] = { */ 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; + auto backend_data = session_get_backend_data(ps); + auto buffer_age_fn = backend_data->ops->buffer_age; + int buffer_age = buffer_age_fn ? buffer_age_fn(backend_data) : -1; if (all_damage) { buffer_age = -1; } pixman_region32_init(®ion); - damage_ring_collect(&ps->damage_ring, &ps->screen_reg, ®ion, buffer_age); + damage_ring_collect(session_get_damage_ring(ps), session_get_screen_reg(ps), + ®ion, buffer_age); return region; } @@ -69,7 +72,7 @@ void handle_device_reset(session_t *ps) { // Reset picom log_info("Resetting picom after device reset"); - ev_break(ps->loop, EVBREAK_ALL); + ev_break(session_get_mainloop(ps), EVBREAK_ALL); } /// paint all windows @@ -78,34 +81,31 @@ void handle_device_reset(session_t *ps) { /// this function will return false. bool paint_all_new(session_t *ps, struct managed_win *const t) { struct timespec now = get_time_timespec(); + uint64_t after_sync_fence_us = 0, after_damage_us = 0; auto paint_all_start_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; - if (ps->backend_data->ops->device_status && - ps->backend_data->ops->device_status(ps->backend_data) != DEVICE_STATUS_NORMAL) { + auto backend_data = session_get_backend_data(ps); + auto backend_blur_context = session_get_backend_blur_context(ps); + auto options = session_get_options(ps); + if (backend_data->ops->device_status && + backend_data->ops->device_status(backend_data) != DEVICE_STATUS_NORMAL) { handle_device_reset(ps); return false; } - 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.c, ps->sync_fence); - ps->sync_fence = XCB_NONE; - ps->o.xrender_sync_fence = false; - ps->xsync_exists = false; - } - } + session_xsync_wait_fence(ps); - now = get_time_timespec(); - auto after_sync_fence_us = - (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; - log_trace("Time spent on sync fence: %" PRIu64 " us", - after_sync_fence_us - paint_all_start_us); + if (unlikely(log_get_level_tls() > LOG_LEVEL_TRACE)) { + now = get_time_timespec(); + after_sync_fence_us = + (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; + log_trace("Time spent on sync fence: %" PRIu64 " us", + after_sync_fence_us - paint_all_start_us); + } // 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; - reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage); + reg_damage = get_damage(ps, options->monitor_repaint || !options->use_damage); if (!pixman_region32_not_empty(®_damage)) { pixman_region32_fini(®_damage); @@ -127,11 +127,12 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { /// 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) { + region_t *screen_reg = session_get_screen_reg(ps); + assert(options->blur_method != BLUR_METHOD_INVALID); + if (options->blur_method != BLUR_METHOD_NONE && 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); + backend_data->ops->get_blur_size(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 @@ -155,8 +156,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { blur_height * resize_factor); reg_paint = resize_region(®_damage, blur_width * resize_factor, blur_height * resize_factor); - pixman_region32_intersect(®_paint, ®_paint, &ps->screen_reg); - pixman_region32_intersect(®_damage, ®_damage, &ps->screen_reg); + pixman_region32_intersect(®_paint, ®_paint, screen_reg); + pixman_region32_intersect(®_damage, ®_damage, screen_reg); } else { pixman_region32_init(®_paint); pixman_region32_copy(®_paint, ®_damage); @@ -166,8 +167,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { // backend can optimize based on this info region_t reg_visible; pixman_region32_init(®_visible); - pixman_region32_copy(®_visible, &ps->screen_reg); - if (t && !ps->o.transparent_clipping) { + pixman_region32_copy(®_visible, screen_reg); + if (t && !options->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 // @@ -181,31 +182,25 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { region_t reg_shadow_clip; pixman_region32_init(®_shadow_clip); - now = get_time_timespec(); - auto after_damage_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; - log_trace("Getting damage took %" PRIu64 " us", after_damage_us - after_sync_fence_us); - if (ps->next_render > 0) { - log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %" PRIu64, - labs((long)after_damage_us - (long)ps->next_render), - after_damage_us < ps->next_render ? "early" : "late", - after_damage_us, ps->next_render); - ps->last_schedule_delay = 0; - if (after_damage_us > ps->next_render) { - ps->last_schedule_delay = after_damage_us - ps->next_render; - } + if (unlikely(log_get_level_tls() > LOG_LEVEL_TRACE)) { + now = get_time_timespec(); + after_damage_us = + (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; + log_trace("Getting damage took %" PRIu64 " us", + after_damage_us - after_sync_fence_us); + } + session_record_cpu_time(ps); + + if (backend_data->ops->prepare) { + backend_data->ops->prepare(backend_data, ®_paint); } - if (ps->backend_data->ops->prepare) { - ps->backend_data->ops->prepare(ps->backend_data, ®_paint); - } - - if (ps->root_image) { - ps->backend_data->ops->compose(ps->backend_data, ps->root_image, - (coord_t){0}, NULL, (coord_t){0}, - ®_paint, ®_visible); + auto root_image = session_get_root_image(ps); + if (root_image) { + backend_data->ops->compose(backend_data, root_image, (coord_t){0}, NULL, + (coord_t){0}, ®_paint, ®_visible); } else { - ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1}, - ®_paint); + backend_data->ops->fill(backend_data, (struct color){0, 0, 0, 1}, ®_paint); } // Windows are sorted from bottom to top @@ -214,7 +209,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { // // Whether this is beneficial is to be determined XXX for (struct managed_win *w = t; w; w = w->prev_trans) { - pixman_region32_subtract(®_visible, &ps->screen_reg, w->reg_ignore); + pixman_region32_subtract(®_visible, 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)); @@ -226,7 +221,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { win_get_bounding_shape_global_without_corners_by_val(w); if (!w->mask_image && (w->bounding_shaped || w->corner_radius != 0)) { - win_bind_mask(ps->backend_data, w); + win_bind_mask(backend_data, w); } // The clip region for the current window, in global/target coordinates @@ -234,7 +229,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { region_t reg_paint_in_bound; pixman_region32_init(®_paint_in_bound); pixman_region32_intersect(®_paint_in_bound, ®_bound, ®_paint); - if (ps->o.transparent_clipping) { + if (options->transparent_clipping) { // // If transparent_clipping is enabled, we need to be SURE that // things are not drawn inside reg_ignore, because otherwise they @@ -256,8 +251,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { const double window_opacity = animatable_get(&w->opacity); 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))) { + (options->force_win_blend || real_win_mode == WMODE_TRANS || + (options->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. @@ -267,32 +262,32 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { if (blur_opacity * MAX_ALPHA < 1) { // We don't need to blur if it would be completely // transparent - } else if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) { + } else if (real_win_mode == WMODE_TRANS || options->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, w->mask_image, window_coord, - ®_paint_in_bound, ®_visible); + backend_data->ops->blur(backend_data, blur_opacity, + backend_blur_context, + w->mask_image, window_coord, + ®_paint_in_bound, ®_visible); } else { // Window itself is solid, we only need to blur the frame // region // Readability assertions - assert(ps->o.blur_background_frame); + assert(options->blur_background_frame); assert(real_win_mode == WMODE_FRAME_TRANS); auto reg_blur = win_get_region_frame_local_by_val(w); pixman_region32_translate(®_blur, w->g.x, w->g.y); // make sure reg_blur \in reg_paint pixman_region32_intersect(®_blur, ®_blur, ®_paint); - if (ps->o.transparent_clipping) { + if (options->transparent_clipping) { // ref: pixman_region32_intersect(®_blur, ®_blur, ®_visible); } - ps->backend_data->ops->blur( - ps->backend_data, blur_opacity, ps->backend_blur_context, + backend_data->ops->blur( + backend_data, blur_opacity, backend_blur_context, w->mask_image, window_coord, ®_blur, ®_visible); pixman_region32_fini(®_blur); } @@ -301,11 +296,12 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { // The win_bind_shadow function must be called before checking if a window // has shadow enabled because it disables shadow for a window on failure. if (w->shadow && !w->shadow_image) { - struct color shadow_color = {.red = ps->o.shadow_red, - .green = ps->o.shadow_green, - .blue = ps->o.shadow_blue, - .alpha = ps->o.shadow_opacity}; - win_bind_shadow(ps->backend_data, w, shadow_color, ps->shadow_context); + struct color shadow_color = {.red = options->shadow_red, + .green = options->shadow_green, + .blue = options->shadow_blue, + .alpha = options->shadow_opacity}; + win_bind_shadow(backend_data, w, shadow_color, + session_get_backend_shadow_context(ps)); } // Draw shadow on target @@ -316,17 +312,19 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { pixman_region32_intersect(®_shadow, ®_shadow, ®_paint); // Mask out the region we don't want shadow on - if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) { + auto shadow_exclude_reg = session_get_shadow_exclude_reg(ps); + if (pixman_region32_not_empty(shadow_exclude_reg)) { pixman_region32_subtract(®_shadow, ®_shadow, - &ps->shadow_exclude_reg); + shadow_exclude_reg); } if (pixman_region32_not_empty(®_shadow_clip)) { pixman_region32_subtract(®_shadow, ®_shadow, ®_shadow_clip); } - if (ps->o.crop_shadow_to_monitor && w->randr_monitor >= 0 && - w->randr_monitor < ps->monitors.count) { + auto monitors = session_get_monitors(ps); + if (options->crop_shadow_to_monitor && w->randr_monitor >= 0 && + w->randr_monitor < monitors->count) { // There can be a window where number of monitors is // updated, but the monitor number attached to the window // have not. @@ -334,42 +332,40 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { // Window monitor number will be updated eventually, so // here we just check to make sure we don't access out of // bounds. - pixman_region32_intersect( - ®_shadow, ®_shadow, - &ps->monitors.regions[w->randr_monitor]); + pixman_region32_intersect(®_shadow, ®_shadow, + &monitors->regions[w->randr_monitor]); } - if (ps->o.transparent_clipping) { + if (options->transparent_clipping) { // ref: pixman_region32_intersect(®_shadow, ®_shadow, ®_visible); } assert(w->shadow_image); - ps->backend_data->ops->set_image_property( - ps->backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image, - &w->opacity); + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image, &w->opacity); coord_t shadow_coord = {.x = w->g.x + w->shadow_dx, .y = w->g.y + w->shadow_dy}; auto inverted_mask = NULL; - if (!ps->o.wintype_option[w->window_type].full_shadow) { + if (!options->wintype_option[w->window_type].full_shadow) { pixman_region32_subtract(®_shadow, ®_shadow, ®_bound_no_corner); if (w->mask_image) { inverted_mask = w->mask_image; - ps->backend_data->ops->set_image_property( - ps->backend_data, IMAGE_PROPERTY_INVERTED, + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_INVERTED, inverted_mask, (bool[]){true}); } } - ps->backend_data->ops->compose( - ps->backend_data, w->shadow_image, shadow_coord, - inverted_mask, window_coord, ®_shadow, ®_visible); + backend_data->ops->compose(backend_data, w->shadow_image, + shadow_coord, inverted_mask, + window_coord, ®_shadow, ®_visible); if (inverted_mask) { - ps->backend_data->ops->set_image_property( - ps->backend_data, IMAGE_PROPERTY_INVERTED, - inverted_mask, (bool[]){false}); + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_INVERTED, inverted_mask, + (bool[]){false}); } pixman_region32_fini(®_shadow); } @@ -378,26 +374,25 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { { double dim_opacity = 0.0; if (w->dim) { - dim_opacity = ps->o.inactive_dim; - if (!ps->o.inactive_dim_fixed) { + dim_opacity = options->inactive_dim; + if (!options->inactive_dim_fixed) { dim_opacity *= window_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, + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image, + &options->max_brightness); + backend_data->ops->set_image_property( + 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, - &window_opacity); - ps->backend_data->ops->set_image_property( - ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image, + backend_data->ops->set_image_property(backend_data, + IMAGE_PROPERTY_DIM_LEVEL, + w->win_image, &dim_opacity); + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, &window_opacity); + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image, (double[]){w->corner_radius}); if (w->corner_radius) { int border_width = w->g.border_width; @@ -407,13 +402,13 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { w->frame_extents.right, w->frame_extents.bottom); } - ps->backend_data->ops->set_image_property( - ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH, + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_BORDER_WIDTH, w->win_image, &border_width); } - ps->backend_data->ops->set_image_property( - ps->backend_data, IMAGE_PROPERTY_CUSTOM_SHADER, w->win_image, + backend_data->ops->set_image_property( + backend_data, IMAGE_PROPERTY_CUSTOM_SHADER, w->win_image, w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL); } @@ -433,9 +428,9 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { // Draw window on target if (w->frame_opacity == 1) { - ps->backend_data->ops->compose(ps->backend_data, w->win_image, - window_coord, NULL, window_coord, - ®_paint_in_bound, ®_visible); + backend_data->ops->compose(backend_data, w->win_image, + window_coord, NULL, window_coord, + ®_paint_in_bound, ®_visible); } else { // For window image processing, we don't have to limit the process // region to damage for correctness. (see for @@ -466,17 +461,17 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { ®_visible_local, ®_visible_local, ®_bound_local); } - auto new_img = ps->backend_data->ops->clone_image( - ps->backend_data, w->win_image, ®_visible_local); + auto new_img = backend_data->ops->clone_image( + backend_data, w->win_image, ®_visible_local); auto reg_frame = win_get_region_frame_local_by_val(w); - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, ®_frame, - ®_visible_local, (double[]){w->frame_opacity}); + backend_data->ops->image_op(backend_data, IMAGE_OP_APPLY_ALPHA, + new_img, ®_frame, ®_visible_local, + (double[]){w->frame_opacity}); pixman_region32_fini(®_frame); - ps->backend_data->ops->compose(ps->backend_data, new_img, - window_coord, NULL, window_coord, - ®_paint_in_bound, ®_visible); - ps->backend_data->ops->release_image(ps->backend_data, new_img); + backend_data->ops->compose(backend_data, new_img, window_coord, + NULL, window_coord, + ®_paint_in_bound, ®_visible); + backend_data->ops->release_image(backend_data, new_img); pixman_region32_fini(®_visible_local); pixman_region32_fini(®_bound_local); } @@ -488,20 +483,20 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) { pixman_region32_fini(®_paint); pixman_region32_fini(®_shadow_clip); - if (ps->o.monitor_repaint) { + if (options->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, ®_damage_debug); + backend_data->ops->fill(backend_data, DEBUG_COLOR, ®_damage_debug); pixman_region32_fini(®_damage_debug); } // Move the head of the damage ring - damage_ring_advance(&ps->damage_ring); + damage_ring_advance(session_get_damage_ring(ps)); - if (ps->backend_data->ops->present) { + if (backend_data->ops->present) { // Present the rendered scene // Vsync is done here - ps->backend_data->ops->present(ps->backend_data, ®_damage); + backend_data->ops->present(backend_data, ®_damage); } pixman_region32_fini(®_damage); diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c index adc14152..9f0b6cac 100644 --- a/src/backend/backend_common.c +++ b/src/backend/backend_common.c @@ -12,6 +12,7 @@ #include "config.h" #include "kernel.h" #include "log.h" +#include "picom.h" #include "utils.h" #include "win.h" #include "x.h" @@ -508,8 +509,8 @@ struct backend_image *default_new_backend_image(int w, int h) { } void init_backend_base(struct backend_base *base, session_t *ps) { - base->c = &ps->c; - base->loop = ps->loop; + base->c = session_get_x_connection(ps); + base->loop = session_get_mainloop(ps); base->busy = false; base->ops = NULL; } diff --git a/src/backend/driver.c b/src/backend/driver.c index 99ea4d7a..206992d1 100644 --- a/src/backend/driver.c +++ b/src/backend/driver.c @@ -15,7 +15,7 @@ /// 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) { - ps->o.xrender_sync_fence = true; + session_get_options(ps)->xrender_sync_fence = true; } } diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c index f43729dd..cacb151a 100644 --- a/src/backend/dummy/dummy.c +++ b/src/backend/dummy/dummy.c @@ -30,9 +30,7 @@ struct dummy_data { struct backend_base *dummy_init(session_t *ps attr_unused, xcb_window_t target attr_unused) { auto ret = (struct backend_base *)ccalloc(1, struct dummy_data); - ret->c = &ps->c; - ret->loop = ps->loop; - ret->busy = false; + init_backend_base(ret, ps); return ret; } diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 981aedfe..a6c2547f 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -136,10 +136,11 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) { } gd = ccalloc(1, struct egl_data); - gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, ps->c.dpy, + auto c = session_get_x_connection(ps); + gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, c->dpy, (EGLint[]){ EGL_PLATFORM_X11_SCREEN_EXT, - ps->c.screen, + c->screen, EGL_NONE, }); if (gd->display == EGL_NO_DISPLAY) { @@ -172,7 +173,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) { goto end; } - auto visual_info = x_get_visual_info(&ps->c, ps->c.screen_info->root_visual); + auto visual_info = x_get_visual_info(c, c->screen_info->root_visual); EGLConfig config = NULL; int nconfigs = 1; // clang-format off @@ -228,7 +229,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) { gd->gl.decouple_texture_user_data = egl_decouple_user_data; gd->gl.release_user_data = egl_release_image; - if (ps->o.vsync) { + if (session_get_options(ps)->vsync) { if (!egl_set_swap_interval(1, gd->display)) { log_error("Failed to enable vsync. %#x", eglGetError()); } diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 3ef9b30e..280b3c7b 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -13,6 +13,7 @@ #include "config.h" #include "kernel.h" #include "log.h" +#include "picom.h" #include "region.h" #include "string_utils.h" #include "types.h" @@ -623,7 +624,8 @@ static bool gl_win_shader_from_stringv(const char **vshader_strv, void gl_root_change(backend_t *base, session_t *ps) { auto gd = (struct gl_data *)base; - gl_resize(gd, ps->root_width, ps->root_height); + auto root_extent = session_get_root_extent(ps); + gl_resize(gd, root_extent.width, root_extent.height); } /** @@ -889,7 +891,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); glUseProgram(0); - gd->dithered_present = ps->o.dithered_present; + gd->dithered_present = session_get_options(ps)->dithered_present; gd->dummy_prog = gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL}, (const char *[]){dummy_frag, NULL}); @@ -949,8 +951,9 @@ bool gl_init(struct gl_data *gd, session_t *ps) { const GLint *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16} : (const GLint[]){GL_RGB8, GL_RGBA8}; for (int i = 0; i < 2; i++) { + auto root_extent = session_get_root_extent(ps); gd->back_format = format[i]; - gl_resize(gd, ps->root_width, ps->root_height); + gl_resize(gd, root_extent.width, root_extent.height); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gd->back_texture, 0); diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 4201a492..ea095da7 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -245,7 +245,8 @@ static bool glx_set_swap_interval(int interval, Display *dpy, GLXDrawable drawab */ static backend_t *glx_init(session_t *ps, xcb_window_t target) { bool success = false; - glxext_init(ps->c.dpy, ps->c.screen); + auto c = session_get_x_connection(ps); + glxext_init(c->dpy, c->screen); auto gd = ccalloc(1, struct _glx_data); init_backend_base(&gd->gl.base, ps); @@ -254,15 +255,15 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { XVisualInfo *pvis = NULL; // Check for GLX extension - if (!ps->glx_exists) { + if (!session_has_glx_extension(ps)) { log_error("No GLX extension."); goto end; } // Get XVisualInfo int nitems = 0; - XVisualInfo vreq = {.visualid = ps->c.screen_info->root_visual}; - pvis = XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems); + XVisualInfo vreq = {.visualid = c->screen_info->root_visual}; + pvis = XGetVisualInfo(c->dpy, VisualIDMask, &vreq, &nitems); if (!pvis) { log_error("Failed to acquire XVisualInfo for current visual."); goto end; @@ -270,22 +271,22 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { // Ensure the visual is double-buffered int value = 0; - if (glXGetConfig(ps->c.dpy, pvis, GLX_USE_GL, &value) || !value) { + if (glXGetConfig(c->dpy, pvis, GLX_USE_GL, &value) || !value) { log_error("Root visual is not a GL visual."); goto end; } - if (glXGetConfig(ps->c.dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) { + if (glXGetConfig(c->dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) { log_error("Root visual lacks stencil buffer."); goto end; } - if (glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) { + if (glXGetConfig(c->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) { log_error("Root visual is not a double buffered GL visual."); goto end; } - if (glXGetConfig(ps->c.dpy, pvis, GLX_RGBA, &value) || !value) { + if (glXGetConfig(c->dpy, pvis, GLX_RGBA, &value) || !value) { log_error("Root visual is a color index visual, not supported"); goto end; } @@ -303,11 +304,11 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { // 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(ps->c.dpy, ps->c.screen, &ncfgs); + GLXFBConfig *cfg = glXGetFBConfigs(c->dpy, c->screen, &ncfgs); bool found = false; for (int i = 0; i < ncfgs; i++) { int visualid; - glXGetFBConfigAttribChecked(ps->c.dpy, cfg[i], GLX_VISUAL_ID, &visualid); + glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_VISUAL_ID, &visualid); if ((VisualID)visualid != pvis->visualid) { continue; } @@ -326,7 +327,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { attributes[7] = GLX_LOSE_CONTEXT_ON_RESET_ARB; } - gd->ctx = glXCreateContextAttribsARB(ps->c.dpy, cfg[i], 0, true, attributes); + gd->ctx = glXCreateContextAttribsARB(c->dpy, cfg[i], 0, true, attributes); free(cfg); if (!gd->ctx) { @@ -344,7 +345,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { // Attach GLX context GLXDrawable tgt = gd->target_win; - if (!glXMakeCurrent(ps->c.dpy, tgt, gd->ctx)) { + if (!glXMakeCurrent(c->dpy, tgt, gd->ctx)) { log_error("Failed to attach GLX context."); goto end; } @@ -357,12 +358,12 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { 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->c.dpy, tgt)) { + if (session_get_options(ps)->vsync) { + if (!glx_set_swap_interval(1, c->dpy, tgt)) { log_error("Failed to enable vsync."); } } else { - glx_set_swap_interval(0, ps->c.dpy, tgt); + glx_set_swap_interval(0, c->dpy, tgt); } success = true; diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 4b961564..04465ee2 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -893,7 +893,9 @@ static void xrender_get_blur_size(void *blur_context, int *width, int *height) { } static backend_t *xrender_init(session_t *ps, xcb_window_t target) { - if (ps->o.dithered_present) { + auto c = session_get_x_connection(ps); + auto options = session_get_options(ps); + if (options->dithered_present) { log_warn("\"dithered-present\" is not supported by the xrender backend."); } @@ -902,30 +904,29 @@ static backend_t *xrender_init(session_t *ps, xcb_window_t target) { for (int i = 0; i <= MAX_ALPHA; ++i) { double o = (double)i / (double)MAX_ALPHA; - xd->alpha_pict[i] = solid_picture(&ps->c, false, o, 0, 0, 0); + xd->alpha_pict[i] = solid_picture(c, 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->black_pixel = solid_picture(&ps->c, true, 1, 0, 0, 0); - xd->white_pixel = solid_picture(&ps->c, true, 1, 1, 1, 1); + auto root_extent = session_get_root_extent(ps); + xd->target_width = root_extent.width; + xd->target_height = root_extent.height; + xd->black_pixel = solid_picture(c, true, 1, 0, 0, 0); + xd->white_pixel = solid_picture(c, true, 1, 1, 1, 1); xd->target_win = target; 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->c.screen_info->root_visual, xd->target_win, - XCB_RENDER_CP_SUBWINDOW_MODE, &pa); + c, c->screen_info->root_visual, xd->target_win, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); - xd->vsync = ps->o.vsync; - if (ps->present_exists) { - auto eid = x_new_id(&ps->c); - auto e = - xcb_request_check(ps->c.c, xcb_present_select_input_checked( - ps->c.c, eid, xd->target_win, - XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY)); + xd->vsync = options->vsync; + if (session_has_present_extension(ps)) { + auto eid = x_new_id(c); + auto e = xcb_request_check(c->c, xcb_present_select_input_checked( + c->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; @@ -933,7 +934,7 @@ static backend_t *xrender_init(session_t *ps, xcb_window_t target) { } xd->present_event = - xcb_register_for_special_xge(ps->c.c, &xcb_present_id, eid, NULL); + xcb_register_for_special_xge(c->c, &xcb_present_id, eid, NULL); if (!xd->present_event) { log_error("Cannot register for special XGE, vsync will be " "disabled"); @@ -944,22 +945,22 @@ static backend_t *xrender_init(session_t *ps, xcb_window_t target) { } if (xd->vsync) { - xd->present_region = x_create_region(&ps->c, &ps->screen_reg); + xd->present_region = x_create_region(c, session_get_screen_reg(ps)); } // 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, ps->c.screen_info->root_depth, - to_u16_checked(ps->root_width), - to_u16_checked(ps->root_height)); + xd->back_pixmap[i] = x_create_pixmap(c, c->screen_info->root_depth, + to_u16_checked(root_extent.width), + to_u16_checked(root_extent.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_visual_and_pixmap( - &ps->c, ps->c.screen_info->root_visual, xd->back_pixmap[i], - pic_attrs_mask, &pic_attrs); + c, c->screen_info->root_visual, 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"); diff --git a/src/common.h b/src/common.h index f15607da..3fa17b2f 100644 --- a/src/common.h +++ b/src/common.h @@ -145,249 +145,7 @@ struct damage_ring { /// Number of damage regions we track int count; }; - -/// 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; - /// Use an ev_timer callback for drawing - ev_timer draw_timer; - /// Called every time 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 related === - /// 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; - /// Shaders - struct shader_info *shaders; - - // === Display related === - /// X connection - struct x_connection c; - /// Whether the X server is grabbed by us - bool server_grabbed; - /// Width of root window. - int root_width; - /// Height of root window. - int root_height; - /// X Composite overlay window. - 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 - image_handle 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; - /// Whether screen has been turned off - bool screen_is_off; - /// When last MSC event happened, in useconds. - uint64_t last_msc_instant; - /// The last MSC number - uint64_t last_msc; - /// The delay between when the last frame was scheduled to be rendered, and when - /// the render actually started. - uint64_t last_schedule_delay; - /// When do we want our next frame to start rendering. - uint64_t next_render; - /// Whether we can perform frame pacing. - bool frame_pacing; - /// Vblank event scheduler - struct vblank_scheduler *vblank_scheduler; - - /// Render statistics - struct render_statistics render_stats; - - // === Operation related === - /// Flags related to the root window - uint64_t root_flags; - /// Program options. - options_t o; - /// State object for c2. - struct c2_state *c2_state; - /// Whether we have hit unredirection timeout. - bool tmout_unredir_hit; - /// If the backend is busy. This means two things: - /// Either the backend is currently rendering a frame, or a frame has been - /// rendered but has yet to be presented. In either case, we should not start - /// another render right now. As if we start issuing rendering commands now, we - /// will have to wait for either the current render to finish, or the current - /// back buffer to become available again. In either case, we will be wasting - /// time. - bool backend_busy; - /// Whether a render is queued. This generally means there are pending updates - /// to the screen that's neither included in the current render, nor on the - /// screen. - bool render_queued; - /// For tracking damage regions - struct damage_ring damage_ring; - /// 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 long fade_time; - // Cached blur convolution kernels. - struct x_convolution_kernel **blur_kerns_cache; - /// If we should quit - bool quit : 1; - // TODO(yshui) use separate flags for different 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 XRectangle-s of exposed region. - /// XXX why do we need this array? - rect_t *expose_rects; - /// Number of XRectangle-s in expose_rects. - int size_expose; - /// Index of the next free slot in expose_rects. - 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 win of current active window. Used by - /// EWMH _NET_ACTIVE_WINDOW 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; - /// Backend shadow context. - struct backend_shadow_context *shadow_context; - // for shadow precomputation - /// A region in which shadow is not painted on. - region_t shadow_exclude_reg; - - // === Software-optimization-related === - /// 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 DPMS extension exists - bool dpms_exists; - /// 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; - /// Information about monitors. - struct x_monitors monitors; - /// 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; - -#ifdef CONFIG_DBUS - // === DBus related === - struct cdbus_data *dbus_data; -#endif - - int (*vsync_wait)(session_t *); -} session_t; +struct session; /// Enumeration for window event hints. typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, WIN_EVMODE_CLIENT } win_evmode_t; @@ -461,18 +219,13 @@ static inline struct timespec get_time_timespec(void) { 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->c.screen_info->root; -} - +struct options *session_get_options(session_t *ps); /** * 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; + auto backend = session_get_options(ps)->backend; + return backend == BKEND_GLX || backend == BKEND_XR_GLX_HYBRID; } /** @@ -520,7 +273,7 @@ static inline void damage_ring_advance(struct damage_ring *ring) { pixman_region32_clear(&ring->damages[ring->cursor]); } -static inline void damage_ring_collect(struct damage_ring *ring, region_t *all_region, +static inline void damage_ring_collect(const struct damage_ring *ring, region_t *all_region, region_t *region, int buffer_age) { if (buffer_age == -1 || buffer_age > ring->count) { pixman_region32_copy(region, all_region); diff --git a/src/config.c b/src/config.c index 63b8fb0c..d341cf5b 100644 --- a/src/config.c +++ b/src/config.c @@ -25,6 +25,7 @@ #include "compiler.h" #include "kernel.h" #include "log.h" +#include "picom.h" #include "region.h" #include "string_utils.h" #include "types.h" @@ -420,12 +421,14 @@ bool parse_geometry(session_t *ps, const char *src, region_t *dest) { if (!src) { return true; } - if (!ps->root_width || !ps->root_height) { + + auto root_extent = session_get_root_extent(ps); + if (!root_extent.width || !root_extent.height) { return true; } long x = 0, y = 0; - long width = ps->root_width, height = ps->root_height; + long width = root_extent.width, height = root_extent.height; long val = 0L; char *endptr = NULL; @@ -472,7 +475,7 @@ bool parse_geometry(session_t *ps, const char *src, region_t *dest) { if (endptr && src != endptr) { x = val; if (*src == '-') { - x += ps->root_width - width; + x += root_extent.width - width; } src = endptr; } @@ -485,7 +488,7 @@ bool parse_geometry(session_t *ps, const char *src, region_t *dest) { if (endptr && src != endptr) { y = val; if (*src == '-') { - y += ps->root_height - height; + y += root_extent.height - height; } src = endptr; } diff --git a/src/dbus.c b/src/dbus.c index 01f0c327..a136fc87 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -24,12 +24,11 @@ #include "config.h" #include "dbus/dbus-protocol.h" #include "dbus/dbus-shared.h" -#include "list.h" #include "log.h" +#include "picom.h" #include "string_utils.h" #include "transition.h" #include "types.h" -#include "uthash_extra.h" #include "utils.h" #include "win.h" #include "win_defs.h" @@ -163,7 +162,7 @@ struct cdbus_data *cdbus_init(session_t *ps, const char *uniq) { } // Add watch handlers - cd->loop = ps->loop; + cd->loop = session_get_mainloop(ps); if (!dbus_connection_set_watch_functions(cd->dbus_conn, cdbus_callback_add_watch, cdbus_callback_remove_watch, cdbus_callback_watch_toggled, cd, NULL)) { @@ -472,41 +471,45 @@ static bool cdbus_append_string_variant(DBusMessage *msg, const char *data) { return true; } +struct cdbus_append_wids_data { + DBusMessageIter iter; + bool success; +}; + +static void cdbus_append_wids_callback(struct win *w, void *user_data) { + auto data = (struct cdbus_append_wids_data *)user_data; + assert(!w->destroyed); + if (data->success) { + data->success = + dbus_message_iter_append_basic(&data->iter, CDBUS_TYPE_WINDOW, &w->id); + } +} + /// Append all window IDs in the window list of a session to a D-Bus message static bool cdbus_append_wids(DBusMessage *msg, session_t *ps) { - // Get the number of wids we are to include - unsigned count = 0; - HASH_ITER2(ps->windows, w) { - assert(!w->destroyed); - ++count; - } - + auto count = session_get_window_count(ps); 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)) { + DBusMessageIter iter; + struct cdbus_append_wids_data callback_data = {.success = true}; + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_open_container( + &iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &callback_data.iter)) { log_error("Failed to append argument."); - free(arr); return false; } - free(arr); + session_foreach_win(ps, cdbus_append_wids_callback, &callback_data); + if (callback_data.success) { + callback_data.success = + dbus_message_iter_close_container(&iter, &callback_data.iter); + } + if (!callback_data.success) { + log_error("Failed to append argument."); + return false; + } return true; } @@ -615,12 +618,8 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_ append_win_property(Name, name, string_variant); if (!strcmp("Next", target)) { - cdbus_window_t next_id = 0; - if (!list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)) { - next_id = list_entry(w->base.stack_neighbour.next, struct win, - stack_neighbour) - ->id; - } + auto next_win = session_get_next_win_in_stack(ps, &w->base); + cdbus_window_t next_id = next_win ? next_win->id : XCB_NONE; if (!cdbus_append_wid_variant(reply, next_id)) { return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -640,7 +639,7 @@ static DBusHandlerResult cdbus_process_reset(session_t *ps, DBusMessage *msg att DBusMessage *reply, DBusError *e attr_unused) { // Reset the compositor log_info("picom is resetting..."); - ev_break(ps->loop, EVBREAK_ALL); + ev_break(session_get_mainloop(ps), EVBREAK_ALL); if (reply != NULL && !cdbus_append_boolean(reply, true)) { return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -694,11 +693,8 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE #define append_win_property(tgt, type) append(tgt, type, w->tgt) if (!strcmp("next", target)) { - xcb_window_t next_id = - list_node_is_last(&ps->window_stack, &w->base.stack_neighbour) - ? 0 - : list_entry(w->base.stack_neighbour.next, struct win, stack_neighbour) - ->id; + auto next_win = session_get_next_win_in_stack(ps, &w->base); + xcb_window_t next_id = next_win ? next_win->id : XCB_NONE; if (!cdbus_append_wid(reply, next_id)) { return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -818,14 +814,15 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL); return DBUS_HANDLER_RESULT_HANDLED; } - auto w = find_toplevel(ps, client); + auto w = session_find_toplevel(ps, client); if (w) { wid = w->base.id; } } else if (!strcmp("focused", target)) { // Find focused window - if (ps->active_win && ps->active_win->state != WSTATE_UNMAPPED) { - wid = ps->active_win->base.id; + auto active_win = session_get_active_win(ps); + if (active_win && active_win->state != WSTATE_UNMAPPED) { + wid = active_win->base.id; } } else { log_debug(CDBUS_ERROR_BADTGT_S, target); @@ -853,7 +850,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL); return DBUS_HANDLER_RESULT_HANDLED; } - assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); #define append(tgt, type, ret) \ if (!strcmp(#tgt, target)) { \ @@ -862,20 +858,21 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus } \ return DBUS_HANDLER_RESULT_HANDLED; \ } -#define append_session_option(tgt, type) append(tgt, type, ps->o.tgt) +#define append_session_option(tgt, type) append(tgt, type, options->tgt) + + auto options = session_get_options(ps); + assert(options->backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); append(version, string, PICOM_VERSION); append(pid, int32, getpid()); - append(display, string, DisplayString(ps->c.dpy)); + append(display, string, DisplayString(session_get_x_connection(ps)->dpy)); append(config_file, string, "Unknown"); - append(paint_on_overlay, boolean, ps->overlay != XCB_NONE); - append(paint_on_overlay_id, uint32, ps->overlay); // Sending ID of the X - // composite overlay - // window - append(unredir_if_possible_delay, int32, (int32_t)ps->o.unredir_if_possible_delay); + append(paint_on_overlay, boolean, session_get_overlay(ps) != XCB_NONE); + append(paint_on_overlay_id, uint32, session_get_overlay(ps)); + append(unredir_if_possible_delay, int32, (int32_t)options->unredir_if_possible_delay); append(refresh_rate, int32, 0); append(sw_opti, boolean, false); - append(backend, string, BACKEND_STRS[ps->o.backend]); + append(backend, string, BACKEND_STRS[options->backend]); append_session_option(unredir_if_possible, boolean); append_session_option(write_pid_path, string); @@ -928,9 +925,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus return DBUS_HANDLER_RESULT_HANDLED; } -// XXX Remove this after header clean up -void queue_redraw(session_t *ps); - /** * Process a opts_set D-Bus request. */ @@ -953,7 +947,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus if (strcmp(#tgt, target) == 0) { \ type val; \ get_msg_arg(dbus_type, val); \ - ps->o.tgt = expr; \ + options->tgt = expr; \ goto cdbus_process_opts_set_success; \ } @@ -961,6 +955,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus goto cdbus_process_opts_set_success; } + auto options = session_get_options(ps); opts_set_do(fade_delta, INT32, int32_t, max2(val, 1)); opts_set_do(fade_in_step, DOUBLE, double, normalize_d(val)); opts_set_do(fade_out_step, DOUBLE, double, normalize_d(val)); @@ -970,8 +965,8 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus if (!strcmp("unredir_if_possible", target)) { dbus_bool_t val = FALSE; get_msg_arg(BOOLEAN, val); - if (ps->o.unredir_if_possible != val) { - ps->o.unredir_if_possible = val; + if (options->unredir_if_possible != val) { + options->unredir_if_possible = val; queue_redraw(ps); } goto cdbus_process_opts_set_success; @@ -980,8 +975,8 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus if (!strcmp("redirected_force", target)) { cdbus_enum_t val = UNSET; get_msg_arg(UINT32, val); - if (ps->o.redirected_force != val) { - ps->o.redirected_force = val; + if (options->redirected_force != val) { + options->redirected_force = val; force_repaint(ps); } goto cdbus_process_opts_set_success; @@ -1068,6 +1063,24 @@ static DBusHandlerResult cdbus_process_introspect(DBusMessage *reply) { } ///@} +struct cdbus_window_root_introspect_data { + char *introspect; + bool success; +}; +static void cdbus_window_root_introspect_callback(struct win *w, void *user_data) { + auto data = (struct cdbus_window_root_introspect_data *)user_data; + assert(!w->destroyed); + if (data->success) { + char *tmp = NULL; + if (asprintf(&tmp, "\n", w->id) < 0) { + log_fatal("Failed to allocate memory."); + data->success = false; + return; + } + mstrextend(&data->introspect, tmp); + free(tmp); + } +} /** * Process an D-Bus Introspect request, for /windows. */ @@ -1088,31 +1101,19 @@ cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *reply) { return DBUS_HANDLER_RESULT_HANDLED; } - char *ret = NULL; - bool success = true; - mstrextend(&ret, str_introspect); + struct cdbus_window_root_introspect_data callback_data = {.introspect = NULL, + .success = true}; + mstrextend(&callback_data.introspect, str_introspect); + session_foreach_win(ps, cdbus_window_root_introspect_callback, &callback_data); + mstrextend(&callback_data.introspect, ""); - HASH_ITER2(ps->windows, w) { - assert(!w->destroyed); - if (!w->managed) { - continue; - } - char *tmp = NULL; - if (asprintf(&tmp, "\n", w->id) < 0) { - log_fatal("Failed to allocate memory."); - success = false; - break; - } - mstrextend(&ret, tmp); - free(tmp); + if (callback_data.success) { + callback_data.success = cdbus_append_string(reply, callback_data.introspect); } - mstrextend(&ret, ""); - if (success) { - success = cdbus_append_string(reply, ret); - } - free(ret); - return success ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NEED_MEMORY; + free(callback_data.introspect); + return callback_data.success ? DBUS_HANDLER_RESULT_HANDLED + : DBUS_HANDLER_RESULT_NEED_MEMORY; } static bool cdbus_process_window_introspect(DBusMessage *reply) { diff --git a/src/diagnostic.c b/src/diagnostic.c index ce93cfc0..48aa6956 100644 --- a/src/diagnostic.c +++ b/src/diagnostic.c @@ -15,12 +15,12 @@ void print_diagnostics(session_t *ps, const char *config_file, bool compositor_r printf("**Version:** " PICOM_VERSION "\n"); // printf("**CFLAGS:** %s\n", "??"); printf("\n### Extensions:\n\n"); - printf("* Shape: %s\n", ps->shape_exists ? "Yes" : "No"); - printf("* RandR: %s\n", ps->randr_exists ? "Yes" : "No"); - printf("* Present: %s\n", ps->present_exists ? "Present" : "Not Present"); + printf("* Shape: %s\n", session_has_shape_extension(ps) ? "Yes" : "No"); + printf("* RandR: %s\n", session_has_randr_extension(ps) ? "Yes" : "No"); + printf("* Present: %s\n", session_has_present_extension(ps) ? "Present" : "Not Present"); printf("\n### Misc:\n\n"); - printf("* Use Overlay: %s\n", ps->overlay != XCB_NONE ? "Yes" : "No"); - if (ps->overlay == XCB_NONE) { + printf("* Use Overlay: %s\n", session_get_overlay(ps) != XCB_NONE ? "Yes" : "No"); + if (session_get_overlay(ps) == XCB_NONE) { if (compositor_running) { printf(" (Another compositor is already running)\n"); } else if (session_redirection_mode(ps) != XCB_COMPOSITE_REDIRECT_MANUAL) { @@ -34,7 +34,7 @@ void print_diagnostics(session_t *ps, const char *config_file, bool compositor_r #endif printf("* Config file used: %s\n", config_file ?: "None"); printf("\n### Drivers (inaccurate):\n\n"); - print_drivers(ps->drivers); + print_drivers(session_get_driver(ps)); for (int i = 0; i < NUM_BKEND; i++) { if (backend_list[i] && backend_list[i]->diagnostics) { diff --git a/src/event.c b/src/event.c index f76d693c..f6d51c86 100644 --- a/src/event.c +++ b/src/event.c @@ -23,6 +23,7 @@ #include "win.h" #include "win_defs.h" #include "x.h" +#include "xcb/xfixes.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 @@ -66,14 +67,14 @@ 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->c.screen_info->root == wid) { + if (session_get_x_connection(ps)->screen_info->root == wid) { name = "(Root window)"; - } else if (ps->overlay == wid) { + } else if (session_get_overlay(ps) == wid) { name = "(Overlay)"; } else { auto w = find_managed_win(ps, wid); if (!w) { - w = find_toplevel(ps, wid); + w = session_find_toplevel(ps, wid); } if (w && w->name) { @@ -99,11 +100,13 @@ static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_ 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) { + if (session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY == + ev->response_type) { return ((xcb_damage_notify_event_t *)ev)->drawable; } - if (ps->shape_exists && ev->response_type == ps->shape_event) { + if (session_has_shape_extension(ps) && + ev->response_type == session_get_shape_extention_event(ps)) { return ((xcb_shape_notify_event_t *)ev)->affected_window; } @@ -131,16 +134,17 @@ static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) { CASESTRRET(ClientMessage); } - if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) { + if (session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY == ev->response_type) { return "Damage"; } - if (ps->shape_exists && ev->response_type == ps->shape_event) { + if (session_has_shape_extension(ps) && + ev->response_type == session_get_shape_extention_event(ps)) { return "ShapeNotify"; } - if (ps->xsync_exists) { - int o = ev->response_type - ps->xsync_event; + if (session_has_xsync_extension(ps)) { + int o = ev->response_type - session_get_xsync_extention_event(ps); switch (o) { CASESTRRET(XSyncCounterNotify); CASESTRRET(XSyncAlarmNotify); @@ -183,37 +187,37 @@ static inline const char *attr_pure ev_focus_detail_name(xcb_focus_in_event_t *e 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; + session_mark_updates_pending(ps); } 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; + session_mark_updates_pending(ps); } static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) { - if (ev->parent == ps->c.screen_info->root) { - add_win_top(ps, ev->window); + if (ev->parent == session_get_x_connection(ps)->screen_info->root) { + session_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); + auto w = session_find_win(ps, ce->window); if (!w) { return; } if (!w->managed) { - restack_above(ps, w, ce->above_sibling); + session_restack_above(ps, w, ce->above_sibling); return; } auto mw = (struct managed_win *)w; - restack_above(ps, w, ce->above_sibling); + session_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 @@ -227,7 +231,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { 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; + session_mark_updates_pending(ps); // At least one of the following if's is true if (position_changed) { @@ -248,7 +252,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { } // Recalculate which monitor this window is on - win_update_monitor(&ps->monitors, mw); + win_update_monitor(session_get_monitors(ps), mw); } // override_redirect flag cannot be changed after window creation, as far @@ -259,7 +263,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { 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->c.screen_info->root) { + if (ev->window == session_get_x_connection(ps)->screen_info->root) { set_root_flags(ps, ROOT_FLAGS_CONFIGURED); } else { configure_win(ps, ev); @@ -267,8 +271,8 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event } 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); + auto w = session_find_win(ps, ev->window); + auto mw = session_find_toplevel(ps, ev->window); if (mw && mw->client_win == mw->base.id) { // We only want _real_ frame window assert(&mw->base == w); @@ -291,7 +295,7 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t * if (mw != NULL) { win_unmark_client(ps, mw); win_set_flags(mw, WIN_FLAGS_CLIENT_STALE); - ps->pending_updates = true; + session_mark_updates_pending(ps); return; } log_debug("Received a destroy notify from an unknown window, %#010x", ev->window); @@ -300,10 +304,11 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t * 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) { + auto overlay = session_get_overlay(ps); + auto c = session_get_x_connection(ps); + if (overlay && ev->window == overlay && !session_is_redirected(ps)) { log_debug("Overlay is mapped while we are not redirected"); - auto e = xcb_request_check( - ps->c.c, xcb_unmap_window_checked(ps->c.c, ps->overlay)); + auto e = xcb_request_check(c->c, xcb_unmap_window_checked(c->c, overlay)); if (e) { log_error("Failed to unmap the overlay window"); free(e); @@ -328,7 +333,7 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) { // FocusIn/Out may be ignored when the window is unmapped, so we must // recheck focus here - ps->pending_updates = true; // to update focus + session_mark_updates_pending(ps); // to update focus } static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) { @@ -341,30 +346,31 @@ static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) 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); + auto w_top = session_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; + session_mark_updates_pending(ps); } - if (ev->parent == ps->c.screen_info->root) { + auto c = session_get_x_connection(ps); + if (ev->parent == c->screen_info->root) { // X will generate reparent notify 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); + auto w = session_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); + session_restack_top(ps, w); } else { - add_win_top(ps, ev->window); + session_add_win_top(ps, ev->window); } } else { // otherwise, find and destroy the window first { - auto w = find_win(ps, ev->window); + auto w = session_find_win(ps, ev->window); if (w) { if (w->managed) { auto mw = (struct managed_win *)w; @@ -395,7 +401,8 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t // Reset event mask in case something wrong happens uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN); - if (!wid_has_prop(ps->c.c, ev->window, ps->atoms->aWM_STATE)) { + auto atoms = session_get_atoms(ps); + if (!wid_has_prop(c->c, ev->window, 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.", @@ -408,70 +415,50 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t "client", w_real_top->base.id, w_real_top->name); win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE); - ps->pending_updates = true; + session_mark_updates_pending(ps); } else { log_debug("parent %#010x not found", ev->parent); } } - XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window, + XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, ev->window, XCB_CW_EVENT_MASK, (const uint32_t[]){evmask}); } } static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) { - auto w = find_win(ps, ev->window); + auto w = session_find_win(ps, ev->window); if (!w) { return; } if (ev->place == PlaceOnTop) { - restack_top(ps, w); + session_restack_top(ps, w); } else { - restack_bottom(ps, w); + session_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(®ion, rects, nrects); - add_damage(ps, ®ion); - pixman_region32_fini(®ion); -} - static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) { - if (ev->window == ps->c.screen_info->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; - } + auto overlay = session_get_overlay(ps); + if (ev->window == session_get_x_connection(ps)->screen_info->root || + (overlay && ev->window == overlay)) { + region_t region; + pixman_region32_init_rect(®ion, ev->x, ev->y, ev->width, ev->height); + add_damage(ps, ®ion); + pixman_region32_fini(®ion); } } static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) { + auto options = session_get_options(ps); + auto atoms = session_get_atoms(ps); + auto c = session_get_x_connection(ps); + auto c2_state = session_get_c2(ps); 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.c, xcb_get_atom_name(ps->c.c, ev->atom), NULL); + xcb_get_atom_name_reply_t *reply = + xcb_get_atom_name_reply(c->c, xcb_get_atom_name(c->c, ev->atom), NULL); const char *name = "?"; int name_len = 1; if (reply) { @@ -483,13 +470,13 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t free(reply); } - if (ps->c.screen_info->root == ev->window) { - if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) { + if (c->screen_info->root == ev->window) { + if (options->use_ewmh_active_win && atoms->a_NET_ACTIVE_WINDOW == ev->atom) { // to update focus - ps->pending_updates = true; + session_mark_updates_pending(ps); } else { // Destroy the root "image" if the wallpaper probably changed - if (x_is_root_back_pixmap_atom(ps->atoms, ev->atom)) { + if (x_is_root_back_pixmap_atom(atoms, ev->atom)) { root_damaged(ps); } } @@ -498,15 +485,15 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t return; } - ps->pending_updates = true; + session_mark_updates_pending(ps); // If WM_STATE changes - if (ev->atom == ps->atoms->aWM_STATE) { + if (ev->atom == atoms->aWM_STATE) { // Check whether it could be a client window - if (!find_toplevel(ps, ev->window)) { + if (!session_find_toplevel(ps, ev->window)) { // Reset event mask anyway const uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN); - XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window, + XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, ev->window, XCB_CW_EVENT_MASK, (const uint32_t[]){evmask}); auto w_top = find_managed_window_or_parent(ps, ev->window); @@ -521,60 +508,61 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t // 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 = find_toplevel(ps, ev->window); + if (ev->atom == atoms->a_NET_WM_WINDOW_TYPE) { + struct managed_win *w = session_find_toplevel(ps, ev->window); if (w) { win_set_property_stale(w, ev->atom); } } - if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) { + if (ev->atom == atoms->a_NET_WM_BYPASS_COMPOSITOR) { // Unnecessary until we remove the queue_redraw in ev_handle queue_redraw(ps); } // If _NET_WM_WINDOW_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 (ev->atom == atoms->a_NET_WM_WINDOW_OPACITY) { + auto w = find_managed_win(ps, ev->window) + ?: session_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 (ev->atom == atoms->a_NET_FRAME_EXTENTS) { + auto w = session_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 (atoms->aWM_NAME == ev->atom || atoms->a_NET_WM_NAME == ev->atom) { + auto w = session_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 (atoms->aWM_CLASS == ev->atom) { + auto w = session_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 (atoms->aWM_WINDOW_ROLE == ev->atom) { + auto w = session_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) { + if (atoms->a_COMPTON_SHADOW == ev->atom) { auto w = find_managed_win(ps, ev->window); if (w) { win_set_property_stale(w, ev->atom); @@ -582,31 +570,31 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t } // 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 ((options->detect_transient && atoms->aWM_TRANSIENT_FOR == ev->atom) || + (options->detect_client_leader && atoms->aWM_CLIENT_LEADER == ev->atom)) { + auto w = session_find_toplevel(ps, ev->window); if (w) { win_set_property_stale(w, ev->atom); } } - if (!ps->o.no_ewmh_fullscreen && ev->atom == ps->atoms->a_NET_WM_STATE) { - auto w = find_toplevel(ps, ev->window); + if (!options->no_ewmh_fullscreen && ev->atom == atoms->a_NET_WM_STATE) { + auto w = session_find_toplevel(ps, ev->window); if (w) { win_set_property_stale(w, ev->atom); } } // Check for other atoms we are tracking - if (c2_state_is_property_tracked(ps->c2_state, ev->atom)) { + if (c2_state_is_property_tracked(c2_state, ev->atom)) { bool change_is_on_client = false; auto w = find_managed_win(ps, ev->window); if (!w) { - w = find_toplevel(ps, ev->window); + w = session_find_toplevel(ps, ev->window); change_is_on_client = true; } if (w) { - c2_window_state_mark_dirty(ps->c2_state, &w->c2_state, ev->atom, + c2_window_state_mark_dirty(c2_state, &w->c2_state, ev->atom, change_is_on_client); // Set FACTOR_CHANGED so rules based on properties will be // re-evaluated. @@ -621,6 +609,8 @@ static inline void repair_win(session_t *ps, struct managed_win *w) { // Only mapped window can receive damages assert(w->state == WSTATE_MAPPED || win_check_flags_all(w, WIN_FLAGS_MAPPED)); + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); region_t parts; pixman_region32_init(&parts); @@ -632,10 +622,9 @@ static inline void repair_win(session_t *ps, struct managed_win *w) { // from the X server, which implies it has received our DamageSubtract request. if (!w->ever_damaged) { auto e = xcb_request_check( - ps->c.c, - xcb_damage_subtract_checked(ps->c.c, w->damage, XCB_NONE, XCB_NONE)); + c->c, xcb_damage_subtract_checked(c->c, w->damage, XCB_NONE, XCB_NONE)); if (e) { - if (ps->o.show_all_xerrors) { + if (options->show_all_xerrors) { x_print_error(e->sequence, e->major_code, e->minor_code, e->error_code); } @@ -645,14 +634,14 @@ static inline void repair_win(session_t *ps, struct managed_win *w) { // We only binds the window pixmap once the window is damaged. win_set_flags(w, WIN_FLAGS_PIXMAP_STALE); - ps->pending_updates = true; + session_mark_updates_pending(ps); } else { - auto cookie = xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, - ps->damage_ring.x_region); - if (!ps->o.show_all_xerrors) { - set_ignore_cookie(&ps->c, cookie); + xcb_xfixes_region_t damage_region = session_get_damage_ring(ps)->x_region; + auto cookie = xcb_damage_subtract(c->c, w->damage, XCB_NONE, damage_region); + if (!options->show_all_xerrors) { + set_ignore_cookie(c, cookie); } - x_fetch_region(&ps->c, ps->damage_ring.x_region, &parts); + x_fetch_region(c, damage_region, &parts); pixman_region32_translate(&parts, w->g.x + w->g.border_width, w->g.y + w->g.border_width); } @@ -663,13 +652,13 @@ static inline void repair_win(session_t *ps, struct managed_win *w) { // Why care about damage when screen is unredirected? // We will force full-screen repaint on redirection. - if (!ps->redirected) { + if (!session_is_redirected(ps)) { 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)) { + if (w->reg_ignore && session_is_win_region_ignore_valid(ps, w)) { pixman_region32_subtract(&parts, &parts, w->reg_ignore); } @@ -714,7 +703,7 @@ static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev) w->reg_ignore_valid = false; win_set_flags(w, WIN_FLAGS_SIZE_STALE); - ps->pending_updates = true; + session_mark_updates_pending(ps); } static inline void @@ -727,12 +716,13 @@ ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) { } void ev_handle(session_t *ps, xcb_generic_event_t *ev) { + auto c = session_get_x_connection(ps); if (XCB_EVENT_RESPONSE_TYPE(ev) != KeymapNotify) { - x_discard_pending(&ps->c, ev->full_sequence); + x_discard_pending(c, ev->full_sequence); } xcb_window_t wid = ev_window(ps, ev); - if (ev->response_type != ps->damage_event + XCB_DAMAGE_NOTIFY) { + if (ev->response_type != session_get_damage_extention_event(ps) + 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 { @@ -750,9 +740,9 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) { // https://bugs.freedesktop.org/show_bug.cgi?id=35945 // https://lists.freedesktop.org/archives/xcb/2011-November/007337.html auto response_type = XCB_EVENT_RESPONSE_TYPE(ev); - auto proc = XESetWireToEvent(ps->c.dpy, response_type, 0); + auto proc = XESetWireToEvent(c->dpy, response_type, 0); if (proc) { - XESetWireToEvent(ps->c.dpy, response_type, proc); + XESetWireToEvent(c->dpy, response_type, proc); XEvent dummy; // Stop Xlib from complaining about lost sequence numbers. @@ -762,8 +752,8 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) { // // We only need the low 16 bits uint16_t seq = ev->sequence; - ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->c.dpy) & 0xffff); - proc(ps->c.dpy, &dummy, (xEvent *)ev); + ev->sequence = (uint16_t)(LastKnownRequestProcessed(c->dpy) & 0xffff); + proc(c->dpy, &dummy, (xEvent *)ev); // Restore the sequence number ev->sequence = seq; } @@ -798,18 +788,21 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) { case SelectionClear: ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev); break; - case 0: x_handle_error(&ps->c, (xcb_generic_error_t *)ev); break; + case 0: x_handle_error(c, (xcb_generic_error_t *)ev); break; default: - if (ps->shape_exists && ev->response_type == ps->shape_event) { + if (session_has_shape_extension(ps) && + ev->response_type == session_get_shape_extention_event(ps)) { 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)) { + if (session_has_randr_extension(ps) && + ev->response_type == (session_get_randr_extention_event(ps) + + XCB_RANDR_SCREEN_CHANGE_NOTIFY)) { set_root_flags(ps, ROOT_FLAGS_SCREEN_CHANGE); break; } - if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) { + if (session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY == + ev->response_type) { ev_damage_notify(ps, (xcb_damage_notify_event_t *)ev); break; } diff --git a/src/opengl.c b/src/opengl.c index 26679b37..0d12b55b 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -23,7 +23,9 @@ #include "config.h" #include "kernel.h" #include "log.h" +#include "picom.h" #include "region.h" +#include "render.h" #include "string_utils.h" #include "uthash_extra.h" #include "utils.h" @@ -39,24 +41,26 @@ static inline XVisualInfo *get_visualinfo_from_visual(session_t *ps, xcb_visuali XVisualInfo vreq = {.visualid = visual}; int nitems = 0; - return XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems); + return XGetVisualInfo(session_get_x_connection(ps)->dpy, VisualIDMask, &vreq, &nitems); } /** * Initialize OpenGL. */ -bool glx_init(session_t *ps, bool need_render) { +struct glx_session *glx_init(session_t *ps, bool need_render) { bool success = false; XVisualInfo *pvis = NULL; + glx_session_t *psglx = NULL; + auto c = session_get_x_connection(ps); // Check for GLX extension - if (!ps->glx_exists) { + if (!session_has_glx_extension(ps)) { log_error("No GLX extension."); goto glx_init_end; } // Get XVisualInfo - pvis = get_visualinfo_from_visual(ps, ps->c.screen_info->root_visual); + pvis = get_visualinfo_from_visual(ps, c->screen_info->root_visual); if (!pvis) { log_error("Failed to acquire XVisualInfo for current visual."); goto glx_init_end; @@ -65,13 +69,12 @@ bool glx_init(session_t *ps, bool need_render) { // Ensure the visual is double-buffered if (need_render) { int value = 0; - if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_USE_GL, &value) || !value) { + if (Success != glXGetConfig(c->dpy, pvis, GLX_USE_GL, &value) || !value) { log_error("Root visual is not a GL visual."); goto glx_init_end; } - if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) || - !value) { + if (Success != glXGetConfig(c->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) { log_error("Root visual is not a double buffered GL visual."); goto glx_init_end; } @@ -83,38 +86,34 @@ bool glx_init(session_t *ps, bool need_render) { } // 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)); + auto options = session_get_options(ps); + psglx = cmalloc(glx_session_t); + *psglx = (glx_session_t){.context = NULL, .glx_prog_win = GLX_PROG_MAIN_INIT}; - // +1 for the zero terminator - ps->psglx->blur_passes = ccalloc(ps->o.blur_kernel_count, glx_blur_pass_t); + // +1 for the zero terminator + psglx->blur_passes = ccalloc(options->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; + for (int i = 0; i < options->blur_kernel_count; ++i) { + glx_blur_pass_t *ppass = &psglx->blur_passes[i]; + ppass->unifm_factor_center = -1; + ppass->unifm_offset_x = -1; + ppass->unifm_offset_y = -1; } - glx_session_t *psglx = ps->psglx; + psglx->round_passes = ccalloc(1, glx_round_pass_t); + glx_round_pass_t *ppass = 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; if (!psglx->context) { // Get GLX context #ifndef DEBUG_GLX_DEBUG_CONTEXT - psglx->context = glXCreateContext(ps->c.dpy, pvis, None, GL_TRUE); + psglx->context = glXCreateContext(c->dpy, pvis, None, GL_TRUE); #else { GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); @@ -136,7 +135,7 @@ bool glx_init(session_t *ps, bool need_render) { static const int attrib_list[] = { GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None}; psglx->context = p_glXCreateContextAttribsARB( - ps->c.dpy, fbconfig, NULL, GL_TRUE, attrib_list); + c->dpy, fbconfig, NULL, GL_TRUE, attrib_list); } #endif @@ -146,7 +145,7 @@ bool glx_init(session_t *ps, bool need_render) { } // Attach GLX context - if (!glXMakeCurrent(ps->c.dpy, get_tgt_window(ps), psglx->context)) { + if (!glXMakeCurrent(c->dpy, session_get_target_window(ps), psglx->context)) { log_error("Failed to attach GLX context."); goto glx_init_end; } @@ -168,7 +167,7 @@ bool glx_init(session_t *ps, bool need_render) { // 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) { + if (need_render && !options->glx_no_stencil) { GLint val = 0; glGetIntegerv(GL_STENCIL_BITS, &val); if (!val) { @@ -193,7 +192,7 @@ bool glx_init(session_t *ps, bool need_render) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); - if (!ps->o.glx_no_stencil) { + if (!options->glx_no_stencil) { // Initialize stencil buffer glClear(GL_STENCIL_BUFFER_BIT); glDisable(GL_STENCIL_TEST); @@ -204,7 +203,7 @@ bool glx_init(session_t *ps, bool need_render) { // Clear screen glClearColor(0.0F, 0.0F, 0.0F, 1.0F); // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // glXSwapBuffers(ps->c.dpy, get_tgt_window(ps)); + // glXSwapBuffers(ps->c.dpy, session_get_target_window(ps)); } success = true; @@ -213,10 +212,11 @@ glx_init_end: XFree(pvis); if (!success) { - glx_destroy(ps); + glx_destroy(ps, psglx); + psglx = NULL; } - return success; + return psglx; } static void glx_free_prog_main(glx_prog_main_t *pprogram) { @@ -235,19 +235,14 @@ static void glx_free_prog_main(glx_prog_main_t *pprogram) { /** * Destroy GLX related resources. */ -void glx_destroy(session_t *ps) { - if (!ps->psglx) { +void glx_destroy(struct session *ps, struct glx_session *psglx) { + if (!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]; + for (int i = 0; i < session_get_options(ps)->blur_kernel_count; ++i) { + glx_blur_pass_t *ppass = &psglx->blur_passes[i]; if (ppass->frag_shader) { glDeleteShader(ppass->frag_shader); } @@ -255,43 +250,44 @@ void glx_destroy(session_t *ps) { glDeleteProgram(ppass->prog); } } - free(ps->psglx->blur_passes); + free(psglx->blur_passes); - glx_round_pass_t *ppass = ps->psglx->round_passes; + glx_round_pass_t *ppass = psglx->round_passes; if (ppass->frag_shader) { glDeleteShader(ppass->frag_shader); } if (ppass->prog) { glDeleteProgram(ppass->prog); } - free(ps->psglx->round_passes); + free(psglx->round_passes); - glx_free_prog_main(&ps->glx_prog_win); + glx_free_prog_main(&psglx->glx_prog_win); gl_check_err(); // Destroy GLX context - if (ps->psglx->context) { - glXMakeCurrent(ps->c.dpy, None, NULL); - glXDestroyContext(ps->c.dpy, ps->psglx->context); - ps->psglx->context = NULL; + auto c = session_get_x_connection(ps); + if (psglx->context) { + glXMakeCurrent(c->dpy, None, NULL); + glXDestroyContext(c->dpy, psglx->context); + psglx->context = NULL; } - free(ps->psglx); - ps->psglx = NULL; - ps->argb_fbconfig = (struct glx_fbconfig_info){0}; + free(psglx); + psglx = 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); + auto root_extent = session_get_root_extent(ps); + glViewport(0, 0, root_extent.width, root_extent.height); // Initialize matrix, copied from dcompmgr glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(0, ps->root_width, 0, ps->root_height, -1000.0, 1000.0); + glOrtho(0, root_extent.width, 0, root_extent.height, -1000.0, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } @@ -300,12 +296,13 @@ void glx_on_root_change(session_t *ps) { * 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]); + auto options = session_get_options(ps); + assert(options->blur_kernel_count > 0); + assert(options->blur_kerns); + assert(options->blur_kerns[0]); // Allocate PBO if more than one blur kernel is present - if (ps->o.blur_kernel_count > 1) { + if (options->blur_kernel_count > 1) { // Try to generate a framebuffer GLuint fbo = 0; glGenFramebuffers(1, &fbo); @@ -343,7 +340,8 @@ bool glx_init_blur(session_t *ps) { " gl_FragColor = sum / (factor_center + float(%.7g));\n" "}\n"; - const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; + auto psglx = session_get_psglx(ps); + const bool use_texture_rect = !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; @@ -356,9 +354,9 @@ bool glx_init_blur(session_t *ps) { 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]; + for (int i = 0; i < options->blur_kernel_count; ++i) { + auto kern = options->blur_kerns[i]; + glx_blur_pass_t *ppass = &psglx->blur_passes[i]; // Build shader int width = kern->w, height = kern->h; @@ -505,7 +503,8 @@ bool glx_init_rounded_corners(session_t *ps) { "\n" "}\n"; - const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; + auto psglx = session_get_psglx(ps); + const bool use_texture_rect = !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; @@ -519,7 +518,7 @@ bool glx_init_rounded_corners(session_t *ps) { bool success = false; // Build rounded corners shader - auto ppass = ps->psglx->round_passes; + auto ppass = psglx->round_passes; auto len = strlen(FRAG_SHADER) + strlen(extension) + strlen(sampler_type) + strlen(texture_func) + 1; char *shader_str = ccalloc(len, char); @@ -614,7 +613,8 @@ static inline void glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int bas 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); + session_get_root_extent(ps).height - dy - height, + width, height); } } @@ -641,7 +641,7 @@ static inline GLuint glx_gen_texture(GLenum tex_tgt, int width, int height) { */ 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) { + if (!bkend_use_glx(ps)) { return true; } @@ -662,7 +662,7 @@ bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, i ptex->width = width; ptex->height = height; ptex->target = GL_TEXTURE_RECTANGLE; - if (ps->psglx->has_texture_non_power_of_two) { + if (session_get_psglx(ps)->has_texture_non_power_of_two) { ptex->target = GL_TEXTURE_2D; } } @@ -697,7 +697,7 @@ bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, i */ 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) { + if (!bkend_use_glx(ps)) { return true; } @@ -734,13 +734,14 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, // Create GLX pixmap int depth = 0; + auto c = session_get_x_connection(ps); 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.c, xcb_get_geometry(ps->c.c, pixmap), NULL); + c->c, xcb_get_geometry(c->c, pixmap), NULL); if (!r) { log_error("Failed to query info of pixmap %#010x.", pixmap); return false; @@ -761,7 +762,7 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, // 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) { + session_get_psglx(ps)->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; @@ -782,7 +783,7 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, 0, }; - ptex->glpixmap = glXCreatePixmap(ps->c.dpy, fbcfg->cfg, pixmap, attrs); + ptex->glpixmap = glXCreatePixmap(c->dpy, fbcfg->cfg, pixmap, attrs); ptex->pixmap = pixmap; ptex->target = (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE); @@ -829,10 +830,10 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, // The specification requires rebinding whenever the content changes... // We can't follow this, too slow. if (need_release) { - glXReleaseTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + glXReleaseTexImageEXT(c->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); } - glXBindTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); + glXBindTexImageEXT(c->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); // Cleanup glBindTexture(ptex->target, 0); @@ -847,16 +848,17 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, * @brief Release binding of a texture. */ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { + auto c = session_get_x_connection(ps); // Release binding if (ptex->glpixmap && ptex->texture) { glBindTexture(ptex->target, ptex->texture); - glXReleaseTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + glXReleaseTexImageEXT(c->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); glBindTexture(ptex->target, 0); } // Free GLX Pixmap if (ptex->glpixmap) { - glXDestroyPixmap(ps->c.dpy, ptex->glpixmap); + glXDestroyPixmap(c->dpy, ptex->glpixmap); ptex->glpixmap = 0; } @@ -868,7 +870,7 @@ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { */ void glx_set_clip(session_t *ps, const region_t *reg) { // Quit if we aren't using stencils - if (ps->o.glx_no_stencil) { + if (session_get_options(ps)->glx_no_stencil) { return; } @@ -884,7 +886,7 @@ void glx_set_clip(session_t *ps, const region_t *reg) { if (nrects == 1) { glEnable(GL_SCISSOR_TEST); - glScissor(rects[0].x1, ps->root_height - rects[0].y2, + glScissor(rects[0].x1, session_get_root_extent(ps).height - rects[0].y2, rects[0].x2 - rects[0].x1, rects[0].y2 - rects[0].y1); } @@ -917,8 +919,10 @@ 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) { - assert(ps->psglx->blur_passes[0].prog); - const bool more_passes = ps->o.blur_kernel_count > 1; + auto psglx = session_get_psglx(ps); + assert(psglx->blur_passes[0].prog); + auto options = session_get_options(ps); + const bool more_passes = options->blur_kernel_count > 1; const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); bool ret = false; @@ -954,7 +958,7 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, */ GLenum tex_tgt = GL_TEXTURE_RECTANGLE; - if (ps->psglx->has_texture_non_power_of_two) { + if (psglx->has_texture_non_power_of_two) { tex_tgt = GL_TEXTURE_2D; } @@ -1017,9 +1021,9 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, } 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]; + for (int i = 0; i < options->blur_kernel_count; ++i) { + last_pass = (i == options->blur_kernel_count - 1); + const glx_blur_pass_t *ppass = &psglx->blur_passes[i]; assert(ppass->prog); assert(tex_scr); @@ -1071,7 +1075,7 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, auto rdy = (GLfloat)(mheight - crect.y1 + mdy); if (last_pass) { rdx = (GLfloat)crect.x1; - rdy = (GLfloat)(ps->root_height - crect.y1); + rdy = (GLfloat)(session_get_root_extent(ps).height - crect.y1); } auto rdxe = rdx + (GLfloat)(crect.x2 - crect.x1); auto rdye = rdy - (GLfloat)(crect.y2 - crect.y1); @@ -1176,8 +1180,9 @@ void glx_read_border_pixel(int root_height, int root_width, int x, int y, int wi 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); + assert(session_get_psglx(ps)->round_passes->prog); bool ret = false; + auto psglx = session_get_psglx(ps); // 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, @@ -1186,13 +1191,14 @@ bool glx_round_corners_dst(session_t *ps, struct managed_win *w, int mdx = dx, mdy = dy, mwidth = width, mheight = height; log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight); + auto root_extent = session_get_root_extent(ps); if (w->g.border_width > 0) { - glx_read_border_pixel(ps->root_height, ps->root_width, dx, dy, width, - height, &w->border_col[0]); + glx_read_border_pixel(root_extent.height, root_extent.width, dx, dy, + width, height, &w->border_col[0]); } { - const glx_round_pass_t *ppass = ps->psglx->round_passes; + const glx_round_pass_t *ppass = psglx->round_passes; assert(ppass->prog); glEnable(GL_BLEND); @@ -1229,8 +1235,8 @@ bool glx_round_corners_dst(session_t *ps, struct managed_win *w, 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); + glUniform2f(ppass->unifm_resolution, (float)root_extent.width, + (float)root_extent.height); } // Painting @@ -1250,7 +1256,7 @@ bool glx_round_corners_dst(session_t *ps, struct managed_win *w, // coordinates for the texture in the target auto rdx = (GLfloat)crect.x1; - auto rdy = (GLfloat)(ps->root_height - crect.y1); + auto rdy = (GLfloat)(root_extent.height - crect.y1); auto rdxe = (GLfloat)rdx + (GLfloat)(crect.x2 - crect.x1); auto rdye = (GLfloat)rdy - (GLfloat)(crect.y2 - crect.y1); @@ -1305,7 +1311,7 @@ bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z, P_PAINTREG_START(crect) { // XXX what does all of these variables mean? GLint rdx = crect.x1; - GLint rdy = ps->root_height - crect.y1; + GLint rdy = session_get_root_extent(ps).height - crect.y1; GLint rdxe = rdx + (crect.x2 - crect.x1); GLint rdye = rdy - (crect.y2 - crect.y1); @@ -1481,7 +1487,7 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, // coordinates for the texture in the target GLint rdx = crect.x1; - GLint rdy = ps->root_height - crect.y1; + GLint rdy = session_get_root_extent(ps).height - crect.y1; GLint rdxe = rdx + (crect.x2 - crect.x1); GLint rdye = rdy - (crect.y2 - crect.y1); diff --git a/src/opengl.h b/src/opengl.h index 5bf92ed4..2d1df31a 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -13,12 +13,11 @@ #include "common.h" #include "compiler.h" -#include "log.h" +#include "picom.h" #include "region.h" #include "render.h" #include "win.h" -#include #include #include #include @@ -73,6 +72,9 @@ typedef struct glx_session { int z; glx_blur_pass_t *blur_passes; glx_round_pass_t *round_passes; + /// Custom GLX program used for painting window. + glx_prog_main_t glx_prog_win; + struct glx_fbconfig_info argb_fbconfig; } glx_session_t; /// @brief Wrapper of a bound GLX texture. @@ -96,9 +98,9 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, 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); +struct glx_session *glx_init(session_t *ps, bool need_render); -void glx_destroy(session_t *ps); +void glx_destroy(struct session *ps, struct glx_session *psglx); void glx_on_root_change(session_t *ps); @@ -148,7 +150,8 @@ 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; + auto psglx = session_get_psglx(ps); + return psglx && psglx->context; } /** diff --git a/src/picom.c b/src/picom.c index fcb9d8ff..c6f935c7 100644 --- a/src/picom.c +++ b/src/picom.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include +#include "backend/driver.h" #include "common.h" #include "compiler.h" #include "config.h" @@ -50,6 +52,7 @@ #include "picom.h" #include "transition.h" #include "win_defs.h" +#include "xcb/xproto.h" #ifdef CONFIG_OPENGL #include "opengl.h" #endif @@ -73,6 +76,246 @@ #include "win.h" #include "x.h" +/// 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; + /// Use an ev_timer callback for drawing + ev_timer draw_timer; + /// Called every time 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 related === + /// 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; + /// Shaders + struct shader_info *shaders; + + // === Display related === + /// X connection + struct x_connection c; + /// Whether the X server is grabbed by us + bool server_grabbed; + /// Width of root window. + int root_width; + /// Height of root window. + int root_height; + /// X Composite overlay window. + 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 + image_handle 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; +#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; + /// Whether screen has been turned off + bool screen_is_off; + /// When last MSC event happened, in useconds. + uint64_t last_msc_instant; + /// The last MSC number + uint64_t last_msc; + /// The delay between when the last frame was scheduled to be rendered, and when + /// the render actually started. + uint64_t last_schedule_delay; + /// When do we want our next frame to start rendering. + uint64_t next_render; + /// Whether we can perform frame pacing. + bool frame_pacing; + /// Vblank event scheduler + struct vblank_scheduler *vblank_scheduler; + + /// Render statistics + struct render_statistics render_stats; + + // === Operation related === + /// Flags related to the root window + uint64_t root_flags; + /// Program options. + options_t o; + /// State object for c2. + struct c2_state *c2_state; + /// Whether we have hit unredirection timeout. + bool tmout_unredir_hit; + /// If the backend is busy. This means two things: + /// Either the backend is currently rendering a frame, or a frame has been + /// rendered but has yet to be presented. In either case, we should not start + /// another render right now. As if we start issuing rendering commands now, we + /// will have to wait for either the current render to finish, or the current + /// back buffer to become available again. In either case, we will be wasting + /// time. + bool backend_busy; + /// Whether a render is queued. This generally means there are pending updates + /// to the screen that's neither included in the current render, nor on the + /// screen. + bool render_queued; + + /// For tracking damaged regions + struct damage_ring damage_ring; + /// 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 long fade_time; + // Cached blur convolution kernels. + struct x_convolution_kernel **blur_kerns_cache; + /// If we should quit + bool quit : 1; + // TODO(yshui) use separate flags for different 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 XRectangle-s of exposed region. + /// XXX why do we need this array? + rect_t *expose_rects; + /// Number of XRectangle-s in expose_rects. + int size_expose; + /// Index of the next free slot in expose_rects. + 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 win of current active window. Used by + /// EWMH _NET_ACTIVE_WINDOW 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; + /// Backend shadow context. + struct backend_shadow_context *shadow_context; + // for shadow precomputation + /// A region in which shadow is not painted on. + region_t shadow_exclude_reg; + + // === Software-optimization-related === + /// 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 DPMS extension exists + bool dpms_exists; + /// 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; + /// Information about monitors. + struct x_monitors monitors; + /// 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; + +#ifdef CONFIG_DBUS + // === DBus related === + struct cdbus_data *dbus_data; +#endif + + int (*vsync_wait)(session_t *); +} session_t; + /// Get session_t pointer from a pointer to a member of session_t #define session_ptr(ptr, member) \ ({ \ @@ -156,7 +399,7 @@ static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t auto w = find_managed_win(ps, wid); if (!w) { - w = find_toplevel(ps, wid); + w = session_find_toplevel(ps, wid); } if (!w) { w = find_managed_window_or_parent(ps, wid); @@ -536,8 +779,8 @@ uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode) { } // 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)) { + if (mode == WIN_EVMODE_CLIENT || ((w = session_find_toplevel(ps, wid)) && + w->a.map_state == XCB_MAP_STATE_VIEWABLE)) { evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE; } @@ -824,7 +1067,7 @@ static void configure_root(session_t *ps) { rebuild_shadow_exclude_reg(ps); // Invalidate reg_ignore from the top - auto top_w = win_stack_find_next_managed(ps, &ps->window_stack); + auto top_w = session_get_next_managed_win_in_stack(ps, &ps->window_stack); if (top_w) { rc_region_unref(&top_w->reg_ignore); top_w->reg_ignore_valid = false; @@ -1459,9 +1702,505 @@ xcb_window_t session_get_target_window(session_t *ps) { return ps->overlay != XCB_NONE ? ps->overlay : ps->c.screen_info->root; } +#ifdef CONFIG_DBUS struct cdbus_data *session_get_cdbus(struct session *ps) { return ps->dbus_data; } +#endif + +struct ev_loop *session_get_mainloop(struct session *ps) { + return ps->loop; +} + +struct options *session_get_options(struct session *ps) { + return &ps->o; +} + +enum driver session_get_driver(struct session *ps) { + return ps->drivers; +} + +unsigned int session_get_window_count(session_t *ps) { + unsigned int count = 0; + HASH_ITER2(ps->windows, w) { + assert(!w->destroyed); + ++count; + } + return count; +} + +struct managed_win *session_get_active_win(session_t *ps) { + return ps->active_win; +} + +void session_set_active_win(session_t *ps, struct managed_win *w) { + ps->active_win = w; +} + +xcb_window_t session_get_active_leader(session_t *ps) { + return ps->active_leader; +} + +void session_set_active_leader(session_t *ps, xcb_window_t leader) { + ps->active_leader = leader; +} + +struct x_connection *session_get_x_connection(session_t *ps) { + return &ps->c; +} + +struct win *session_get_next_win_in_stack(struct session *ps, struct win *w) { + if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) { + return list_entry(w->stack_neighbour.next, struct win, stack_neighbour); + } + return NULL; +} + +// Find the managed window immediately below `i` in the window stack +struct managed_win * +session_get_next_managed_win_in_stack(const session_t *ps, const struct list_node *cursor) { + while (!list_node_is_last(&ps->window_stack, cursor)) { + auto next = list_entry(cursor->next, struct win, stack_neighbour); + if (next->managed) { + return (struct managed_win *)next; + } + cursor = &next->stack_neighbour; + } + return NULL; +} + +xcb_window_t session_get_overlay(session_t *ps) { + return ps->overlay; +} + +struct damage_ring *session_get_damage_ring(session_t *ps) { + return &ps->damage_ring; +} + +void session_assert_server_grabbed(session_t *ps attr_unused) { + assert(ps->server_grabbed); +} + +image_handle session_get_root_image(session_t *ps) { + return ps->root_image; +} + +void session_record_cpu_time(session_t *ps) { + auto now = get_time_timespec(); + auto now_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; + if (ps->next_render > 0) { + log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %" PRIu64, + labs((long)now_us - (long)ps->next_render), + now_us < ps->next_render ? "early" : "late", now_us, + ps->next_render); + ps->last_schedule_delay = 0; + if (now_us > ps->next_render) { + ps->last_schedule_delay = now_us - ps->next_render; + } + } +} + +typeof(session_get_root_extent(NULL)) session_get_root_extent(session_t *ps) { + typeof(session_get_root_extent(ps)) ret = {.height = ps->root_height, + .width = ps->root_width}; + return ret; +} + +struct atom *session_get_atoms(session_t *ps) { + return ps->atoms; +} + +struct c2_state *session_get_c2(session_t *ps) { + return ps->c2_state; +} + +void session_mark_updates_pending(session_t *ps) { + ps->pending_updates = true; +} + +struct backend_base *session_get_backend_data(session_t *ps) { + return ps->backend_data; +} + +struct backend_shadow_context *session_get_backend_shadow_context(session_t *ps) { + return ps->shadow_context; +} + +void *session_get_backend_blur_context(session_t *ps) { + return ps->backend_blur_context; +} + +struct x_convolution_kernel **session_get_blur_kern_cache(session_t *ps) { + return ps->blur_kerns_cache; +} + +void session_set_blur_kern_cache(session_t *ps, struct x_convolution_kernel **cache) { + ps->blur_kerns_cache = cache; +} + +void session_foreach_win(session_t *ps, void (*func)(struct win *, void *), void *data) { + HASH_ITER2(ps->windows, w) { + assert(!w->destroyed); + func(w, data); + } +} + +struct shader_info *session_get_shader_info(session_t *ps, const char *key) { + struct shader_info *info = NULL; + if (key) { + HASH_FIND_STR(ps->shaders, key, info); + } + return info; +} + +bool session_is_redirected(session_t *ps) { + return ps->redirected; +} + +void session_delete_win(session_t *ps, struct win *w) { + HASH_DEL(ps->windows, w); +} + +/** + * Find a managed window from window id in window linked list of the session. + */ +struct win *session_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; +} + +/// Insert a new window after list_node `prev` +/// New window will be in unmapped state +static struct win *session_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); + session_mark_updates_pending(ps); + return new_w; +} + +/// Insert a new win entry at the top of the stack +struct win *session_add_win_top(session_t *ps, xcb_window_t id) { + return session_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 *session_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 session_add_win_top(ps, id); + } + // 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 session_add_win(ps, id, w->stack_neighbour.prev); +} + +/// Move window `w` so it's before `next` in the list +static inline void session_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 = session_get_next_managed_win_in_stack(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 +} + +struct list_node *session_get_win_stack(session_t *ps) { + return &ps->window_stack; +} + +/// Move window `w` so it's right above `below` +void session_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; + } + session_restack_win(ps, w, new_next); + } +} + +void session_restack_bottom(session_t *ps, struct win *w) { + session_restack_above(ps, w, 0); +} + +void session_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; + } + session_restack_win(ps, w, ps->window_stack.next); +} + +/** + * Clear leader cache of all windows. + */ +void session_clear_cache_win_leaders(session_t *ps) { + win_stack_foreach_managed(w, &ps->window_stack) { + w->cache_leader = XCB_NONE; + } +} + +bool session_is_win_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; +} + +/** + * 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 *session_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; +} + +#ifdef CONFIG_OPENGL +struct glx_session *session_get_psglx(session_t *ps) { + return ps->psglx; +} +void session_set_psglx(session_t *ps, struct glx_session *psglx) { + ps->psglx = psglx; +} +#endif + +xcb_render_picture_t *session_get_alpha_pictures(session_t *ps) { + return ps->alpha_picts; +} + +struct x_monitors *session_get_monitors(session_t *ps) { + return &ps->monitors; +} + +paint_t *session_get_tgt_buffer(session_t *ps) { + return &ps->tgt_buffer; +} + +xcb_render_picture_t session_get_tgt_picture(session_t *ps) { + return ps->tgt_picture; +} + +paint_t *session_get_root_tile_paint(session_t *ps) { + return &ps->root_tile_paint; +} + +bool session_get_root_tile_fill(session_t *ps) { + return ps->root_tile_fill; +} + +void session_set_root_tile_fill(session_t *ps, bool fill) { + ps->root_tile_fill = fill; +} + +void session_vsync_wait(session_t *ps) { + if (ps->vsync_wait) { + ps->vsync_wait(ps); + } +} + +xcb_render_picture_t session_get_white_picture(session_t *ps) { + return ps->white_picture; +} + +xcb_render_picture_t session_get_black_picture(session_t *ps) { + return ps->black_picture; +} + +xcb_render_picture_t session_get_cshadow_picture(session_t *ps) { + return ps->cshadow_picture; +} + +void session_set_white_picture(session_t *ps, xcb_render_picture_t p) { + ps->white_picture = p; +} + +void session_set_black_picture(session_t *ps, xcb_render_picture_t p) { + ps->black_picture = p; +} + +void session_set_cshadow_picture(session_t *ps, xcb_render_picture_t p) { + ps->cshadow_picture = p; +} + +region_t *session_get_screen_reg(session_t *ps) { + return &ps->screen_reg; +} + +region_t *session_get_shadow_exclude_reg(session_t *ps) { + return &ps->shadow_exclude_reg; +} + +void session_set_vsync_wait(session_t *ps, int (*vsync_wait)(session_t *)) { + ps->vsync_wait = vsync_wait; +} + +bool session_has_glx_extension(session_t *ps) { + return ps->glx_exists; +} + +bool session_has_shape_extension(session_t *ps) { + return ps->shape_exists; +} + +bool session_has_present_extension(session_t *ps) { + return ps->present_exists; +} + +bool session_has_xsync_extension(session_t *ps) { + return ps->xsync_exists; +} + +bool session_has_randr_extension(session_t *ps) { + return ps->randr_exists; +} + +int session_get_xfixes_extension_error(session_t *ps) { + return ps->xfixes_error; +} + +int session_get_render_extension_error(session_t *ps) { + return ps->render_error; +} + +int session_get_damage_extension_error(session_t *ps) { + return ps->damage_error; +} + +int session_get_glx_extension_error(session_t *ps) { + return ps->glx_error; +} + +int session_get_xsync_extension_error(session_t *ps) { + return ps->xsync_error; +} + +int session_get_xsync_extention_event(session_t *ps) { + return ps->xsync_event; +} + +int session_get_damage_extention_event(session_t *ps) { + return ps->damage_event; +} + +int session_get_shape_extention_event(session_t *ps) { + return ps->shape_event; +} + +int session_get_randr_extention_event(session_t *ps) { + return ps->randr_event; +} + +void session_xsync_wait_fence(session_t *ps) { + if (ps->o.xrender_sync_fence && 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.c, ps->sync_fence); + ps->sync_fence = XCB_NONE; + ps->o.xrender_sync_fence = false; + ps->xsync_exists = false; + } +} uint8_t session_redirection_mode(session_t *ps) { if (ps->o.debug_mode) { @@ -1679,7 +2418,7 @@ static void handle_new_windows(session_t *ps) { } // Send D-Bus signal if (ps->o.dbus) { - cdbus_ev_win_added(ps->dbus_data, new_w); + cdbus_ev_win_added(session_get_cdbus(ps), new_w); } } } @@ -2031,9 +2770,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy, .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, @@ -2413,9 +3149,12 @@ static session_t *session_init(int argc, char **argv, Display *dpy, apply_driver_workarounds(ps, ps->drivers); // Initialize filters, must be preceded by OpenGL context creation - if (ps->o.legacy_backends && !init_render(ps)) { - log_fatal("Failed to initialize the backend"); - exit(1); + if (ps->o.legacy_backends) { + ps->alpha_picts = ccalloc(MAX_ALPHA + 1, xcb_render_picture_t); + if (!init_render(ps)) { + log_fatal("Failed to initialize the backend"); + exit(1); + } } if (ps->o.print_diagnostics) { @@ -2554,7 +3293,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, 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); + session_add_win_above(ps, children[i], i ? children[i - 1] : XCB_NONE); } free(query_tree_reply); } @@ -2697,16 +3436,15 @@ static void session_destroy(session_t *ps) { // backend is deinitialized in unredirect() assert(ps->backend_data == NULL); } else { + // Free all GLX resources of windows + win_stack_foreach_managed(w, &ps->window_stack) { + free_win_res_glx(ps, w); + } deinit_render(ps); + free(ps->alpha_picts); + ps->alpha_picts = NULL; } -#if CONFIG_OPENGL - if (glx_has_context(ps)) { - // GLX context created, but not for rendering - glx_destroy(ps); - } -#endif - // Flush all events xcb_aux_sync(ps->c.c); ev_io_stop(ps->loop, &ps->xiow); diff --git a/src/picom.h b/src/picom.h index 1a19af58..f5de01ce 100644 --- a/src/picom.h +++ b/src/picom.h @@ -1,29 +1,25 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) - -// Throw everything in here. -// !!! DON'T !!! +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui. // === Includes === - -#include +#pragma once #include -#include #include #include #include "backend/backend.h" +#include "backend/driver.h" #include "c2.h" #include "common.h" -#include "compiler.h" #include "config.h" -#include "log.h" // XXX clean up +#include "ev.h" +#include "list.h" #include "region.h" #include "render.h" #include "types.h" -#include "utils.h" #include "win.h" #include "x.h" +#include "xcb/render.h" enum root_flags { ROOT_FLAGS_SCREEN_CHANGE = 1, // Received RandR screen change notify, we @@ -31,28 +27,120 @@ enum root_flags { 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 root_damaged(session_t *ps); - void queue_redraw(session_t *ps); - void discard_pending(session_t *ps, uint32_t 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); +struct options *session_get_options(session_t *ps); +struct ev_loop *session_get_mainloop(session_t *ps); +struct x_connection *session_get_x_connection(session_t *ps); +xcb_window_t session_get_overlay(session_t *ps); +struct atom *session_get_atoms(session_t *ps); +struct c2_state *session_get_c2(session_t *ps); +void session_mark_updates_pending(session_t *ps); +// TODO(yshui) can we lump these 3 backend related functions together? +struct backend_base *session_get_backend_data(session_t *ps); +struct backend_shadow_context *session_get_backend_shadow_context(session_t *ps); +void *session_get_backend_blur_context(session_t *ps); +struct shader_info *session_get_shader_info(session_t *ps, const char *key); +bool session_is_redirected(session_t *ps); +void session_xsync_wait_fence(session_t *ps); +enum driver session_get_driver(session_t *ps); +struct damage_ring *session_get_damage_ring(session_t *ps); +void session_assert_server_grabbed(session_t *ps); +image_handle session_get_root_image(session_t *ps); +/// Record the amount of CPU time spent in the render cycle. This is called in +/// backend.c just before we start sending backend rendering commands. `struct session` +/// keeps track when each render call started, when this function is called, the time +/// difference is used as the CPU time spent in the render cycle. +void session_record_cpu_time(session_t *ps); + +// TODO(yshui) move window related data out of session_t, and remove these getters +void session_delete_win(session_t *ps, struct win *w); +struct list_node *session_get_win_stack(session_t *ps); +struct managed_win *session_get_active_win(session_t *ps); +void session_set_active_win(session_t *ps, struct managed_win *w); +struct win *session_get_next_win_in_stack(session_t *ps, struct win *w); +unsigned int session_get_window_count(session_t *ps); +void session_foreach_win(session_t *ps, void (*func)(struct win *w, void *data), void *data); +xcb_window_t session_get_active_leader(session_t *ps); +void session_set_active_leader(session_t *ps, xcb_window_t leader); +void session_clear_cache_win_leaders(session_t *ps); +bool session_is_win_region_ignore_valid(session_t *ps, const struct managed_win *w); +struct win *session_find_win(session_t *ps, xcb_window_t id); +/// Insert a new window above window with id `below`, if there is no window, add to top +/// New window will be in unmapped state +struct win *session_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 *session_add_win_top(session_t *ps, xcb_window_t id); +/// Move window `w` to be right above `below` +void session_restack_above(session_t *ps, struct win *w, xcb_window_t below); +/// Move window `w` to the bottom of the stack +void session_restack_bottom(session_t *ps, struct win *w); +/// Move window `w` to the top of the stack +void session_restack_top(session_t *ps, struct win *w); +/** + * 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 *session_find_toplevel(session_t *ps, xcb_window_t id); +// Find the managed window immediately below `w` in the window stack +struct managed_win * +session_get_next_managed_win_in_stack(const session_t *ps, const struct list_node *cursor); + +// TODO(yshui) Legacy backend stuff, should probably lump them together. Also remove them +// once the legacy backends are removed. +#ifdef CONFIG_OPENGL +struct glx_session *session_get_psglx(session_t *ps); +void session_set_psglx(session_t *ps, struct glx_session *psglx); +#endif +struct { + int height, width; +} session_get_root_extent(session_t *ps); +xcb_render_picture_t *session_get_alpha_pictures(session_t *ps); +struct x_monitors *session_get_monitors(session_t *ps); +paint_t *session_get_tgt_buffer(session_t *ps); +xcb_render_picture_t session_get_tgt_picture(session_t *ps); +paint_t *session_get_root_tile_paint(session_t *ps); +bool session_get_root_tile_fill(session_t *ps); +void session_set_root_tile_fill(session_t *ps, bool fill); +void session_vsync_wait(session_t *ps); +void session_set_vsync_wait(session_t *ps, int (*vsync_wait)(session_t *)); +xcb_render_picture_t session_get_white_picture(session_t *ps); +xcb_render_picture_t session_get_black_picture(session_t *ps); +xcb_render_picture_t session_get_cshadow_picture(session_t *ps); +void session_set_black_picture(session_t *ps, xcb_render_picture_t p); +void session_set_white_picture(session_t *ps, xcb_render_picture_t p); +void session_set_cshadow_picture(session_t *ps, xcb_render_picture_t p); +region_t *session_get_screen_reg(session_t *ps); +region_t *session_get_shadow_exclude_reg(session_t *ps); +struct x_convolution_kernel **session_get_blur_kern_cache(session_t *ps); +void session_set_blur_kern_cache(session_t *ps, struct x_convolution_kernel **cache); + +// TODO(yshui) has_extension and get_*_extension_error should probably be in struct +// x_connection, or just use xcb functions instead - those are cached as well. +bool session_has_glx_extension(session_t *ps); +bool session_has_shape_extension(session_t *ps); +bool session_has_present_extension(session_t *ps); +bool session_has_randr_extension(session_t *ps); +bool session_has_xsync_extension(session_t *ps); +int session_get_xfixes_extension_error(session_t *ps); +int session_get_render_extension_error(session_t *ps); +int session_get_damage_extension_error(session_t *ps); +int session_get_glx_extension_error(session_t *ps); +int session_get_xsync_extension_error(session_t *ps); +int session_get_xsync_extention_event(session_t *ps); +int session_get_damage_extention_event(session_t *ps); +int session_get_shape_extention_event(session_t *ps); +int session_get_randr_extention_event(session_t *ps); #ifdef CONFIG_DBUS struct cdbus_data *session_get_cdbus(struct session *); @@ -99,17 +187,3 @@ 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.c, xcb_get_geometry(ps->c.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/render.c b/src/render.c index b03a2102..44d8177e 100644 --- a/src/render.c +++ b/src/render.c @@ -12,6 +12,7 @@ #include "common.h" #include "options.h" +#include "picom.h" #include "transition.h" #ifdef CONFIG_OPENGL @@ -55,24 +56,26 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h } struct glx_fbconfig_info *fbcfg; + auto c = session_get_x_connection(ps); + auto psglx = session_get_psglx(ps); if (!visual) { assert(depth == 32); - if (!ps->argb_fbconfig.cfg) { - glx_find_fbconfig(&ps->c, + if (!psglx->argb_fbconfig.cfg) { + glx_find_fbconfig(c, (struct xvisual_info){.red_size = 8, .green_size = 8, .blue_size = 8, .alpha_size = 8, .visual_depth = 32}, - &ps->argb_fbconfig); + &psglx->argb_fbconfig); } - if (!ps->argb_fbconfig.cfg) { + if (!psglx->argb_fbconfig.cfg) { log_error("Failed to find appropriate FBConfig for 32 bit depth"); return false; } - fbcfg = &ps->argb_fbconfig; + fbcfg = &psglx->argb_fbconfig; } else { - auto m = x_get_visual_info(&ps->c, visual); + auto m = x_get_visual_info(c, visual); if (m.visual_depth < 0) { return false; } @@ -83,7 +86,7 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h } if (!ppaint->fbcfg.cfg) { - glx_find_fbconfig(&ps->c, m, &ppaint->fbcfg); + glx_find_fbconfig(session_get_x_connection(ps), m, &ppaint->fbcfg); } if (!ppaint->fbcfg.cfg) { log_error("Failed to find appropriate FBConfig for X pixmap"); @@ -113,34 +116,37 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h * 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; + auto backend = session_get_options(ps)->backend; + return BKEND_XRENDER == backend || BKEND_XR_GLX_HYBRID == backend; } int maximum_buffer_age(session_t *ps) { - if (bkend_use_glx(ps) && ps->o.use_damage) { + if (bkend_use_glx(ps) && session_get_options(ps)->use_damage) { return CGLX_MAX_BUFFER_AGE; } return 1; } static int get_buffer_age(session_t *ps) { + auto options = session_get_options(ps); #ifdef CONFIG_OPENGL if (bkend_use_glx(ps)) { - if (!glxext.has_GLX_EXT_buffer_age && ps->o.use_damage) { + if (!glxext.has_GLX_EXT_buffer_age && options->use_damage) { log_warn("GLX_EXT_buffer_age not supported by your driver," "`use-damage` has to be disabled"); - ps->o.use_damage = false; + options->use_damage = false; } - if (ps->o.use_damage) { + if (options->use_damage) { unsigned int val; - glXQueryDrawable(ps->c.dpy, get_tgt_window(ps), + glXQueryDrawable(session_get_x_connection(ps)->dpy, + session_get_target_window(ps), GLX_BACK_BUFFER_AGE_EXT, &val); return (int)val ?: -1; } return -1; } #endif - return ps->o.use_damage ? 1 : -1; + return options->use_damage ? 1 : -1; } /** @@ -148,16 +154,18 @@ static int get_buffer_age(session_t *ps) { */ static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) { #define FILTER "Nearest" - xcb_render_set_picture_filter(ps->c.c, p, strlen(FILTER), FILTER, 0, NULL); + xcb_render_set_picture_filter(session_get_x_connection(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) { + switch (session_get_options(ps)->backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: - x_set_picture_clip_region(&ps->c, ps->tgt_buffer.pict, 0, 0, reg); + x_set_picture_clip_region(session_get_x_connection(ps), + session_get_tgt_buffer(ps)->pict, 0, 0, reg); break; #ifdef CONFIG_OPENGL case BKEND_GLX: glx_set_clip(ps, reg); break; @@ -173,12 +181,13 @@ void free_paint(session_t *ps, paint_t *ppaint) { #ifdef CONFIG_OPENGL free_paint_glx(ps, ppaint); #endif + auto c = session_get_x_connection(ps); if (ppaint->pict != XCB_NONE) { - x_free_picture(&ps->c, ppaint->pict); + x_free_picture(c, ppaint->pict); ppaint->pict = XCB_NONE; } if (ppaint->pixmap) { - xcb_free_pixmap(ps->c.c, ppaint->pixmap); + xcb_free_pixmap(c->c, ppaint->pixmap); ppaint->pixmap = XCB_NONE; } } @@ -243,15 +252,17 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f 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) { + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); + switch (options->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]; + xcb_render_picture_t alpha_pict = session_get_alpha_pictures(ps)[alpha_step]; if (alpha_step != 0) { if (cr) { xcb_render_picture_t p_tmp = x_create_picture_with_standard( - &ps->c, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0); + c, 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 = { @@ -259,7 +270,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f .y = 0, .width = to_u16_checked(fullwid), .height = to_u16_checked(fullhei)}; - xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_SRC, + xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_SRC, p_tmp, trans, 1, &rect); uint32_t max_ntraps = to_u32_checked(cr); @@ -269,24 +280,24 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f traps, max_ntraps, cr, fullwid, fullhei); xcb_render_trapezoids( - ps->c.c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp, - x_get_pictfmt_for_standard(&ps->c, XCB_PICT_STANDARD_A_8), + c->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp, + x_get_pictfmt_for_standard(c, XCB_PICT_STANDARD_A_8), 0, 0, n, traps); xcb_render_composite( - ps->c.c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp, - ps->tgt_buffer.pict, to_i16_checked(x), + c->c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp, + session_get_tgt_buffer(ps)->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)); - x_free_picture(&ps->c, p_tmp); + x_free_picture(c, p_tmp); } else { xcb_render_picture_t p_tmp = alpha_pict; if (clip) { p_tmp = x_create_picture_with_standard( - &ps->c, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0); + c, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0); xcb_render_color_t black = { .red = 255, .blue = 255, .green = 255, .alpha = 255}; @@ -295,18 +306,17 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f .y = 0, .width = to_u16_checked(wid), .height = to_u16_checked(hei)}; - xcb_render_fill_rectangles(ps->c.c, - XCB_RENDER_PICT_OP_SRC, + xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_SRC, p_tmp, black, 1, &rect); if (alpha_pict) { xcb_render_composite( - ps->c.c, XCB_RENDER_PICT_OP_SRC, + c->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.c, XCB_RENDER_PICT_OP_OUT_REVERSE, + c->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)); @@ -316,23 +326,25 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f : XCB_RENDER_PICT_OP_OVER); xcb_render_composite( - ps->c.c, op, pict, p_tmp, ps->tgt_buffer.pict, + c->c, op, pict, p_tmp, session_get_tgt_buffer(ps)->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) { - x_free_picture(&ps->c, p_tmp); + x_free_picture(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; + case BKEND_GLX: { + auto psglx = session_get_psglx(ps); + glx_render(ps, ptex, x, y, dx, dy, wid, hei, psglx->z, opacity, argb, neg, + reg_paint, pprogram); + psglx->z += 1; break; + } #endif default: assert(0); } @@ -351,14 +363,15 @@ paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid, 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 argb = + (w && (win_has_alpha(w) || session_get_options(ps)->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, + (w ? w->paint.ptex : session_get_root_tile_paint(ps)->ptex), reg_paint, #ifdef CONFIG_OPENGL - w ? &ps->glx_prog_win : NULL + w ? &session_get_psglx(ps)->glx_prog_win : NULL #else NULL #endif @@ -381,7 +394,8 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { } #ifdef CONFIG_OPENGL - if (BKEND_GLX == ps->o.backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) { + if (BKEND_GLX == session_get_options(ps)->backend && + !glx_tex_bound(ppaint->ptex, XCB_NONE)) { return false; } #endif @@ -393,11 +407,13 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { * Paint a window itself and dim it if asked. */ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) { + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); // Fetch Pixmap if (!w->paint.pixmap) { - w->paint.pixmap = x_new_id(&ps->c); - set_ignore_cookie(&ps->c, xcb_composite_name_window_pixmap( - ps->c.c, w->base.id, w->paint.pixmap)); + w->paint.pixmap = x_new_id(c); + set_ignore_cookie( + c, xcb_composite_name_window_pixmap(c->c, w->base.id, w->paint.pixmap)); } xcb_drawable_t draw = w->paint.pixmap; @@ -415,7 +431,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) }; w->paint.pict = x_create_picture_with_pictfmt_and_pixmap( - &ps->c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); + c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); } // GLX: Build texture @@ -423,7 +439,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) // 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))) { + (!options->glx_no_rebind_pixmap && w->pixmap_damaged))) { log_error("Failed to bind texture for window %#010x.", w->base.id); } w->pixmap_damaged = false; @@ -444,7 +460,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) // 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, wid, hei, w->pictfmt, 0, NULL); + x_create_picture_with_pictfmt(c, wid, hei, w->pictfmt, 0, NULL); if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { @@ -457,15 +473,15 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) pixman_region32_fini(®); } - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, pict, - XCB_NONE, newpict, 0, 0, 0, 0, 0, 0, wid, hei); - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_DIFFERENCE, - ps->white_picture, XCB_NONE, newpict, 0, 0, - 0, 0, 0, 0, wid, hei); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE, + newpict, 0, 0, 0, 0, 0, 0, wid, hei); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_DIFFERENCE, + session_get_white_picture(ps), 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.c, XCB_RENDER_PICT_OP_IN_REVERSE, + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_IN_REVERSE, pict, XCB_NONE, newpict, 0, 0, 0, 0, 0, 0, wid, hei); } @@ -551,18 +567,18 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) #undef COMP_BDR if (pict != w->paint.pict) { - x_free_picture(&ps->c, pict); + x_free_picture(c, pict); pict = XCB_NONE; } // Dimming the window if needed if (w->dim) { - double dim_opacity = ps->o.inactive_dim; - if (!ps->o.inactive_dim_fixed) { + double dim_opacity = options->inactive_dim; + if (!options->inactive_dim_fixed) { dim_opacity *= window_opacity; } - switch (ps->o.backend) { + switch (options->backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { auto cval = (uint16_t)(0xffff * dim_opacity); @@ -582,12 +598,13 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) .height = hei, }; - xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_OVER, - ps->tgt_buffer.pict, color, 1, &rect); + xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_OVER, + session_get_tgt_buffer(ps)->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), + glx_dim_dst(ps, x, y, wid, hei, (int)(session_get_psglx(ps)->z - 0.7), (float)dim_opacity, reg_paint); break; #endif @@ -598,36 +615,37 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) static bool get_root_tile(session_t *ps) { /* - if (ps->o.paint_on_overlay) { + if (options->paint_on_overlay) { return ps->root_picture; } */ - assert(!ps->root_tile_paint.pixmap); - ps->root_tile_fill = false; + assert(!session_get_root_tile_paint(ps)->pixmap); + auto c = session_get_x_connection(ps); + auto atoms = session_get_atoms(ps); + session_set_root_tile_fill(ps, false); bool fill = false; - xcb_pixmap_t pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms); + xcb_pixmap_t pixmap = x_get_root_back_pixmap(c, atoms); xcb_get_geometry_reply_t *r; if (pixmap) { - r = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL); + r = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, pixmap), NULL); } // Create a pixmap if there isn't any xcb_visualid_t visual; if (!pixmap || !r) { - pixmap = - x_create_pixmap(&ps->c, (uint8_t)ps->c.screen_info->root_depth, 1, 1); + pixmap = x_create_pixmap(c, (uint8_t)c->screen_info->root_depth, 1, 1); if (pixmap == XCB_NONE) { log_error("Failed to create pixmaps for root tile."); return false; } - visual = ps->c.screen_info->root_visual; + visual = c->screen_info->root_visual; fill = true; } else { - visual = r->depth == ps->c.screen_info->root_depth - ? ps->c.screen_info->root_visual - : x_get_visual_for_depth(ps->c.screen_info, r->depth); + visual = r->depth == c->screen_info->root_depth + ? c->screen_info->root_visual + : x_get_visual_for_depth(c->screen_info, r->depth); free(r); } @@ -635,8 +653,9 @@ static bool get_root_tile(session_t *ps) { xcb_render_create_picture_value_list_t pa = { .repeat = true, }; - ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap( - &ps->c, visual, pixmap, XCB_RENDER_CP_REPEAT, &pa); + auto root_tile_paint = session_get_root_tile_paint(ps); + root_tile_paint->pict = x_create_picture_with_visual_and_pixmap( + c, visual, pixmap, XCB_RENDER_CP_REPEAT, &pa); // Fill pixmap if needed if (fill) { @@ -649,15 +668,15 @@ static bool get_root_tile(session_t *ps) { rect.x = rect.y = 0; rect.width = rect.height = 1; - xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_SRC, - ps->root_tile_paint.pict, col, 1, &rect); + xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_SRC, + root_tile_paint->pict, col, 1, &rect); } - ps->root_tile_fill = fill; - ps->root_tile_paint.pixmap = pixmap; + session_set_root_tile_fill(ps, fill); + 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, visual, false); + if (session_get_options(ps)->backend == BKEND_GLX) { + return paint_bind_tex(ps, root_tile_paint, 0, 0, true, 0, visual, false); } #endif @@ -670,12 +689,14 @@ static bool get_root_tile(session_t *ps) { 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)) { + auto root_tile_paint = session_get_root_tile_paint(ps); + if (!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); + auto root_extent = session_get_root_extent(ps); + paint_region(ps, NULL, 0, 0, root_extent.width, root_extent.height, 1.0, + reg_paint, root_tile_paint->pict); } /** @@ -691,16 +712,17 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit 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, (conv *)ps->shadow_context, opacity, width, height); + auto c = session_get_x_connection(ps); + auto shadow_context = (conv *)session_get_backend_shadow_context(ps); + shadow_image = make_shadow(c, shadow_context, opacity, width, height); if (!shadow_image) { log_error("failed to make shadow"); return XCB_NONE; } - shadow_pixmap = x_create_pixmap(&ps->c, 8, shadow_image->width, shadow_image->height); + shadow_pixmap = x_create_pixmap(c, 8, shadow_image->width, shadow_image->height); shadow_pixmap_argb = - x_create_pixmap(&ps->c, 32, shadow_image->width, shadow_image->height); + x_create_pixmap(c, 32, shadow_image->width, shadow_image->height); if (!shadow_pixmap || !shadow_pixmap_argb) { log_error("failed to create shadow pixmaps"); @@ -708,18 +730,18 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit } shadow_picture = x_create_picture_with_standard_and_pixmap( - &ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL); + 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); + 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.c, gc, shadow_pixmap, 0, NULL); + gc = x_new_id(c); + xcb_create_gc(c->c, gc, shadow_pixmap, 0, NULL); - xcb_image_put(ps->c.c, shadow_pixmap, gc, shadow_image, 0, 0, 0); - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture, + xcb_image_put(c->c, shadow_pixmap, gc, shadow_image, 0, 0, 0); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, session_get_cshadow_picture(ps), shadow_picture, shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width, shadow_image->height); @@ -728,10 +750,10 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit assert(!w->shadow_paint.pict); w->shadow_paint.pict = shadow_picture_argb; - xcb_free_gc(ps->c.c, gc); + xcb_free_gc(c->c, gc); xcb_image_destroy(shadow_image); - xcb_free_pixmap(ps->c.c, shadow_pixmap); - x_free_picture(&ps->c, shadow_picture); + xcb_free_pixmap(c->c, shadow_pixmap); + x_free_picture(c, shadow_picture); return true; @@ -740,19 +762,19 @@ shadow_picture_err: xcb_image_destroy(shadow_image); } if (shadow_pixmap) { - xcb_free_pixmap(ps->c.c, shadow_pixmap); + xcb_free_pixmap(c->c, shadow_pixmap); } if (shadow_pixmap_argb) { - xcb_free_pixmap(ps->c.c, shadow_pixmap_argb); + xcb_free_pixmap(c->c, shadow_pixmap_argb); } if (shadow_picture) { - x_free_picture(&ps->c, shadow_picture); + x_free_picture(c, shadow_picture); } if (shadow_picture_argb) { - x_free_picture(&ps->c, shadow_picture_argb); + x_free_picture(c, shadow_picture_argb); } if (gc) { - xcb_free_gc(ps->c.c, gc); + xcb_free_gc(c->c, gc); } return false; @@ -772,32 +794,35 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) { } xcb_render_picture_t td = XCB_NONE; - bool should_clip = - (w->corner_radius > 0) && (!ps->o.wintype_option[w->window_type].full_shadow); + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); + bool should_clip = (w->corner_radius > 0) && + (!options->wintype_option[w->window_type].full_shadow); if (should_clip) { - if (ps->o.backend == BKEND_XRENDER || ps->o.backend == BKEND_XR_GLX_HYBRID) { + if (options->backend == BKEND_XRENDER || + options->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, w->widthb, w->heightb, XCB_PICT_STANDARD_ARGB_32, 0, 0); + c, 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.c, XCB_RENDER_PICT_OP_SRC, td, + xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_SRC, td, trans, 1, &rect); - auto solid = solid_picture(&ps->c, false, 1, 0, 0, 0); + auto solid = solid_picture(c, false, 1, 0, 0, 0); xcb_render_trapezoids( - ps->c.c, XCB_RENDER_PICT_OP_OVER, solid, td, - x_get_pictfmt_for_standard(&ps->c, XCB_PICT_STANDARD_A_8), 0, - 0, n, traps); - x_free_picture(&ps->c, solid); + c->c, XCB_RENDER_PICT_OP_OVER, solid, td, + x_get_pictfmt_for_standard(c, XCB_PICT_STANDARD_A_8), 0, 0, n, + traps); + x_free_picture(c, solid); } else { // Not implemented } @@ -813,7 +838,7 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) { w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL, should_clip ? &clip : NULL); if (td) { - x_free_picture(&ps->c, td); + x_free_picture(c, td); } } @@ -841,8 +866,9 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y // 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, wid, hei, ps->c.screen_info->root_visual, 0, NULL); + auto c = session_get_x_connection(ps); + xcb_render_picture_t tmp_picture = + x_create_picture_with_visual(c, wid, hei, c->screen_info->root_visual, 0, NULL); if (!tmp_picture) { log_error("Failed to build intermediate Picture."); @@ -850,7 +876,7 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y } if (reg_clip && tmp_picture) { - x_set_picture_clip_region(&ps->c, tmp_picture, 0, 0, reg_clip); + x_set_picture_clip_region(c, tmp_picture, 0, 0, reg_clip); } xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture; @@ -865,9 +891,9 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y // be applied on source picture, to get the nearby pixels outside the // window. xcb_render_set_picture_filter( - ps->c.c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION, + c->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION, (uint32_t)(kwid * khei + 2), convolution_blur); - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, + xcb_render_composite(c->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); @@ -881,11 +907,11 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y } if (src_pict != tgt_buffer) { - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded, + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } - x_free_picture(&ps->c, tmp_picture); + x_free_picture(c, tmp_picture); return true; } @@ -904,33 +930,36 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t const double window_opacity = animatable_get(&w->opacity); double factor_center = 1.0; + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); + auto blur_kerns_cache = session_get_blur_kern_cache(ps); // Adjust blur strength according to window opacity, to make it appear // better during fading - if (!ps->o.blur_background_fixed) { + if (!options->blur_background_fixed) { double pct = 1.0 - window_opacity * (1.0 - 1.0 / 9.0); factor_center = pct * 8.0 / (1.1 - pct); } - switch (ps->o.backend) { + switch (options->backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { // Normalize blur kernels - for (int i = 0; i < ps->o.blur_kernel_count; i++) { + for (int i = 0; i < options->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]; + auto kern_src = options->blur_kerns[i]; + auto kern_dst = 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) { + if (options->blur_background_fixed && kern_dst) { continue; } x_create_convolution_kernel(kern_src, factor_center, - &ps->blur_kerns_cache[i]); + &blur_kerns_cache[i]); } xcb_render_picture_t td = XCB_NONE; @@ -941,29 +970,29 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t make_rounded_window_shape(traps, max_ntraps, cr, wid, hei); td = x_create_picture_with_standard( - &ps->c, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0); + c, 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.c, XCB_RENDER_PICT_OP_SRC, td, + xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_SRC, td, trans, 1, &rect); - auto solid = solid_picture(&ps->c, false, 1, 0, 0, 0); + auto solid = solid_picture(c, false, 1, 0, 0, 0); xcb_render_trapezoids( - ps->c.c, XCB_RENDER_PICT_OP_OVER, solid, td, - x_get_pictfmt_for_standard(&ps->c, XCB_PICT_STANDARD_A_8), 0, - 0, n, traps); - x_free_picture(&ps->c, solid); + c->c, XCB_RENDER_PICT_OP_OVER, solid, td, + x_get_pictfmt_for_standard(c, XCB_PICT_STANDARD_A_8), 0, 0, n, + traps); + x_free_picture(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) { + if (w->mode == WMODE_FRAME_TRANS && !options->force_win_blend) { region_t reg_noframe; pixman_region32_init(®_noframe); win_get_region_noframe_local(w, ®_noframe); @@ -974,19 +1003,19 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t // Translate global coordinates to local ones pixman_region32_translate(®_blur, -x, -y); - xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, - ps->o.blur_kernel_count, ®_blur, td); + xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, blur_kerns_cache, + options->blur_kernel_count, ®_blur, td); if (td) { - x_free_picture(&ps->c, td); + x_free_picture(c, td); } pixman_region32_clear(®_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, + case BKEND_GLX: { + glx_blur_dst(ps, x, y, wid, hei, (float)session_get_psglx(ps)->z - 0.5F, (float)factor_center, reg_paint, &w->glx_blur_cache); break; + } #endif default: assert(0); } @@ -999,20 +1028,15 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t /// region = ?? /// region_real = the damage region void paint_all(session_t *ps, struct managed_win *t) { - if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) { - if (ps->xsync_exists && !x_fence_sync(&ps->c, ps->sync_fence)) { - log_error("x_fence_sync failed, xrender-sync-fence will be " - "disabled from now on."); - xcb_sync_destroy_fence(ps->c.c, ps->sync_fence); - ps->sync_fence = XCB_NONE; - ps->o.xrender_sync_fence = false; - ps->xsync_exists = false; - } - } + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); + auto tgt_picture = session_get_tgt_picture(ps); + session_xsync_wait_fence(ps); region_t region; pixman_region32_init(®ion); - damage_ring_collect(&ps->damage_ring, &ps->screen_reg, ®ion, get_buffer_age(ps)); + damage_ring_collect(session_get_damage_ring(ps), session_get_screen_reg(ps), + ®ion, get_buffer_age(ps)); if (!pixman_region32_not_empty(®ion)) { return; } @@ -1021,40 +1045,42 @@ void paint_all(session_t *ps, struct managed_win *t) { static struct timespec last_paint = {0}; #endif - if (ps->o.resize_damage > 0) { - resize_region_in_place(®ion, ps->o.resize_damage, ps->o.resize_damage); + if (options->resize_damage > 0) { + resize_region_in_place(®ion, options->resize_damage, options->resize_damage); } // Remove the damaged area out of screen - pixman_region32_intersect(®ion, ®ion, &ps->screen_reg); + pixman_region32_intersect(®ion, ®ion, session_get_screen_reg(ps)); - 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, ps->c.screen_info->root_depth, - ps->root_width, ps->root_height); - if (ps->tgt_buffer.pixmap == XCB_NONE) { + if (!paint_isvalid(ps, session_get_tgt_buffer(ps))) { + auto tgt_buffer = session_get_tgt_buffer(ps); + auto root_extent = session_get_root_extent(ps); + if (!tgt_buffer->pixmap) { + free_paint(ps, tgt_buffer); + tgt_buffer->pixmap = + x_create_pixmap(c, c->screen_info->root_depth, + root_extent.width, root_extent.height); + if (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->c.screen_info->root_visual, ps->tgt_buffer.pixmap, - 0, 0); + if (BKEND_GLX != options->backend) { + tgt_buffer->pict = x_create_picture_with_visual_and_pixmap( + c, c->screen_info->root_visual, + session_get_tgt_buffer(ps)->pixmap, 0, 0); } } - if (BKEND_XRENDER == ps->o.backend) { - x_set_picture_clip_region(&ps->c, ps->tgt_picture, 0, 0, ®ion); + if (BKEND_XRENDER == options->backend) { + x_set_picture_clip_region(c, tgt_picture, 0, 0, ®ion); } #ifdef CONFIG_OPENGL if (bkend_use_glx(ps)) { - ps->psglx->z = 0; + session_get_psglx(ps)->z = 0; } #endif @@ -1101,9 +1127,9 @@ void paint_all(session_t *ps, struct managed_win *t) { pixman_region32_subtract(®_tmp, ®ion, 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(®_tmp, ®_tmp, - &ps->shadow_exclude_reg); + auto shadow_exclude_reg = session_get_shadow_exclude_reg(ps); + if (pixman_region32_not_empty(shadow_exclude_reg)) { + pixman_region32_subtract(®_tmp, ®_tmp, shadow_exclude_reg); } if (pixman_region32_not_empty(®_shadow_clip)) { pixman_region32_subtract(®_tmp, ®_tmp, ®_shadow_clip); @@ -1120,12 +1146,13 @@ void paint_all(session_t *ps, struct managed_win *t) { // 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) { + if (!options->wintype_option[w->window_type].full_shadow) { pixman_region32_subtract(®_tmp, ®_tmp, &bshape_no_corners); } - if (ps->o.crop_shadow_to_monitor && w->randr_monitor >= 0 && - w->randr_monitor < ps->monitors.count) { + auto monitors = session_get_monitors(ps); + if (options->crop_shadow_to_monitor && w->randr_monitor >= 0 && + w->randr_monitor < monitors->count) { // There can be a window where number of monitors is // updated, but the monitor number attached to the window // have not. @@ -1134,8 +1161,7 @@ void paint_all(session_t *ps, struct managed_win *t) { // here we just check to make sure we don't access out of // bounds. pixman_region32_intersect( - ®_tmp, ®_tmp, - &ps->monitors.regions[w->randr_monitor]); + ®_tmp, ®_tmp, &monitors->regions[w->randr_monitor]); } // Detect if the region is empty before painting @@ -1173,7 +1199,7 @@ void paint_all(session_t *ps, struct managed_win *t) { #ifdef CONFIG_OPENGL // If rounded corners backup the region first - if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) { + if (w->corner_radius > 0 && options->backend == BKEND_GLX) { const int16_t x = w->g.x; const int16_t y = w->g.y; auto const wid = to_u16_checked(w->widthb); @@ -1185,9 +1211,10 @@ void paint_all(session_t *ps, struct managed_win *t) { // 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, ®_tmp); + (options->blur_background_frame && w->mode == WMODE_FRAME_TRANS) || + options->force_win_blend)) { + win_blur_background( + ps, w, session_get_tgt_buffer(ps)->pict, ®_tmp); } // Painting the window @@ -1196,12 +1223,12 @@ void paint_all(session_t *ps, struct managed_win *t) { #ifdef CONFIG_OPENGL // Rounded corners for XRender is implemented inside render() // Round window corners - if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) { + if (w->corner_radius > 0 && options->backend == BKEND_GLX) { auto const wid = to_u16_checked(w->widthb); auto const 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)session_get_psglx(ps)->z - 0.5F, (float)w->corner_radius, ®_tmp); } #endif @@ -1213,18 +1240,18 @@ void paint_all(session_t *ps, struct managed_win *t) { pixman_region32_fini(®_shadow_clip); // Move the head of the damage ring - damage_ring_advance(&ps->damage_ring); + damage_ring_advance(session_get_damage_ring(ps)); // Do this as early as possible - set_tgt_clip(ps, &ps->screen_reg); + set_tgt_clip(ps, session_get_screen_reg(ps)); - if (ps->o.vsync) { + if (options->vsync) { // Make sure all previous requests are processed to achieve best // effect - xcb_aux_sync(ps->c.c); + xcb_aux_sync(c->c); #ifdef CONFIG_OPENGL if (glx_has_context(ps)) { - if (ps->o.vsync_use_glfinish) { + if (options->vsync_use_glfinish) { glFinish(); } else { glFlush(); @@ -1234,78 +1261,78 @@ void paint_all(session_t *ps, struct managed_win *t) { #endif } - if (ps->vsync_wait) { - ps->vsync_wait(ps); - } + session_vsync_wait(ps); - auto rwidth = to_u16_checked(ps->root_width); - auto rheight = to_u16_checked(ps->root_height); - switch (ps->o.backend) { + auto root_extent = session_get_root_extent(ps); + auto rwidth = to_u16_checked(root_extent.width); + auto rheight = to_u16_checked(root_extent.height); + auto tgt_buffer = session_get_tgt_buffer(ps); + switch (options->backend) { case BKEND_XRENDER: - if (ps->o.monitor_repaint) { + if (options->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->c.screen_info->root_visual); + auto pictfmt = + x_get_pictform_for_visual(c, c->screen_info->root_visual); xcb_render_picture_t new_pict = x_create_picture_with_pictfmt( - &ps->c, rwidth, rheight, pictfmt, 0, NULL); - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, - ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, - 0, 0, 0, 0, 0, rwidth, rheight); + c, rwidth, rheight, pictfmt, 0, NULL); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, + 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, ®ion); - xcb_render_composite(ps->c.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); + x_set_picture_clip_region(c, new_pict, 0, 0, ®ion); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_OVER, + session_get_white_picture(ps), + session_get_alpha_pictures(ps)[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.c, XCB_RENDER_PICT_OP_SRC, new_pict, - XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0, - rwidth, rheight); - x_free_picture(&ps->c, new_pict); + auto screen_reg = session_get_screen_reg(ps); + x_set_picture_clip_region(c, new_pict, 0, 0, screen_reg); + x_set_picture_clip_region(c, tgt_picture, 0, 0, screen_reg); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, new_pict, XCB_NONE, + tgt_picture, 0, 0, 0, 0, 0, 0, rwidth, rheight); + x_free_picture(c, new_pict); } else { - xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, - ps->tgt_buffer.pict, XCB_NONE, ps->tgt_picture, - 0, 0, 0, 0, 0, 0, rwidth, rheight); + xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, + tgt_buffer->pict, XCB_NONE, tgt_picture, 0, + 0, 0, 0, 0, 0, rwidth, rheight); } break; #ifdef CONFIG_OPENGL case BKEND_XR_GLX_HYBRID: - xcb_aux_sync(ps->c.c); - if (ps->o.vsync_use_glfinish) { + xcb_aux_sync(c->c); + if (options->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->c.screen_info->root_depth, - ps->c.screen_info->root_visual, !ps->o.glx_no_rebind_pixmap); - if (ps->o.vsync_use_glfinish) { + assert(tgt_buffer->pixmap); + paint_bind_tex(ps, tgt_buffer, root_extent.width, root_extent.height, + false, c->screen_info->root_depth, + c->screen_info->root_visual, !options->glx_no_rebind_pixmap); + if (options->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, ®ion, NULL); + glx_render(ps, tgt_buffer->ptex, 0, 0, 0, 0, root_extent.width, + root_extent.height, 0, 1.0, false, false, ®ion, NULL); fallthrough(); - case BKEND_GLX: glXSwapBuffers(ps->c.dpy, get_tgt_window(ps)); break; + case BKEND_GLX: glXSwapBuffers(c->dpy, session_get_target_window(ps)); break; #endif default: assert(0); } - xcb_aux_sync(ps->c.c); + xcb_aux_sync(c->c); #ifdef CONFIG_OPENGL if (glx_has_context(ps)) { @@ -1333,9 +1360,11 @@ void paint_all(session_t *ps, struct managed_win *t) { * Query needed X Render / OpenGL filters to check for their existence. */ static bool xr_init_blur(session_t *ps) { + auto c = session_get_x_connection(ps); + bool xrfilter_convolution_exists = false; // Query filters xcb_render_query_filters_reply_t *pf = xcb_render_query_filters_reply( - ps->c.c, xcb_render_query_filters(ps->c.c, get_tgt_window(ps)), NULL); + c->c, xcb_render_query_filters(c->c, session_get_target_window(ps)), NULL); if (pf) { xcb_str_iterator_t iter = xcb_render_query_filters_filters_iterator(pf); for (; iter.rem; xcb_str_next(&iter)) { @@ -1344,14 +1373,14 @@ static bool xr_init_blur(session_t *ps) { // Check for the convolution filter if (strlen(XRFILTER_CONVOLUTION) == len && !memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION))) { - ps->xrfilter_convolution_exists = true; + xrfilter_convolution_exists = true; } } free(pf); } // Turn features off if any required filter is not present - if (!ps->xrfilter_convolution_exists) { + if (!xrfilter_convolution_exists) { log_error("Xrender convolution filter " "unsupported by your X server. " "Background blur is not possible."); @@ -1361,30 +1390,16 @@ static bool xr_init_blur(session_t *ps) { 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, 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) { + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); + if (options->backend == BKEND_DUMMY) { return false; } // Initialize OpenGL as early as possible #ifdef CONFIG_OPENGL - glxext_init(ps->c.dpy, ps->c.screen); + glxext_init(c->dpy, c->screen); #endif if (bkend_use_glx(ps)) { #ifdef CONFIG_OPENGL @@ -1403,9 +1418,11 @@ bool init_render(session_t *ps) { } // Initialize window GL shader - if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { + if (BKEND_GLX == options->backend && options->glx_fshader_win_str) { #ifdef CONFIG_OPENGL - if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win)) { + auto psglx = session_get_psglx(ps); + if (!glx_load_prog_main(NULL, options->glx_fshader_win_str, + &psglx->glx_prog_win)) { return false; } #else @@ -1415,24 +1432,30 @@ bool init_render(session_t *ps) { #endif } - if (!init_alpha_picts(ps)) { - log_error("Failed to init alpha pictures."); - return false; + auto alpha_picts = session_get_alpha_pictures(ps); + for (int i = 0; i <= MAX_ALPHA; ++i) { + double o = (double)i / MAX_ALPHA; + alpha_picts[i] = solid_picture(c, false, o, 0, 0, 0); + if (alpha_picts[i] == XCB_NONE) { + log_error("Failed to init alpha pictures."); + return false; + } } // Blur filter - if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL) { + if (options->blur_method && options->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; + options->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 *); + if (options->blur_method == BLUR_METHOD_KERNEL) { + auto blur_kerns_cache = + ccalloc(options->blur_kernel_count, struct x_convolution_kernel *); + session_set_blur_kern_cache(ps, blur_kerns_cache); bool ret = false; - if (ps->o.backend == BKEND_GLX) { + if (options->backend == BKEND_GLX) { #ifdef CONFIG_OPENGL ret = glx_init_blur(ps); #else @@ -1446,29 +1469,34 @@ bool init_render(session_t *ps) { } } - ps->black_picture = solid_picture(&ps->c, true, 1, 0, 0, 0); - ps->white_picture = solid_picture(&ps->c, true, 1, 1, 1, 1); + auto black_picture = solid_picture(c, true, 1, 0, 0, 0); + auto white_picture = solid_picture(c, true, 1, 1, 1, 1); - if (ps->black_picture == XCB_NONE || ps->white_picture == XCB_NONE) { + if (black_picture == XCB_NONE || white_picture == XCB_NONE) { log_error("Failed to create solid xrender pictures."); return false; } + session_set_black_picture(ps, black_picture); + session_set_white_picture(ps, white_picture); // 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; + if (options->shadow_red == 0 && options->shadow_green == 0 && + options->shadow_blue == 0) { + session_set_cshadow_picture(ps, black_picture); } else { - ps->cshadow_picture = solid_picture(&ps->c, true, 1, ps->o.shadow_red, - ps->o.shadow_green, ps->o.shadow_blue); - if (ps->cshadow_picture == XCB_NONE) { + auto cshadow_picture = + solid_picture(c, true, 1, options->shadow_red, options->shadow_green, + options->shadow_blue); + if (cshadow_picture == XCB_NONE) { log_error("Failed to create shadow picture."); return false; } + session_set_cshadow_picture(ps, cshadow_picture); } // Initialize our rounded corners fragment shader - if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) { + if (options->corner_radius > 0 && options->backend == BKEND_GLX) { #ifdef CONFIG_OPENGL if (!glx_init_rounded_corners(ps)) { log_error("Failed to init rounded corners shader."); @@ -1485,53 +1513,63 @@ bool init_render(session_t *ps) { * Free root tile related things. */ void free_root_tile(session_t *ps) { - x_free_picture(&ps->c, ps->root_tile_paint.pict); + auto c = session_get_x_connection(ps); + auto root_tile_paint = session_get_root_tile_paint(ps); + x_free_picture(c, root_tile_paint->pict); #ifdef CONFIG_OPENGL - free_texture(ps, &ps->root_tile_paint.ptex); + free_texture(ps, &root_tile_paint->ptex); #else - assert(!ps->root_tile_paint.ptex); + assert(!root_tile_paint->ptex); #endif - if (ps->root_tile_fill) { - xcb_free_pixmap(ps->c.c, ps->root_tile_paint.pixmap); - ps->root_tile_paint.pixmap = XCB_NONE; + if (session_get_root_tile_fill(ps)) { + xcb_free_pixmap(c->c, root_tile_paint->pixmap); } - ps->root_tile_paint.pixmap = XCB_NONE; - ps->root_tile_fill = false; + root_tile_paint->pixmap = XCB_NONE; + session_set_root_tile_fill(ps, false); } void deinit_render(session_t *ps) { + auto c = session_get_x_connection(ps); // Free alpha_picts + auto alpha_picts = session_get_alpha_pictures(ps); for (int i = 0; i <= MAX_ALPHA; ++i) { - x_free_picture(&ps->c, ps->alpha_picts[i]); + x_free_picture(c, alpha_picts[i]); } - free(ps->alpha_picts); - ps->alpha_picts = NULL; // Free cshadow_picture and black_picture - if (ps->cshadow_picture != ps->black_picture) { - x_free_picture(&ps->c, ps->cshadow_picture); + auto cshadow_picture = session_get_cshadow_picture(ps); + auto black_picture = session_get_black_picture(ps); + auto white_picture = session_get_white_picture(ps); + if (cshadow_picture != black_picture) { + x_free_picture(c, cshadow_picture); } - x_free_picture(&ps->c, ps->black_picture); - x_free_picture(&ps->c, ps->white_picture); - ps->cshadow_picture = ps->black_picture = ps->white_picture = XCB_NONE; + x_free_picture(c, black_picture); + x_free_picture(c, white_picture); + session_set_black_picture(ps, XCB_NONE); + session_set_white_picture(ps, XCB_NONE); + session_set_cshadow_picture(ps, XCB_NONE); // Free other X resources free_root_tile(ps); #ifdef CONFIG_OPENGL - ps->root_tile_paint.fbcfg = (struct glx_fbconfig_info){0}; - if (bkend_use_glx(ps)) { - glx_destroy(ps); + session_get_root_tile_paint(ps)->fbcfg = (struct glx_fbconfig_info){0}; + if (glx_has_context(ps)) { + glx_destroy(ps, session_get_psglx(ps)); + session_set_psglx(ps, NULL); } #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]); + auto options = session_get_options(ps); + auto blur_kerns_cache = session_get_blur_kern_cache(ps); + if (options->blur_method != BLUR_METHOD_NONE) { + for (int i = 0; i < options->blur_kernel_count; i++) { + free(blur_kerns_cache[i]); } - free(ps->blur_kerns_cache); + free(blur_kerns_cache); } + session_set_blur_kern_cache(ps, NULL); } // vim: set ts=8 sw=8 noet : diff --git a/src/vsync.c b/src/vsync.c index 57fbb595..98adcc97 100644 --- a/src/vsync.c +++ b/src/vsync.c @@ -5,6 +5,7 @@ #include "common.h" #include "log.h" +#include "picom.h" #ifdef CONFIG_OPENGL #include "backend/gl/glx.h" @@ -105,7 +106,8 @@ static inline bool vsync_opengl_swc_swap_interval(session_t *ps, int interval) { // We don't have a context?? return false; } - glXSwapIntervalEXT(ps->c.dpy, glXGetCurrentDrawable(), interval); + glXSwapIntervalEXT(session_get_x_connection(ps)->dpy, + glXGetCurrentDrawable(), interval); return true; } return false; @@ -143,9 +145,11 @@ static int vsync_opengl_wait(session_t *ps attr_unused) { */ static int vsync_opengl_oml_wait(session_t *ps) { int64_t ust = 0, msc = 0, sbc = 0; + auto c = session_get_x_connection(ps); - glXGetSyncValuesOML(ps->c.dpy, ps->reg_win, &ust, &msc, &sbc); - glXWaitForMscOML(ps->c.dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc); + glXGetSyncValuesOML(c->dpy, session_get_target_window(ps), &ust, &msc, &sbc); + glXWaitForMscOML(c->dpy, session_get_target_window(ps), 0, 2, (msc + 1) % 2, &ust, + &msc, &sbc); return 0; } #endif @@ -164,7 +168,7 @@ bool vsync_init(session_t *ps) { log_warn("The DRM vsync method is deprecated, please don't enable it."); #endif - if (!ps->o.vsync) { + if (!session_get_options(ps)->vsync) { return true; } @@ -173,8 +177,9 @@ bool vsync_init(session_t *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. + // glXSwapBuffers will automatically wait for vsync, we don't + // need to do anything. + session_set_vsync_wait(ps, NULL); return true; } #endif @@ -184,13 +189,13 @@ bool vsync_init(session_t *ps) { #ifdef CONFIG_OPENGL if (vsync_opengl_oml_init(ps)) { log_info("Using the opengl-oml vsync method"); - ps->vsync_wait = vsync_opengl_oml_wait; + session_set_vsync_wait(ps, vsync_opengl_oml_wait); return true; } if (vsync_opengl_init(ps)) { log_info("Using the opengl vsync method"); - ps->vsync_wait = vsync_opengl_wait; + session_set_vsync_wait(ps, vsync_opengl_wait); return true; } #endif diff --git a/src/win.c b/src/win.c index 51769ea1..ee82cfc6 100644 --- a/src/win.c +++ b/src/win.c @@ -34,6 +34,7 @@ #include "utils.h" #include "win_defs.h" #include "x.h" +#include "xcb/xproto.h" #ifdef CONFIG_OPENGL // TODO(yshui) Get rid of this include @@ -62,11 +63,6 @@ static void win_update_prop_shadow_raw(struct x_connection *c, struct atom *atom struct managed_win *w); static bool win_update_prop_shadow(struct x_connection *c, struct atom *atoms, struct managed_win *w); -/** - * Update window EWMH fullscreen state. - */ -bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms, - struct managed_win *w); /** * Update leader of a window. */ @@ -96,15 +92,6 @@ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t c 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); /** @@ -120,31 +107,47 @@ static inline xcb_window_t win_get_leader(session_t *ps, struct managed_win *w) * Update focused state of a window. */ static void win_update_focused(session_t *ps, struct managed_win *w) { + auto c2_state = session_get_c2(ps); if (w->focused_force != UNSET) { w->focused = w->focused_force; } else { bool is_wmwin = win_is_wmwin(w); + auto options = session_get_options(ps); w->focused = win_is_focused_raw(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 && is_wmwin) || - (ps->o.mark_ovredir_focused && w->base.id == w->client_win && !is_wmwin) || + if (options->wintype_option[w->window_type].focus || + (options->mark_wmwin_focused && is_wmwin) || + (options->mark_ovredir_focused && w->base.id == w->client_win && !is_wmwin) || (w->a.map_state == XCB_MAP_STATE_VIEWABLE && - c2_match(ps->c2_state, w, ps->o.focus_blacklist, NULL))) { + c2_match(c2_state, w, options->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) { + auto active_leader = session_get_active_leader(ps); + if (options->track_leader && active_leader && + win_get_leader(ps, w) == active_leader) { w->focused = true; } } } +static inline void group_on_factor_change_callback(struct win *w, void *user_data) { + struct { + session_t *ps; + xcb_window_t leader; + } *data = user_data; + if (!w->managed) { + return; + } + auto mw = (struct managed_win *)w; + if (win_get_leader(data->ps, mw) == data->leader) { + win_on_factor_change(data->ps, mw); + } +} /** * Run win_on_factor_change() on all windows with the same leader window. * @@ -154,25 +157,27 @@ 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); - } - } + struct { + session_t *ps; + xcb_window_t leader; + } data = {ps, leader}; + session_foreach_win(ps, group_on_factor_change_callback, &data); } -static inline const char *win_get_name_if_managed(const struct win *w) { +static inline void group_is_focused_callback(struct win *w, void *user_data) { + struct { + session_t *ps; + xcb_window_t leader; + bool is_focused; + } *data = user_data; if (!w->managed) { - return "(unmanaged)"; + return; } auto mw = (struct managed_win *)w; - return mw->name; + if (!data->is_focused && win_get_leader(data->ps, mw) == data->leader && + win_is_focused_raw(mw)) { + data->is_focused = true; + } } /** @@ -185,19 +190,14 @@ static inline bool group_is_focused(session_t *ps, xcb_window_t leader) { if (!leader) { return false; } + struct { + session_t *ps; + xcb_window_t leader; + bool is_focused; + } data = {ps, leader, false}; + session_foreach_win(ps, group_is_focused_callback, &data); - 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(mw)) { - return true; - } - } - - return false; + return data.is_focused; } /** @@ -210,14 +210,13 @@ static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_wind // 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); + session_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(w) && cache_leader_old != cache_leader) { - ps->active_leader = cache_leader; - + session_set_active_leader(ps, cache_leader); group_on_factor_change(ps, cache_leader_old); group_on_factor_change(ps, cache_leader); } @@ -427,59 +426,61 @@ static void win_update_properties(session_t *ps, struct managed_win *w) { // we cannot receive property change when window has been destroyed assert(w->state != WSTATE_DESTROYED); - if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_TYPE)) { - if (win_update_wintype(&ps->c, ps->atoms, w)) { + auto c = session_get_x_connection(ps); + auto options = session_get_options(ps); + auto atoms = session_get_atoms(ps); + if (win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_WINDOW_TYPE)) { + if (win_update_wintype(c, atoms, w)) { win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED); } } - if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_OPACITY)) { - win_update_opacity_prop(&ps->c, ps->atoms, w, ps->o.detect_client_opacity); + if (win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_WINDOW_OPACITY)) { + win_update_opacity_prop(c, atoms, w, options->detect_client_opacity); 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->c, ps->atoms, w, w->client_win, - ps->o.frame_opacity); + if (win_fetch_and_unset_property_stale(w, atoms->a_NET_FRAME_EXTENTS)) { + win_update_frame_extents(c, atoms, w, w->client_win, options->frame_opacity); 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->c, ps->atoms, w) == 1) { + if (win_fetch_and_unset_property_stale(w, atoms->aWM_NAME) || + win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_NAME)) { + if (win_update_name(c, atoms, 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->c, ps->atoms, w)) { + if (win_fetch_and_unset_property_stale(w, atoms->aWM_CLASS)) { + if (win_update_class(c, atoms, 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->c, ps->atoms, w) == 1) { + if (win_fetch_and_unset_property_stale(w, atoms->aWM_WINDOW_ROLE)) { + if (win_update_role(c, atoms, w) == 1) { win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED); } } - if (win_fetch_and_unset_property_stale(w, ps->atoms->a_COMPTON_SHADOW)) { - if (win_update_prop_shadow(&ps->c, ps->atoms, w)) { + if (win_fetch_and_unset_property_stale(w, atoms->a_COMPTON_SHADOW)) { + if (win_update_prop_shadow(c, atoms, w)) { win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED); } } - if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_STATE)) { - if (win_update_prop_fullscreen(&ps->c, ps->atoms, w)) { + if (win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_STATE)) { + if (win_update_prop_fullscreen(c, atoms, w)) { win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED); } } - 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)) { - auto new_leader = win_get_leader_property(&ps->c, ps->atoms, w->client_win, - ps->o.detect_transient, - ps->o.detect_client_leader); + if (win_fetch_and_unset_property_stale(w, atoms->aWM_CLIENT_LEADER) || + win_fetch_and_unset_property_stale(w, atoms->aWM_TRANSIENT_FOR)) { + auto new_leader = win_get_leader_property(c, atoms, w->client_win, + options->detect_transient, + options->detect_client_leader); if (w->leader != new_leader) { win_set_leader(ps, w, new_leader); win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED); @@ -508,8 +509,11 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { // Check client first, because later property updates need accurate client // window information + auto c = session_get_x_connection(ps); + auto options = session_get_options(ps); + auto atoms = session_get_atoms(ps); if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) { - auto client_win = win_get_client_window(&ps->c, ps->atoms, w); + auto client_win = win_get_client_window(c, atoms, w); if (w->client_win && w->client_win != client_win) { win_unmark_client(ps, w); } @@ -547,10 +551,11 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { win_update_is_fullscreen(ps, w); if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { - win_on_win_size_change(w, ps->o.shadow_offset_x, - ps->o.shadow_offset_y, ps->o.shadow_radius); - win_update_bounding_shape(&ps->c, w, ps->shape_exists, - ps->o.detect_rounded_corners); + win_on_win_size_change(w, options->shadow_offset_x, + options->shadow_offset_y, + options->shadow_radius); + win_update_bounding_shape(c, w, session_has_shape_extension(ps), + options->detect_rounded_corners); damaged = true; win_clear_flags(w, WIN_FLAGS_SIZE_STALE); @@ -558,9 +563,10 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { // log_trace("free out dated pict"); win_set_flags(w, WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_FACTOR_CHANGED); - win_release_mask(ps->backend_data, w); - win_release_shadow(ps->backend_data, w); - ps->pending_updates = true; + auto backend_data = session_get_backend_data(ps); + win_release_mask(backend_data, w); + win_release_shadow(backend_data, w); + session_mark_updates_pending(ps); free_paint(ps, &w->paint); free_paint(ps, &w->shadow_paint); } @@ -570,7 +576,7 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { win_clear_flags(w, WIN_FLAGS_POSITION_STALE); } - win_update_monitor(&ps->monitors, w); + win_update_monitor(session_get_monitors(ps), w); } if (win_check_flags_all(w, WIN_FLAGS_PROPERTY_STALE)) { @@ -602,10 +608,11 @@ void win_process_image_flags(session_t *ps, struct managed_win *w) { } // Not a loop + auto backend_data = session_get_backend_data(ps); while (win_check_flags_any(w, WIN_FLAGS_PIXMAP_STALE) && !win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) { // Image needs to be updated, update it. - if (!ps->backend_data) { + if (!backend_data) { // We are using legacy backend, nothing to do here. break; } @@ -618,9 +625,9 @@ void win_process_image_flags(session_t *ps, struct managed_win *w) { 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_release_pixmap(backend_data, w); } - win_bind_pixmap(ps->backend_data, w); + win_bind_pixmap(backend_data, w); } // break here, loop always run only once @@ -848,26 +855,27 @@ static double win_calc_opacity_target(session_t *ps, const struct managed_win *w return 0; } // Try obeying opacity property and window type opacity firstly + auto options = session_get_options(ps); 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 if (!safe_isnan(options->wintype_option[w->window_type].opacity)) { + opacity = options->wintype_option[w->window_type].opacity; } else { // Respect active_opacity only when the window is physically // focused if (win_is_focused_raw(w)) { - opacity = ps->o.active_opacity; + opacity = options->active_opacity; } else if (!w->focused) { // Respect inactive_opacity in some cases - opacity = ps->o.inactive_opacity; + opacity = options->inactive_opacity; } } // respect inactive override - if (ps->o.inactive_opacity_override && !w->focused) { - opacity = ps->o.inactive_opacity; + if (options->inactive_opacity_override && !w->focused) { + opacity = options->inactive_opacity; } return opacity; @@ -880,11 +888,12 @@ static void unmap_win_finish(session_t *ps, struct managed_win *w) { w->state = WSTATE_UNMAPPED; // We are in unmap_win, this window definitely was viewable - if (ps->backend_data) { + auto backend_data = session_get_backend_data(ps); + if (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_pixmap(backend_data, w); } } else { assert(!w->win_image); @@ -940,10 +949,12 @@ bool win_should_fade(session_t *ps, const struct managed_win *w) { if (w->fade_force != UNSET) { return w->fade_force; } - if (ps->o.no_fading_openclose && w->in_openclose) { + + auto options = session_get_options(ps); + if (options->no_fading_openclose && w->in_openclose) { return false; } - if (ps->o.no_fading_destroyed_argb && w->state == WSTATE_DESTROYED && + if (options->no_fading_destroyed_argb && w->state == WSTATE_DESTROYED && win_has_alpha(w) && w->client_win && w->client_win != w->base.id) { // deprecated return false; @@ -951,7 +962,7 @@ bool win_should_fade(session_t *ps, const struct managed_win *w) { if (w->fade_excluded) { return false; } - return ps->o.wintype_option[w->window_type].fade; + return options->wintype_option[w->window_type].fade; } /// Call `animatable_set_target` on the opacity of a window, with appropriate @@ -960,8 +971,9 @@ static inline void win_start_fade(session_t *ps, struct managed_win *w, double target_blur_opacity) { double current_opacity = animatable_get(&w->opacity), target_opacity = win_calc_opacity_target(ps, w); - double step_size = - target_opacity > current_opacity ? ps->o.fade_in_step : ps->o.fade_out_step; + auto options = session_get_options(ps); + double step_size = target_opacity > current_opacity ? options->fade_in_step + : options->fade_out_step; unsigned int duration = (unsigned int)(fabs(target_opacity - current_opacity) / step_size); if (!win_should_fade(ps, w)) { @@ -997,14 +1009,8 @@ win_start_fade(session_t *ps, struct managed_win *w, double target_blur_opacity) */ 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; - } - return false; + return w->state != WSTATE_UNMAPPED && session_get_options(ps)->inactive_dim > 0 && + !w->focused; } /** @@ -1044,7 +1050,7 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new pixman_region32_init(&extents); win_extents(w, &extents); - if (ps->redirected) { + if (session_is_redirected(ps)) { // Add damage for shadow change // Window extents need update on shadow state change @@ -1060,12 +1066,12 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new // Mark the old extents as damaged if the shadow is // removed add_damage(ps, &extents); - win_release_shadow(ps->backend_data, w); + win_release_shadow(session_get_backend_data(ps), w); } // 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; + session_mark_updates_pending(ps); } w->shadow = shadow_new; @@ -1081,17 +1087,19 @@ 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; + auto options = session_get_options(ps); + auto c2_state = session_get_c2(ps); 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) { + if (!options->wintype_option[w->window_type].shadow) { log_debug("Shadow disabled by wintypes"); shadow_new = false; - } else if (c2_match(ps->c2_state, w, ps->o.shadow_blacklist, NULL)) { + } else if (c2_match(c2_state, w, options->shadow_blacklist, NULL)) { log_debug("Shadow disabled by shadow-exclude"); shadow_new = false; - } else if (ps->o.shadow_ignore_shaped && w->bounding_shaped && + } else if (options->shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) { log_debug("Shadow disabled by shadow-ignore-shaped"); shadow_new = false; @@ -1136,8 +1144,10 @@ bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms } 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->c2_state, w, ps->o.shadow_clip_list, NULL)); + auto options = session_get_options(ps); + auto c2_state = session_get_c2(ps); + bool should_crop = (options->wintype_option[w->window_type].clip_shadow_above || + c2_match(c2_state, w, options->shadow_clip_list, NULL)); w->clip_shadow_above = should_crop; } @@ -1156,11 +1166,13 @@ static void win_set_invert_color(session_t *ps, struct managed_win *w, bool inve */ static void win_determine_invert_color(session_t *ps, struct managed_win *w) { bool invert_color_new = w->invert_color; + auto c2_state = session_get_c2(ps); + auto options = session_get_options(ps); 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->c2_state, w, ps->o.invert_color_list, NULL); + invert_color_new = c2_match(c2_state, w, options->invert_color_list, NULL); } win_set_invert_color(ps, w, invert_color_new); @@ -1244,12 +1256,14 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w) return; } - bool blur_background_new = ps->o.blur_method != BLUR_METHOD_NONE; + auto options = session_get_options(ps); + auto c2_state = session_get_c2(ps); + bool blur_background_new = options->blur_method != BLUR_METHOD_NONE; if (blur_background_new) { - if (!ps->o.wintype_option[w->window_type].blur_background) { + if (!options->wintype_option[w->window_type].blur_background) { log_debug("Blur background disabled by wintypes"); blur_background_new = false; - } else if (c2_match(ps->c2_state, w, ps->o.blur_background_blacklist, NULL)) { + } else if (c2_match(c2_state, w, options->blur_background_blacklist, NULL)) { log_debug("Blur background disabled by " "blur-background-exclude"); blur_background_new = false; @@ -1264,11 +1278,13 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w) */ static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) { void *radius_override = NULL; - if (c2_match(ps->c2_state, w, ps->o.corner_radius_rules, &radius_override)) { + auto c2_state = session_get_c2(ps); + auto options = session_get_options(ps); + if (c2_match(c2_state, w, options->corner_radius_rules, &radius_override)) { log_debug("Matched corner rule! %d", w->corner_radius); } - if (ps->o.corner_radius == 0 && !radius_override) { + if (options->corner_radius == 0 && !radius_override) { w->corner_radius = 0; return; } @@ -1277,14 +1293,14 @@ static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) // unless we find a corner override in corner_radius_rules if (!radius_override && ((w && w->is_fullscreen) || - c2_match(ps->c2_state, w, ps->o.rounded_corners_blacklist, NULL))) { + c2_match(c2_state, w, options->rounded_corners_blacklist, NULL))) { w->corner_radius = 0; log_debug("Not rounding corners for window %#010x", w->base.id); } else { if (radius_override) { w->corner_radius = (int)(long)radius_override; } else { - w->corner_radius = ps->o.corner_radius; + w->corner_radius = options->corner_radius; } log_debug("Rounding corners for window %#010x", w->base.id); @@ -1302,18 +1318,14 @@ static void win_determine_fg_shader(session_t *ps, struct managed_win *w) { return; } - auto shader_new = ps->o.window_shader_fg; + auto options = session_get_options(ps); + auto c2_state = session_get_c2(ps); + auto shader_new = options->window_shader_fg; void *val = NULL; - if (c2_match(ps->c2_state, w, ps->o.window_shader_fg_rules, &val)) { + if (c2_match(c2_state, w, options->window_shader_fg_rules, &val)) { shader_new = val; } - - struct shader_info *shader = NULL; - if (shader_new) { - HASH_FIND_STR(ps->shaders, shader_new, shader); - } - - win_set_fg_shader(ps, w, shader); + win_set_fg_shader(ps, w, session_get_shader_info(ps, shader_new)); } /** @@ -1327,7 +1339,7 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) { double opacity = 1.0; bool is_set = false; void *val = NULL; - if (c2_match(ps->c2_state, w, ps->o.opacity_rules, &val)) { + if (c2_match(session_get_c2(ps), w, session_get_options(ps)->opacity_rules, &val)) { opacity = ((double)(long)val) / 100.0; is_set = true; } @@ -1343,7 +1355,11 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) { */ void win_on_factor_change(session_t *ps, struct managed_win *w) { log_debug("Window %#010x (%s) factor change", w->base.id, w->name); - c2_window_state_update(ps->c2_state, &w->c2_state, ps->c.c, w->client_win, w->base.id); + + auto c2_state = session_get_c2(ps); + auto c = session_get_x_connection(ps); + auto options = session_get_options(ps); + c2_window_state_update(c2_state, &w->c2_state, c->c, w->client_win, w->base.id); // Focus and is_fullscreen needs to be updated first, as other rules might depend // on the focused state of the window win_update_focused(ps, w); @@ -1359,17 +1375,17 @@ void win_on_factor_change(session_t *ps, struct managed_win *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->c2_state, w, ps->o.paint_blacklist, NULL); + w->paint_excluded = c2_match(c2_state, w, options->paint_blacklist, NULL); } if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) { w->unredir_if_possible_excluded = - c2_match(ps->c2_state, w, ps->o.unredir_if_possible_blacklist, NULL); + c2_match(c2_state, w, options->unredir_if_possible_blacklist, NULL); } - w->fade_excluded = c2_match(ps->c2_state, w, ps->o.fade_blacklist, NULL); + w->fade_excluded = c2_match(c2_state, w, options->fade_blacklist, NULL); w->transparent_clipping_excluded = - c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL); + c2_match(c2_state, w, options->transparent_clipping_blacklist, NULL); win_update_opacity_target(ps, w); @@ -1429,6 +1445,9 @@ bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct manag * @param client window ID of the client window */ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client) { + auto options = session_get_options(ps); + auto c = session_get_x_connection(ps); + auto atoms = session_get_atoms(ps); w->client_win = client; // If the window isn't mapped yet, stop here, as the function will be @@ -1438,45 +1457,45 @@ static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t c } auto e = xcb_request_check( - ps->c.c, xcb_change_window_attributes_checked( - ps->c.c, client, XCB_CW_EVENT_MASK, - (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)})); + c->c, xcb_change_window_attributes_checked( + c->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->c, ps->atoms, w); + win_update_wintype(c, atoms, w); // Get frame widths. The window is in damaged area already. - win_update_frame_extents(&ps->c, ps->atoms, w, client, ps->o.frame_opacity); + win_update_frame_extents(c, atoms, w, client, options->frame_opacity); // Get window group - if (ps->o.track_leader) { - auto new_leader = win_get_leader_property(&ps->c, ps->atoms, w->client_win, - ps->o.detect_transient, - ps->o.detect_client_leader); + if (options->track_leader) { + auto new_leader = win_get_leader_property(c, atoms, w->client_win, + options->detect_transient, + options->detect_client_leader); if (w->leader != new_leader) { win_set_leader(ps, w, new_leader); } } // Get window name and class if we are tracking them - win_update_name(&ps->c, ps->atoms, w); - win_update_class(&ps->c, ps->atoms, w); - win_update_role(&ps->c, ps->atoms, w); + win_update_name(c, atoms, w); + win_update_class(c, atoms, w); + win_update_role(c, atoms, w); // Update everything related to conditions win_on_factor_change(ps, w); auto r = xcb_get_window_attributes_reply( - ps->c.c, xcb_get_window_attributes(ps->c.c, w->client_win), &e); + c->c, xcb_get_window_attributes(c->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); + w->client_pictfmt = x_get_pictform_for_visual(c, r->visual); free(r); } @@ -1495,7 +1514,7 @@ void win_unmark_client(session_t *ps, struct managed_win *w) { // Recheck event mask xcb_change_window_attributes( - ps->c.c, client, XCB_CW_EVENT_MASK, + session_get_x_connection(ps)->c, client, XCB_CW_EVENT_MASK, (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)}); } @@ -1568,9 +1587,10 @@ void free_win_res(session_t *ps, struct managed_win *w) { // Above should be done during unmapping // Except when we are called by session_destroy + auto c = session_get_x_connection(ps); pixman_region32_fini(&w->bounding_shape); // BadDamage may be thrown if the window is destroyed - set_ignore_cookie(&ps->c, xcb_damage_destroy(ps->c.c, w->damage)); + set_ignore_cookie(c, xcb_damage_destroy(c->c, w->damage)); rc_region_unref(&w->reg_ignore); free(w->name); free(w->class_instance); @@ -1580,51 +1600,7 @@ void free_win_res(session_t *ps, struct managed_win *w) { free(w->stale_props); w->stale_props = NULL; w->stale_props_capacity = 0; - c2_window_state_destroy(ps->c2_state, &w->c2_state); -} - -/// 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); - } - // 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); + c2_window_state_destroy(session_get_c2(ps), &w->c2_state); } /// Query the Xorg for information about window `win`, and return that @@ -1722,7 +1698,7 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi w->is_new = false; // Reject overlay window and already added windows - if (w->id == ps->overlay) { + if (w->id == session_get_overlay(ps)) { return w; } @@ -1734,11 +1710,11 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi return &duplicated_win->base; } + auto c = session_get_x_connection(ps); log_debug("Managing window %#010x", w->id); - xcb_get_window_attributes_cookie_t acookie = - xcb_get_window_attributes(ps->c.c, w->id); + xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(c->c, w->id); xcb_get_window_attributes_reply_t *a = - xcb_get_window_attributes_reply(ps->c.c, acookie, NULL); + xcb_get_window_attributes_reply(c->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 @@ -1775,7 +1751,7 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi free(a); xcb_generic_error_t *e; - auto g = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, w->id), &e); + auto g = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, w->id), &e); if (!g) { log_error_x_error(e, "Failed to get geometry of window %#010x", w->id); free(e); @@ -1793,10 +1769,10 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi free(g); // Create Damage for window (if not Input Only) - new->damage = x_new_id(&ps->c); + new->damage = x_new_id(c); e = xcb_request_check( - ps->c.c, xcb_damage_create_checked(ps->c.c, new->damage, w->id, - XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY)); + c->c, xcb_damage_create_checked(c->c, new->damage, w->id, + XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY)); if (e) { log_error_x_error(e, "Failed to create damage"); free(e); @@ -1806,15 +1782,15 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi // Set window event mask xcb_change_window_attributes( - ps->c.c, new->base.id, XCB_CW_EVENT_MASK, + c->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.c, new->base.id, 1); + if (session_has_shape_extension(ps)) { + xcb_shape_select_input(c->c, new->base.id, 1); } - new->pictfmt = x_get_pictform_for_visual(&ps->c, new->a.visual); + new->pictfmt = x_get_pictform_for_visual(c, new->a.visual); new->client_pictfmt = NULL; // Set all the stale flags on this new window, so it's properties will get @@ -1822,16 +1798,19 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi win_set_flags(new, WIN_FLAGS_CLIENT_STALE | WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE | WIN_FLAGS_PROPERTY_STALE | WIN_FLAGS_FACTOR_CHANGED); + + auto atoms = session_get_atoms(ps); + auto c2_state = session_get_c2(ps); 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, - ps->atoms->a_NET_WM_STATE, + atoms->a_NET_WM_WINDOW_TYPE, atoms->a_NET_WM_WINDOW_OPACITY, + atoms->a_NET_FRAME_EXTENTS, atoms->aWM_NAME, + atoms->a_NET_WM_NAME, atoms->aWM_CLASS, + atoms->aWM_WINDOW_ROLE, atoms->a_COMPTON_SHADOW, + atoms->aWM_CLIENT_LEADER, atoms->aWM_TRANSIENT_FOR, + atoms->a_NET_WM_STATE, }; win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props)); - c2_window_state_init(ps->c2_state, &new->c2_state); + c2_window_state_init(c2_state, &new->c2_state); return &new->base; } @@ -1871,7 +1850,7 @@ static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int // 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); + auto wp = session_find_toplevel(ps, w->cache_leader); if (wp) { // Dead loop? if (recursions > WIN_GET_LEADER_MAX_RECURSION) { @@ -1930,23 +1909,22 @@ bool win_update_class(struct x_connection *c, struct atom *atoms, struct managed * Handle window focus change. */ static void win_on_focus_change(session_t *ps, struct managed_win *w) { + auto options = session_get_options(ps); + // If window grouping detection is enabled - if (ps->o.track_leader) { + if (options->track_leader) { xcb_window_t leader = win_get_leader(ps, w); + xcb_window_t active_leader_old = session_get_active_leader(ps); - // If the window gets focused, replace the old active_leader - if (win_is_focused_raw(w) && leader != ps->active_leader) { - xcb_window_t active_leader_old = ps->active_leader; - - ps->active_leader = leader; - + if (win_is_focused_raw(w) && leader != active_leader_old) { + // If the window gets focused, replace the old active_leader + session_set_active_leader(ps, 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(w) && leader && - leader == ps->active_leader && !group_is_focused(ps, leader)) { - ps->active_leader = XCB_NONE; + } else if (!win_is_focused_raw(w) && leader && + leader == active_leader_old && !group_is_focused(ps, leader)) { + // If the group get unfocused, remove it from active_leader + session_set_active_leader(ps, XCB_NONE); group_on_factor_change(ps, leader); } } @@ -1955,7 +1933,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) { win_on_factor_change(ps, w); // Send D-Bus signal - if (ps->o.dbus) { + if (options->dbus) { if (win_is_focused_raw(w)) { cdbus_ev_win_focusin(session_get_cdbus(ps), &w->base); } else { @@ -1974,12 +1952,12 @@ void win_set_focused(session_t *ps, struct managed_win *w) { } if (w->is_ewmh_focused) { - assert(ps->active_win == w); + assert(session_get_active_win(ps) == w); return; } - auto old_active_win = ps->active_win; - ps->active_win = w; + auto old_active_win = session_get_active_win(ps); + session_set_active_win(ps, w); w->is_ewmh_focused = true; if (old_active_win) { @@ -2144,23 +2122,12 @@ void win_update_frame_extents(struct x_connection *c, struct atom *atoms, 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.c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0}); + auto c = session_get_x_connection(ps)->c; + xcb_change_window_attributes(c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0}); if (!w->managed) { return; @@ -2168,12 +2135,12 @@ void win_ev_stop(session_t *ps, const struct win *w) { auto mw = (struct managed_win *)w; if (mw->client_win) { - xcb_change_window_attributes(ps->c.c, mw->client_win, XCB_CW_EVENT_MASK, + xcb_change_window_attributes(c, mw->client_win, XCB_CW_EVENT_MASK, (const uint32_t[]){0}); } - if (ps->shape_exists) { - xcb_shape_select_input(ps->c.c, w->id, 0); + if (session_has_shape_extension(ps)) { + xcb_shape_select_input(c, w->id, 0); } } @@ -2182,16 +2149,17 @@ void win_ev_stop(session_t *ps, const struct win *w) { void destroy_win_finish(session_t *ps, struct win *w) { log_verbose("Trying to finish destroying (%#010x)", w->id); - auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour); + auto next_w = session_get_next_managed_win_in_stack(ps, &w->stack_neighbour); list_remove(&w->stack_neighbour); if (w->managed) { auto mw = (struct managed_win *)w; + auto backend_data = session_get_backend_data(ps); unmap_win_finish(ps, mw); // Unmapping might preserve the shadow image, so free it here - win_release_shadow(ps->backend_data, mw); - win_release_mask(ps->backend_data, mw); + win_release_shadow(backend_data, mw); + win_release_mask(backend_data, mw); // Invalidate reg_ignore of windows below this one // TODO(yshui) what if next_w is not mapped?? @@ -2205,7 +2173,7 @@ void destroy_win_finish(session_t *ps, struct win *w) { next_w->reg_ignore_valid = false; } - if (mw == ps->active_win) { + if (mw == session_get_active_win(ps)) { // 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 @@ -2213,7 +2181,7 @@ void destroy_win_finish(session_t *ps, struct win *w) { log_debug("window %#010x (%s) is destroyed while being " "focused", w->id, mw->name); - ps->active_win = NULL; + session_set_active_win(ps, NULL); } free_win_res(ps, mw); @@ -2221,7 +2189,7 @@ void destroy_win_finish(session_t *ps, struct win *w) { // 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) { + win_stack_foreach_managed(w2, session_get_win_stack(ps)) { if (mw == w2->prev_trans) { w2->prev_trans = NULL; } @@ -2230,92 +2198,6 @@ void destroy_win_finish(session_t *ps, struct win *w) { free(w); } - -/// 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. void destroy_win_start(session_t *ps, struct win *w) { @@ -2331,7 +2213,7 @@ void destroy_win_start(session_t *ps, struct win *w) { // 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); + session_delete_win(ps, w); if (w->managed) { if (mw->state != WSTATE_UNMAPPED) { @@ -2388,11 +2270,11 @@ void destroy_win_start(session_t *ps, struct win *w) { // don't need win_ev_stop because the window is gone anyway // Send D-Bus signal - if (ps->o.dbus) { + if (session_get_options(ps)->dbus) { cdbus_ev_win_destroyed(session_get_cdbus(ps), w); } - if (!ps->redirected && w->managed) { + if (!session_is_redirected(ps) && w->managed) { // Skip transition if we are not rendering win_skip_fading(mw); } @@ -2423,11 +2305,11 @@ void unmap_win_start(session_t *ps, struct managed_win *w) { win_start_fade(ps, w, 0); // Send D-Bus signal - if (ps->o.dbus) { + if (session_get_options(ps)->dbus) { cdbus_ev_win_unmapped(session_get_cdbus(ps), &w->base); } - if (!ps->redirected || !w->ever_damaged) { + if (!session_is_redirected(ps) || !w->ever_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 @@ -2469,7 +2351,7 @@ void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) { /// Map an already registered window void map_win_start(session_t *ps, struct managed_win *w) { - assert(ps->server_grabbed); + session_assert_server_grabbed(ps); assert(w); // Don't care about window mapping if it's an InputOnly window @@ -2509,11 +2391,11 @@ void map_win_start(session_t *ps, struct managed_win *w) { animatable_get(&w->opacity), w->opacity.target); // Send D-Bus signal - if (ps->o.dbus) { + if (session_get_options(ps)->dbus) { cdbus_ev_win_mapped(session_get_cdbus(ps), &w->base); } - if (!ps->redirected) { + if (!session_is_redirected(ps)) { win_skip_fading(w); } } @@ -2532,30 +2414,16 @@ void win_update_opacity_target(session_t *ps, struct managed_win *w) { log_debug("Window %#010x (%s) opacity %f, opacity target %f, start %f", w->base.id, w->name, animatable_get(&w->opacity), w->opacity.target, w->opacity.start); - if (!ps->redirected) { + if (!session_is_redirected(ps)) { win_skip_fading(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); + struct win *w = session_find_win(ps, id); if (!w || !w->managed) { return NULL; } @@ -2565,32 +2433,6 @@ struct managed_win *find_managed_win(session_t *ps, xcb_window_t id) { 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`. * @@ -2600,19 +2442,19 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) { */ 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 + // session_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->c.screen_info->root && !(w = find_win(ps, wid))) { + auto c = session_get_x_connection(ps); + while (wid && wid != c->screen_info->root && !(w = session_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.c, xcb_query_tree(ps->c.c, wid), NULL); + auto reply = xcb_query_tree_reply(c->c, xcb_query_tree(c->c, wid), NULL); if (reply == NULL) { break; } @@ -2712,14 +2554,16 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) { * * It's not using w->border_size for performance measures. */ -void win_update_is_fullscreen(const session_t *ps, struct managed_win *w) { - if (!ps->o.no_ewmh_fullscreen && w->is_ewmh_fullscreen) { +void win_update_is_fullscreen(session_t *ps, struct managed_win *w) { + auto options = session_get_options(ps); + if (!options->no_ewmh_fullscreen && w->is_ewmh_fullscreen) { w->is_fullscreen = true; return; } + auto root_extent = session_get_root_extent(ps); w->is_fullscreen = w->g.x <= 0 && w->g.y <= 0 && - (w->g.x + w->widthb) >= ps->root_width && - (w->g.y + w->heightb) >= ps->root_height && + (w->g.x + w->widthb) >= root_extent.width && + (w->g.y + w->heightb) >= root_extent.height && (!w->bounding_shaped || w->rounded_corners); } @@ -2728,11 +2572,12 @@ void win_update_is_fullscreen(const session_t *ps, struct managed_win *w) { * * TODO(yshui) cache this property */ -bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w) { +bool win_is_bypassing_compositor(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); + auto prop = x_get_prop(session_get_x_connection(ps), w->client_win, + session_get_atoms(ps)->a_NET_WM_BYPASS_COMPOSITOR, 1L, + XCB_ATOM_CARDINAL, 32); if (prop.nitems && *prop.c32 == 1) { ret = true; @@ -2749,16 +2594,3 @@ bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win * bool win_is_focused_raw(const struct managed_win *w) { return w->a.map_state == XCB_MAP_STATE_VIEWABLE && w->is_ewmh_focused; } - -// 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 *w) { - while (!list_node_is_last(&ps->window_stack, w)) { - auto next = list_entry(w->next, struct win, stack_neighbour); - if (next->managed) { - return (struct managed_win *)next; - } - w = &next->stack_neighbour; - } - return NULL; -} diff --git a/src/win.h b/src/win.h index 8198343f..3ba5f2bd 100644 --- a/src/win.h +++ b/src/win.h @@ -340,11 +340,11 @@ bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w); void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw); /// Recheck if a window is fullscreen -void win_update_is_fullscreen(const session_t *ps, struct managed_win *w); +void win_update_is_fullscreen(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); +bool win_is_bypassing_compositor(session_t *ps, const struct managed_win *w); /** * Get a rectangular region in global coordinates a window (and possibly * its shadow) occupies. @@ -373,20 +373,9 @@ void win_get_region_noframe_local_without_corners(const struct managed_win *w, r 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 *attr_ret_nonnull maybe_allocate_managed_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); /** * Release a destroyed window that is no longer needed. @@ -403,8 +392,6 @@ void win_skip_fading(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`. * @@ -429,15 +416,16 @@ static inline bool attr_pure win_is_wmwin(const struct managed_win *w) { return w->base.id == w->client_win && !w->a.override_redirect; } -/// 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); +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; +} /// 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 diff --git a/src/x.c b/src/x.c index c82bd984..8dd55284 100644 --- a/src/x.c +++ b/src/x.c @@ -21,6 +21,7 @@ #include #include "atom.h" +#include "picom.h" #ifdef CONFIG_OPENGL #include "backend/gl/glx.h" #endif @@ -49,7 +50,7 @@ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { xcb_err.major_code = ev->request_code; xcb_err.minor_code = ev->minor_code; xcb_err.error_code = ev->error_code; - x_handle_error(&ps_g->c, &xcb_err); + x_handle_error(session_get_x_connection(ps_g), &xcb_err); return 0; } @@ -588,13 +589,13 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c case XCB_##s: name = #s; break // TODO(yshui) separate error code out from session_t - o = error_code - ps->xfixes_error; + o = error_code - session_get_xfixes_extension_error(ps); switch (o) { CASESTRRET2(XFIXES_BAD_REGION); } - o = error_code - ps->damage_error; + o = error_code - session_get_damage_extension_error(ps); switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); } - o = error_code - ps->render_error; + o = error_code - session_get_render_extension_error(ps); switch (o) { CASESTRRET2(RENDER_PICT_FORMAT); CASESTRRET2(RENDER_PICTURE); @@ -603,8 +604,8 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c CASESTRRET2(RENDER_GLYPH); } - if (ps->glx_exists) { - o = error_code - ps->glx_error; + if (session_has_glx_extension(ps)) { + o = error_code - session_get_glx_extension_error(ps); switch (o) { CASESTRRET2(GLX_BAD_CONTEXT); CASESTRRET2(GLX_BAD_CONTEXT_STATE); @@ -623,8 +624,8 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c } } - if (ps->xsync_exists) { - o = error_code - ps->xsync_error; + if (session_get_xsync_extension_error(ps)) { + o = error_code - session_get_xsync_extension_error(ps); switch (o) { CASESTRRET(XSyncBadCounter); CASESTRRET(XSyncBadAlarm);