WIP core: struct sesion is now opaque

This commit is contained in:
Yuxuan Shui 2024-03-24 02:02:14 +00:00
parent 4c8515cf50
commit 959f400ae1
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
22 changed files with 1983 additions and 1548 deletions

View File

@ -9,6 +9,7 @@
#include "compiler.h" #include "compiler.h"
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include "picom.h"
#include "region.h" #include "region.h"
#include "transition.h" #include "transition.h"
#include "types.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 get_damage(session_t *ps, bool all_damage) {
region_t region; region_t region;
auto buffer_age_fn = ps->backend_data->ops->buffer_age; auto backend_data = session_get_backend_data(ps);
int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data) : -1; auto buffer_age_fn = backend_data->ops->buffer_age;
int buffer_age = buffer_age_fn ? buffer_age_fn(backend_data) : -1;
if (all_damage) { if (all_damage) {
buffer_age = -1; buffer_age = -1;
} }
pixman_region32_init(&region); pixman_region32_init(&region);
damage_ring_collect(&ps->damage_ring, &ps->screen_reg, &region, buffer_age); damage_ring_collect(session_get_damage_ring(ps), session_get_screen_reg(ps),
&region, buffer_age);
return region; return region;
} }
@ -69,7 +72,7 @@ void handle_device_reset(session_t *ps) {
// Reset picom // Reset picom
log_info("Resetting picom after device reset"); log_info("Resetting picom after device reset");
ev_break(ps->loop, EVBREAK_ALL); ev_break(session_get_mainloop(ps), EVBREAK_ALL);
} }
/// paint all windows /// paint all windows
@ -78,34 +81,31 @@ void handle_device_reset(session_t *ps) {
/// this function will return false. /// this function will return false.
bool paint_all_new(session_t *ps, struct managed_win *const t) { bool paint_all_new(session_t *ps, struct managed_win *const t) {
struct timespec now = get_time_timespec(); struct timespec now = get_time_timespec();
uint64_t after_sync_fence_us = 0, after_damage_us = 0;
auto paint_all_start_us = auto paint_all_start_us =
(uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
if (ps->backend_data->ops->device_status && auto backend_data = session_get_backend_data(ps);
ps->backend_data->ops->device_status(ps->backend_data) != DEVICE_STATUS_NORMAL) { 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); handle_device_reset(ps);
return false; return false;
} }
if (ps->o.xrender_sync_fence) { session_xsync_wait_fence(ps);
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;
}
}
if (unlikely(log_get_level_tls() > LOG_LEVEL_TRACE)) {
now = get_time_timespec(); now = get_time_timespec();
auto after_sync_fence_us = after_sync_fence_us =
(uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
log_trace("Time spent on sync fence: %" PRIu64 " us", log_trace("Time spent on sync fence: %" PRIu64 " us",
after_sync_fence_us - paint_all_start_us); after_sync_fence_us - paint_all_start_us);
}
// All painting will be limited to the damage, if _some_ of // All painting will be limited to the damage, if _some_ of
// the paints bleed out of the damage region, it will destroy // the paints bleed out of the damage region, it will destroy
// part of the image we want to reuse // part of the image we want to reuse
region_t reg_damage; 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(&reg_damage)) { if (!pixman_region32_not_empty(&reg_damage)) {
pixman_region32_fini(&reg_damage); pixman_region32_fini(&reg_damage);
@ -127,11 +127,12 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
/// The adjusted damaged regions /// The adjusted damaged regions
region_t reg_paint; region_t reg_paint;
assert(ps->o.blur_method != BLUR_METHOD_INVALID); region_t *screen_reg = session_get_screen_reg(ps);
if (ps->o.blur_method != BLUR_METHOD_NONE && ps->backend_data->ops->get_blur_size) { 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; int blur_width, blur_height;
ps->backend_data->ops->get_blur_size(ps->backend_blur_context, backend_data->ops->get_blur_size(backend_blur_context, &blur_width,
&blur_width, &blur_height); &blur_height);
// The region of screen a given window influences will be smeared // The region of screen a given window influences will be smeared
// out by blur. With more windows on top of the given window, the // 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); blur_height * resize_factor);
reg_paint = resize_region(&reg_damage, blur_width * resize_factor, reg_paint = resize_region(&reg_damage, blur_width * resize_factor,
blur_height * resize_factor); blur_height * resize_factor);
pixman_region32_intersect(&reg_paint, &reg_paint, &ps->screen_reg); pixman_region32_intersect(&reg_paint, &reg_paint, screen_reg);
pixman_region32_intersect(&reg_damage, &reg_damage, &ps->screen_reg); pixman_region32_intersect(&reg_damage, &reg_damage, screen_reg);
} else { } else {
pixman_region32_init(&reg_paint); pixman_region32_init(&reg_paint);
pixman_region32_copy(&reg_paint, &reg_damage); pixman_region32_copy(&reg_paint, &reg_damage);
@ -166,8 +167,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
// backend can optimize based on this info // backend can optimize based on this info
region_t reg_visible; region_t reg_visible;
pixman_region32_init(&reg_visible); pixman_region32_init(&reg_visible);
pixman_region32_copy(&reg_visible, &ps->screen_reg); pixman_region32_copy(&reg_visible, screen_reg);
if (t && !ps->o.transparent_clipping) { if (t && !options->transparent_clipping) {
// Calculate the region upon which the root window (wallpaper) is to be // Calculate the region upon which the root window (wallpaper) is to be
// painted based on the ignore region of the lowest window, if available // 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; region_t reg_shadow_clip;
pixman_region32_init(&reg_shadow_clip); pixman_region32_init(&reg_shadow_clip);
if (unlikely(log_get_level_tls() > LOG_LEVEL_TRACE)) {
now = get_time_timespec(); now = get_time_timespec();
auto after_damage_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; after_damage_us =
log_trace("Getting damage took %" PRIu64 " us", after_damage_us - after_sync_fence_us); (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
if (ps->next_render > 0) { log_trace("Getting damage took %" PRIu64 " us",
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %" PRIu64, after_damage_us - after_sync_fence_us);
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;
} }
session_record_cpu_time(ps);
if (backend_data->ops->prepare) {
backend_data->ops->prepare(backend_data, &reg_paint);
} }
if (ps->backend_data->ops->prepare) { auto root_image = session_get_root_image(ps);
ps->backend_data->ops->prepare(ps->backend_data, &reg_paint); if (root_image) {
} backend_data->ops->compose(backend_data, root_image, (coord_t){0}, NULL,
(coord_t){0}, &reg_paint, &reg_visible);
if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
(coord_t){0}, NULL, (coord_t){0},
&reg_paint, &reg_visible);
} else { } else {
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1}, backend_data->ops->fill(backend_data, (struct color){0, 0, 0, 1}, &reg_paint);
&reg_paint);
} }
// Windows are sorted from bottom to top // 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 // Whether this is beneficial is to be determined XXX
for (struct managed_win *w = t; w; w = w->prev_trans) { for (struct managed_win *w = t; w; w = w->prev_trans) {
pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore); pixman_region32_subtract(&reg_visible, screen_reg, w->reg_ignore);
assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR)); assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR));
assert(!(w->flags & WIN_FLAGS_PIXMAP_STALE)); assert(!(w->flags & WIN_FLAGS_PIXMAP_STALE));
assert(!(w->flags & WIN_FLAGS_PIXMAP_NONE)); 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); win_get_bounding_shape_global_without_corners_by_val(w);
if (!w->mask_image && (w->bounding_shaped || w->corner_radius != 0)) { 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 // 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; region_t reg_paint_in_bound;
pixman_region32_init(&reg_paint_in_bound); pixman_region32_init(&reg_paint_in_bound);
pixman_region32_intersect(&reg_paint_in_bound, &reg_bound, &reg_paint); pixman_region32_intersect(&reg_paint_in_bound, &reg_bound, &reg_paint);
if (ps->o.transparent_clipping) { if (options->transparent_clipping) {
// <transparent-clipping-note> // <transparent-clipping-note>
// If transparent_clipping is enabled, we need to be SURE that // If transparent_clipping is enabled, we need to be SURE that
// things are not drawn inside reg_ignore, because otherwise they // 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); const double window_opacity = animatable_get(&w->opacity);
if (w->blur_background && if (w->blur_background &&
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS || (options->force_win_blend || real_win_mode == WMODE_TRANS ||
(ps->o.blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) { (options->blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) {
// Minimize the region we try to blur, if the window // Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is. // 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) { if (blur_opacity * MAX_ALPHA < 1) {
// We don't need to blur if it would be completely // We don't need to blur if it would be completely
// transparent // 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 // We need to blur the bounding shape of the window
// (reg_paint_in_bound = reg_bound \cap reg_paint) // (reg_paint_in_bound = reg_bound \cap reg_paint)
ps->backend_data->ops->blur( backend_data->ops->blur(backend_data, blur_opacity,
ps->backend_data, blur_opacity, backend_blur_context,
ps->backend_blur_context, w->mask_image, window_coord, w->mask_image, window_coord,
&reg_paint_in_bound, &reg_visible); &reg_paint_in_bound, &reg_visible);
} else { } else {
// Window itself is solid, we only need to blur the frame // Window itself is solid, we only need to blur the frame
// region // region
// Readability assertions // Readability assertions
assert(ps->o.blur_background_frame); assert(options->blur_background_frame);
assert(real_win_mode == WMODE_FRAME_TRANS); assert(real_win_mode == WMODE_FRAME_TRANS);
auto reg_blur = win_get_region_frame_local_by_val(w); auto reg_blur = win_get_region_frame_local_by_val(w);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y); pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
// make sure reg_blur \in reg_paint // make sure reg_blur \in reg_paint
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint); pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint);
if (ps->o.transparent_clipping) { if (options->transparent_clipping) {
// ref: <transparent-clipping-note> // ref: <transparent-clipping-note>
pixman_region32_intersect(&reg_blur, &reg_blur, pixman_region32_intersect(&reg_blur, &reg_blur,
&reg_visible); &reg_visible);
} }
ps->backend_data->ops->blur( backend_data->ops->blur(
ps->backend_data, blur_opacity, ps->backend_blur_context, backend_data, blur_opacity, backend_blur_context,
w->mask_image, window_coord, &reg_blur, &reg_visible); w->mask_image, window_coord, &reg_blur, &reg_visible);
pixman_region32_fini(&reg_blur); pixman_region32_fini(&reg_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 // 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. // has shadow enabled because it disables shadow for a window on failure.
if (w->shadow && !w->shadow_image) { if (w->shadow && !w->shadow_image) {
struct color shadow_color = {.red = ps->o.shadow_red, struct color shadow_color = {.red = options->shadow_red,
.green = ps->o.shadow_green, .green = options->shadow_green,
.blue = ps->o.shadow_blue, .blue = options->shadow_blue,
.alpha = ps->o.shadow_opacity}; .alpha = options->shadow_opacity};
win_bind_shadow(ps->backend_data, w, shadow_color, ps->shadow_context); win_bind_shadow(backend_data, w, shadow_color,
session_get_backend_shadow_context(ps));
} }
// Draw shadow on target // Draw shadow on target
@ -316,17 +312,19 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_paint); pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_paint);
// Mask out the region we don't want shadow on // 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(&reg_shadow, &reg_shadow, pixman_region32_subtract(&reg_shadow, &reg_shadow,
&ps->shadow_exclude_reg); shadow_exclude_reg);
} }
if (pixman_region32_not_empty(&reg_shadow_clip)) { if (pixman_region32_not_empty(&reg_shadow_clip)) {
pixman_region32_subtract(&reg_shadow, &reg_shadow, pixman_region32_subtract(&reg_shadow, &reg_shadow,
&reg_shadow_clip); &reg_shadow_clip);
} }
if (ps->o.crop_shadow_to_monitor && w->randr_monitor >= 0 && auto monitors = session_get_monitors(ps);
w->randr_monitor < ps->monitors.count) { 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 // There can be a window where number of monitors is
// updated, but the monitor number attached to the window // updated, but the monitor number attached to the window
// have not. // 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 // Window monitor number will be updated eventually, so
// here we just check to make sure we don't access out of // here we just check to make sure we don't access out of
// bounds. // bounds.
pixman_region32_intersect( pixman_region32_intersect(&reg_shadow, &reg_shadow,
&reg_shadow, &reg_shadow, &monitors->regions[w->randr_monitor]);
&ps->monitors.regions[w->randr_monitor]);
} }
if (ps->o.transparent_clipping) { if (options->transparent_clipping) {
// ref: <transparent-clipping-note> // ref: <transparent-clipping-note>
pixman_region32_intersect(&reg_shadow, &reg_shadow, pixman_region32_intersect(&reg_shadow, &reg_shadow,
&reg_visible); &reg_visible);
} }
assert(w->shadow_image); assert(w->shadow_image);
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image, backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image, &w->opacity);
&w->opacity);
coord_t shadow_coord = {.x = w->g.x + w->shadow_dx, coord_t shadow_coord = {.x = w->g.x + w->shadow_dx,
.y = w->g.y + w->shadow_dy}; .y = w->g.y + w->shadow_dy};
auto inverted_mask = NULL; 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(&reg_shadow, &reg_shadow, pixman_region32_subtract(&reg_shadow, &reg_shadow,
&reg_bound_no_corner); &reg_bound_no_corner);
if (w->mask_image) { if (w->mask_image) {
inverted_mask = w->mask_image; inverted_mask = w->mask_image;
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_INVERTED, backend_data, IMAGE_PROPERTY_INVERTED,
inverted_mask, (bool[]){true}); inverted_mask, (bool[]){true});
} }
} }
ps->backend_data->ops->compose( backend_data->ops->compose(backend_data, w->shadow_image,
ps->backend_data, w->shadow_image, shadow_coord, shadow_coord, inverted_mask,
inverted_mask, window_coord, &reg_shadow, &reg_visible); window_coord, &reg_shadow, &reg_visible);
if (inverted_mask) { if (inverted_mask) {
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_INVERTED, backend_data, IMAGE_PROPERTY_INVERTED, inverted_mask,
inverted_mask, (bool[]){false}); (bool[]){false});
} }
pixman_region32_fini(&reg_shadow); pixman_region32_fini(&reg_shadow);
} }
@ -378,26 +374,25 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
{ {
double dim_opacity = 0.0; double dim_opacity = 0.0;
if (w->dim) { if (w->dim) {
dim_opacity = ps->o.inactive_dim; dim_opacity = options->inactive_dim;
if (!ps->o.inactive_dim_fixed) { if (!options->inactive_dim_fixed) {
dim_opacity *= window_opacity; dim_opacity *= window_opacity;
} }
} }
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image, backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image,
&ps->o.max_brightness); &options->max_brightness);
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_INVERTED, w->win_image, backend_data, IMAGE_PROPERTY_INVERTED, w->win_image,
&w->invert_color); &w->invert_color);
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(backend_data,
ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, w->win_image, IMAGE_PROPERTY_DIM_LEVEL,
&dim_opacity); w->win_image, &dim_opacity);
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, &window_opacity);
&window_opacity); backend_data->ops->set_image_property(
ps->backend_data->ops->set_image_property( backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image,
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image,
(double[]){w->corner_radius}); (double[]){w->corner_radius});
if (w->corner_radius) { if (w->corner_radius) {
int border_width = w->g.border_width; 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.right,
w->frame_extents.bottom); w->frame_extents.bottom);
} }
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH, backend_data, IMAGE_PROPERTY_BORDER_WIDTH,
w->win_image, &border_width); w->win_image, &border_width);
} }
ps->backend_data->ops->set_image_property( backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_CUSTOM_SHADER, w->win_image, backend_data, IMAGE_PROPERTY_CUSTOM_SHADER, w->win_image,
w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL); w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL);
} }
@ -433,7 +428,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
// Draw window on target // Draw window on target
if (w->frame_opacity == 1) { if (w->frame_opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image, backend_data->ops->compose(backend_data, w->win_image,
window_coord, NULL, window_coord, window_coord, NULL, window_coord,
&reg_paint_in_bound, &reg_visible); &reg_paint_in_bound, &reg_visible);
} else { } else {
@ -466,17 +461,17 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
&reg_visible_local, &reg_visible_local, &reg_bound_local); &reg_visible_local, &reg_visible_local, &reg_bound_local);
} }
auto new_img = ps->backend_data->ops->clone_image( auto new_img = backend_data->ops->clone_image(
ps->backend_data, w->win_image, &reg_visible_local); backend_data, w->win_image, &reg_visible_local);
auto reg_frame = win_get_region_frame_local_by_val(w); auto reg_frame = win_get_region_frame_local_by_val(w);
ps->backend_data->ops->image_op( backend_data->ops->image_op(backend_data, IMAGE_OP_APPLY_ALPHA,
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame, new_img, &reg_frame, &reg_visible_local,
&reg_visible_local, (double[]){w->frame_opacity}); (double[]){w->frame_opacity});
pixman_region32_fini(&reg_frame); pixman_region32_fini(&reg_frame);
ps->backend_data->ops->compose(ps->backend_data, new_img, backend_data->ops->compose(backend_data, new_img, window_coord,
window_coord, NULL, window_coord, NULL, window_coord,
&reg_paint_in_bound, &reg_visible); &reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img); backend_data->ops->release_image(backend_data, new_img);
pixman_region32_fini(&reg_visible_local); pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local); pixman_region32_fini(&reg_bound_local);
} }
@ -488,20 +483,20 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
pixman_region32_fini(&reg_paint); pixman_region32_fini(&reg_paint);
pixman_region32_fini(&reg_shadow_clip); pixman_region32_fini(&reg_shadow_clip);
if (ps->o.monitor_repaint) { if (options->monitor_repaint) {
const struct color DEBUG_COLOR = {0.5, 0, 0, 0.5}; const struct color DEBUG_COLOR = {0.5, 0, 0, 0.5};
auto reg_damage_debug = get_damage(ps, false); auto reg_damage_debug = get_damage(ps, false);
ps->backend_data->ops->fill(ps->backend_data, DEBUG_COLOR, &reg_damage_debug); backend_data->ops->fill(backend_data, DEBUG_COLOR, &reg_damage_debug);
pixman_region32_fini(&reg_damage_debug); pixman_region32_fini(&reg_damage_debug);
} }
// Move the head of the damage ring // 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 // Present the rendered scene
// Vsync is done here // Vsync is done here
ps->backend_data->ops->present(ps->backend_data, &reg_damage); backend_data->ops->present(backend_data, &reg_damage);
} }
pixman_region32_fini(&reg_damage); pixman_region32_fini(&reg_damage);

View File

@ -12,6 +12,7 @@
#include "config.h" #include "config.h"
#include "kernel.h" #include "kernel.h"
#include "log.h" #include "log.h"
#include "picom.h"
#include "utils.h" #include "utils.h"
#include "win.h" #include "win.h"
#include "x.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) { void init_backend_base(struct backend_base *base, session_t *ps) {
base->c = &ps->c; base->c = session_get_x_connection(ps);
base->loop = ps->loop; base->loop = session_get_mainloop(ps);
base->busy = false; base->busy = false;
base->ops = NULL; base->ops = NULL;
} }

View File

@ -15,7 +15,7 @@
/// Apply driver specified global workarounds. It's safe to call this multiple times. /// Apply driver specified global workarounds. It's safe to call this multiple times.
void apply_driver_workarounds(struct session *ps, enum driver driver) { void apply_driver_workarounds(struct session *ps, enum driver driver) {
if (driver & DRIVER_NVIDIA) { if (driver & DRIVER_NVIDIA) {
ps->o.xrender_sync_fence = true; session_get_options(ps)->xrender_sync_fence = true;
} }
} }

View File

@ -30,9 +30,7 @@ struct dummy_data {
struct backend_base *dummy_init(session_t *ps attr_unused, xcb_window_t target attr_unused) { 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); auto ret = (struct backend_base *)ccalloc(1, struct dummy_data);
ret->c = &ps->c; init_backend_base(ret, ps);
ret->loop = ps->loop;
ret->busy = false;
return ret; return ret;
} }

View File

@ -136,10 +136,11 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
} }
gd = ccalloc(1, struct egl_data); 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[]){ (EGLint[]){
EGL_PLATFORM_X11_SCREEN_EXT, EGL_PLATFORM_X11_SCREEN_EXT,
ps->c.screen, c->screen,
EGL_NONE, EGL_NONE,
}); });
if (gd->display == EGL_NO_DISPLAY) { if (gd->display == EGL_NO_DISPLAY) {
@ -172,7 +173,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
goto end; 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; EGLConfig config = NULL;
int nconfigs = 1; int nconfigs = 1;
// clang-format off // 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.decouple_texture_user_data = egl_decouple_user_data;
gd->gl.release_user_data = egl_release_image; 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)) { if (!egl_set_swap_interval(1, gd->display)) {
log_error("Failed to enable vsync. %#x", eglGetError()); log_error("Failed to enable vsync. %#x", eglGetError());
} }

View File

@ -13,6 +13,7 @@
#include "config.h" #include "config.h"
#include "kernel.h" #include "kernel.h"
#include "log.h" #include "log.h"
#include "picom.h"
#include "region.h" #include "region.h"
#include "string_utils.h" #include "string_utils.h"
#include "types.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) { void gl_root_change(backend_t *base, session_t *ps) {
auto gd = (struct gl_data *)base; 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]); glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0); glUseProgram(0);
gd->dithered_present = ps->o.dithered_present; gd->dithered_present = session_get_options(ps)->dithered_present;
gd->dummy_prog = gd->dummy_prog =
gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL}, gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL},
(const char *[]){dummy_frag, 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 *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16}
: (const GLint[]){GL_RGB8, GL_RGBA8}; : (const GLint[]){GL_RGB8, GL_RGBA8};
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
auto root_extent = session_get_root_extent(ps);
gd->back_format = format[i]; 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, glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, gd->back_texture, 0); GL_TEXTURE_2D, gd->back_texture, 0);

View File

@ -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) { static backend_t *glx_init(session_t *ps, xcb_window_t target) {
bool success = false; 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); auto gd = ccalloc(1, struct _glx_data);
init_backend_base(&gd->gl.base, ps); 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; XVisualInfo *pvis = NULL;
// Check for GLX extension // Check for GLX extension
if (!ps->glx_exists) { if (!session_has_glx_extension(ps)) {
log_error("No GLX extension."); log_error("No GLX extension.");
goto end; goto end;
} }
// Get XVisualInfo // Get XVisualInfo
int nitems = 0; int nitems = 0;
XVisualInfo vreq = {.visualid = ps->c.screen_info->root_visual}; XVisualInfo vreq = {.visualid = c->screen_info->root_visual};
pvis = XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems); pvis = XGetVisualInfo(c->dpy, VisualIDMask, &vreq, &nitems);
if (!pvis) { if (!pvis) {
log_error("Failed to acquire XVisualInfo for current visual."); log_error("Failed to acquire XVisualInfo for current visual.");
goto end; goto end;
@ -270,22 +271,22 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
// Ensure the visual is double-buffered // Ensure the visual is double-buffered
int value = 0; 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."); log_error("Root visual is not a GL visual.");
goto end; 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."); log_error("Root visual lacks stencil buffer.");
goto end; 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."); log_error("Root visual is not a double buffered GL visual.");
goto end; 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"); log_error("Root visual is a color index visual, not supported");
goto end; 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 // 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. // be sure that the fbconfig is compatible with our target window.
int ncfgs; int ncfgs;
GLXFBConfig *cfg = glXGetFBConfigs(ps->c.dpy, ps->c.screen, &ncfgs); GLXFBConfig *cfg = glXGetFBConfigs(c->dpy, c->screen, &ncfgs);
bool found = false; bool found = false;
for (int i = 0; i < ncfgs; i++) { for (int i = 0; i < ncfgs; i++) {
int visualid; 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) { if ((VisualID)visualid != pvis->visualid) {
continue; 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; 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); free(cfg);
if (!gd->ctx) { if (!gd->ctx) {
@ -344,7 +345,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
// Attach GLX context // Attach GLX context
GLXDrawable tgt = gd->target_win; 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."); log_error("Failed to attach GLX context.");
goto end; 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.decouple_texture_user_data = glx_decouple_user_data;
gd->gl.release_user_data = glx_release_image; gd->gl.release_user_data = glx_release_image;
if (ps->o.vsync) { if (session_get_options(ps)->vsync) {
if (!glx_set_swap_interval(1, ps->c.dpy, tgt)) { if (!glx_set_swap_interval(1, c->dpy, tgt)) {
log_error("Failed to enable vsync."); log_error("Failed to enable vsync.");
} }
} else { } else {
glx_set_swap_interval(0, ps->c.dpy, tgt); glx_set_swap_interval(0, c->dpy, tgt);
} }
success = true; success = true;

View File

@ -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) { 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."); log_warn("\"dithered-present\" is not supported by the xrender backend.");
} }
@ -902,29 +904,28 @@ static backend_t *xrender_init(session_t *ps, xcb_window_t target) {
for (int i = 0; i <= MAX_ALPHA; ++i) { for (int i = 0; i <= MAX_ALPHA; ++i) {
double o = (double)i / (double)MAX_ALPHA; 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); assert(xd->alpha_pict[i] != XCB_NONE);
} }
xd->target_width = ps->root_width; auto root_extent = session_get_root_extent(ps);
xd->target_height = ps->root_height; xd->target_width = root_extent.width;
xd->black_pixel = solid_picture(&ps->c, true, 1, 0, 0, 0); xd->target_height = root_extent.height;
xd->white_pixel = solid_picture(&ps->c, true, 1, 1, 1, 1); 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; xd->target_win = target;
xcb_render_create_picture_value_list_t pa = { xcb_render_create_picture_value_list_t pa = {
.subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS, .subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS,
}; };
xd->target = x_create_picture_with_visual_and_pixmap( xd->target = x_create_picture_with_visual_and_pixmap(
&ps->c, ps->c.screen_info->root_visual, xd->target_win, c, c->screen_info->root_visual, xd->target_win, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
xd->vsync = ps->o.vsync; xd->vsync = options->vsync;
if (ps->present_exists) { if (session_has_present_extension(ps)) {
auto eid = x_new_id(&ps->c); auto eid = x_new_id(c);
auto e = auto e = xcb_request_check(c->c, xcb_present_select_input_checked(
xcb_request_check(ps->c.c, xcb_present_select_input_checked( c->c, eid, xd->target_win,
ps->c.c, eid, xd->target_win,
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY)); XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
if (e) { if (e) {
log_error("Cannot select present input, vsync will be disabled"); log_error("Cannot select present input, vsync will be disabled");
@ -933,7 +934,7 @@ static backend_t *xrender_init(session_t *ps, xcb_window_t target) {
} }
xd->present_event = 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) { if (!xd->present_event) {
log_error("Cannot register for special XGE, vsync will be " log_error("Cannot register for special XGE, vsync will be "
"disabled"); "disabled");
@ -944,22 +945,22 @@ static backend_t *xrender_init(session_t *ps, xcb_window_t target) {
} }
if (xd->vsync) { 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 // We might need to do double buffering for vsync, and buffer 0 and 1 are for
// double buffering. // double buffering.
int first_buffer_index = xd->vsync ? 0 : 2; int first_buffer_index = xd->vsync ? 0 : 2;
for (int i = first_buffer_index; i < 3; i++) { for (int i = first_buffer_index; i < 3; i++) {
xd->back_pixmap[i] = x_create_pixmap(&ps->c, ps->c.screen_info->root_depth, xd->back_pixmap[i] = x_create_pixmap(c, c->screen_info->root_depth,
to_u16_checked(ps->root_width), to_u16_checked(root_extent.width),
to_u16_checked(ps->root_height)); to_u16_checked(root_extent.height));
const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT; const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT;
const xcb_render_create_picture_value_list_t pic_attrs = { const xcb_render_create_picture_value_list_t pic_attrs = {
.repeat = XCB_RENDER_REPEAT_PAD}; .repeat = XCB_RENDER_REPEAT_PAD};
xd->back[i] = x_create_picture_with_visual_and_pixmap( xd->back[i] = x_create_picture_with_visual_and_pixmap(
&ps->c, ps->c.screen_info->root_visual, xd->back_pixmap[i], c, c->screen_info->root_visual, xd->back_pixmap[i], pic_attrs_mask,
pic_attrs_mask, &pic_attrs); &pic_attrs);
xd->buffer_age[i] = -1; xd->buffer_age[i] = -1;
if (xd->back_pixmap[i] == XCB_NONE || xd->back[i] == XCB_NONE) { if (xd->back_pixmap[i] == XCB_NONE || xd->back[i] == XCB_NONE) {
log_error("Cannot create pixmap for rendering"); log_error("Cannot create pixmap for rendering");

View File

@ -145,249 +145,7 @@ struct damage_ring {
/// Number of damage regions we track /// Number of damage regions we track
int count; int count;
}; };
struct session;
/// 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 <code>XRectangle</code>-s of exposed region.
/// XXX why do we need this array?
rect_t *expose_rects;
/// Number of <code>XRectangle</code>-s in <code>expose_rects</code>.
int size_expose;
/// Index of the next free slot in <code>expose_rects</code>.
int n_expose;
// === Window related ===
/// A hash table of all windows.
struct win *windows;
/// Windows in their stacking order
struct list_node window_stack;
/// Pointer to <code>win</code> of current active window. Used by
/// EWMH <code>_NET_ACTIVE_WINDOW</code> focus detection. In theory,
/// it's more reliable to store the window ID directly here, just in
/// case the WM does something extraordinary, but caching the pointer
/// means another layer of complexity.
struct managed_win *active_win;
/// Window ID of leader window of currently active window. Used for
/// subsidiary window detection.
xcb_window_t active_leader;
// === Shadow/dimming related ===
/// 1x1 black Picture.
xcb_render_picture_t black_picture;
/// 1x1 Picture of the shadow color.
xcb_render_picture_t cshadow_picture;
/// 1x1 white Picture.
xcb_render_picture_t white_picture;
/// 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;
/// Enumeration for window event hints. /// Enumeration for window event hints.
typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, WIN_EVMODE_CLIENT } win_evmode_t; 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 tm;
} }
/** struct options *session_get_options(session_t *ps);
* 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;
}
/** /**
* Check if current backend uses GLX. * Check if current backend uses GLX.
*/ */
static inline bool bkend_use_glx(session_t *ps) { 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]); 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) { region_t *region, int buffer_age) {
if (buffer_age == -1 || buffer_age > ring->count) { if (buffer_age == -1 || buffer_age > ring->count) {
pixman_region32_copy(region, all_region); pixman_region32_copy(region, all_region);

View File

@ -25,6 +25,7 @@
#include "compiler.h" #include "compiler.h"
#include "kernel.h" #include "kernel.h"
#include "log.h" #include "log.h"
#include "picom.h"
#include "region.h" #include "region.h"
#include "string_utils.h" #include "string_utils.h"
#include "types.h" #include "types.h"
@ -420,12 +421,14 @@ bool parse_geometry(session_t *ps, const char *src, region_t *dest) {
if (!src) { if (!src) {
return true; 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; return true;
} }
long x = 0, y = 0; 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; long val = 0L;
char *endptr = NULL; char *endptr = NULL;
@ -472,7 +475,7 @@ bool parse_geometry(session_t *ps, const char *src, region_t *dest) {
if (endptr && src != endptr) { if (endptr && src != endptr) {
x = val; x = val;
if (*src == '-') { if (*src == '-') {
x += ps->root_width - width; x += root_extent.width - width;
} }
src = endptr; src = endptr;
} }
@ -485,7 +488,7 @@ bool parse_geometry(session_t *ps, const char *src, region_t *dest) {
if (endptr && src != endptr) { if (endptr && src != endptr) {
y = val; y = val;
if (*src == '-') { if (*src == '-') {
y += ps->root_height - height; y += root_extent.height - height;
} }
src = endptr; src = endptr;
} }

View File

@ -24,12 +24,11 @@
#include "config.h" #include "config.h"
#include "dbus/dbus-protocol.h" #include "dbus/dbus-protocol.h"
#include "dbus/dbus-shared.h" #include "dbus/dbus-shared.h"
#include "list.h"
#include "log.h" #include "log.h"
#include "picom.h"
#include "string_utils.h" #include "string_utils.h"
#include "transition.h" #include "transition.h"
#include "types.h" #include "types.h"
#include "uthash_extra.h"
#include "utils.h" #include "utils.h"
#include "win.h" #include "win.h"
#include "win_defs.h" #include "win_defs.h"
@ -163,7 +162,7 @@ struct cdbus_data *cdbus_init(session_t *ps, const char *uniq) {
} }
// Add watch handlers // 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, if (!dbus_connection_set_watch_functions(cd->dbus_conn, cdbus_callback_add_watch,
cdbus_callback_remove_watch, cdbus_callback_remove_watch,
cdbus_callback_watch_toggled, cd, NULL)) { cdbus_callback_watch_toggled, cd, NULL)) {
@ -472,41 +471,45 @@ static bool cdbus_append_string_variant(DBusMessage *msg, const char *data) {
return true; 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 /// 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) { static bool cdbus_append_wids(DBusMessage *msg, session_t *ps) {
// Get the number of wids we are to include auto count = session_get_window_count(ps);
unsigned count = 0;
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
++count;
}
if (!count) { if (!count) {
// Nothing to append
return true; return true;
} }
// Allocate memory for an array of window IDs DBusMessageIter iter;
auto arr = ccalloc(count, cdbus_window_t); struct cdbus_append_wids_data callback_data = {.success = true};
dbus_message_iter_init_append(msg, &iter);
// Build the array if (!dbus_message_iter_open_container(
cdbus_window_t *pcur = arr; &iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &callback_data.iter)) {
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
*pcur = w->id;
++pcur;
}
assert(pcur == arr + count);
// Append arguments
if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, &arr,
count, DBUS_TYPE_INVALID)) {
log_error("Failed to append argument."); log_error("Failed to append argument.");
free(arr);
return false; 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; 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); append_win_property(Name, name, string_variant);
if (!strcmp("Next", target)) { if (!strcmp("Next", target)) {
cdbus_window_t next_id = 0; auto next_win = session_get_next_win_in_stack(ps, &w->base);
if (!list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)) { cdbus_window_t next_id = next_win ? next_win->id : XCB_NONE;
next_id = list_entry(w->base.stack_neighbour.next, struct win,
stack_neighbour)
->id;
}
if (!cdbus_append_wid_variant(reply, next_id)) { if (!cdbus_append_wid_variant(reply, next_id)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY; 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) { DBusMessage *reply, DBusError *e attr_unused) {
// Reset the compositor // Reset the compositor
log_info("picom is resetting..."); 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)) { if (reply != NULL && !cdbus_append_boolean(reply, true)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY; 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) #define append_win_property(tgt, type) append(tgt, type, w->tgt)
if (!strcmp("next", target)) { if (!strcmp("next", target)) {
xcb_window_t next_id = auto next_win = session_get_next_win_in_stack(ps, &w->base);
list_node_is_last(&ps->window_stack, &w->base.stack_neighbour) xcb_window_t next_id = next_win ? next_win->id : XCB_NONE;
? 0
: list_entry(w->base.stack_neighbour.next, struct win, stack_neighbour)
->id;
if (!cdbus_append_wid(reply, next_id)) { if (!cdbus_append_wid(reply, next_id)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY; 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); dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
return DBUS_HANDLER_RESULT_HANDLED; return DBUS_HANDLER_RESULT_HANDLED;
} }
auto w = find_toplevel(ps, client); auto w = session_find_toplevel(ps, client);
if (w) { if (w) {
wid = w->base.id; wid = w->base.id;
} }
} else if (!strcmp("focused", target)) { } else if (!strcmp("focused", target)) {
// Find focused window // Find focused window
if (ps->active_win && ps->active_win->state != WSTATE_UNMAPPED) { auto active_win = session_get_active_win(ps);
wid = ps->active_win->base.id; if (active_win && active_win->state != WSTATE_UNMAPPED) {
wid = active_win->base.id;
} }
} else { } else {
log_debug(CDBUS_ERROR_BADTGT_S, target); 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); dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
return DBUS_HANDLER_RESULT_HANDLED; return DBUS_HANDLER_RESULT_HANDLED;
} }
assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0]));
#define append(tgt, type, ret) \ #define append(tgt, type, ret) \
if (!strcmp(#tgt, target)) { \ 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; \ 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(version, string, PICOM_VERSION);
append(pid, int32, getpid()); 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(config_file, string, "Unknown");
append(paint_on_overlay, boolean, ps->overlay != XCB_NONE); append(paint_on_overlay, boolean, session_get_overlay(ps) != XCB_NONE);
append(paint_on_overlay_id, uint32, ps->overlay); // Sending ID of the X append(paint_on_overlay_id, uint32, session_get_overlay(ps));
// composite overlay append(unredir_if_possible_delay, int32, (int32_t)options->unredir_if_possible_delay);
// window
append(unredir_if_possible_delay, int32, (int32_t)ps->o.unredir_if_possible_delay);
append(refresh_rate, int32, 0); append(refresh_rate, int32, 0);
append(sw_opti, boolean, false); 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(unredir_if_possible, boolean);
append_session_option(write_pid_path, string); 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; 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. * 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) { \ if (strcmp(#tgt, target) == 0) { \
type val; \ type val; \
get_msg_arg(dbus_type, val); \ get_msg_arg(dbus_type, val); \
ps->o.tgt = expr; \ options->tgt = expr; \
goto cdbus_process_opts_set_success; \ 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; 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_delta, INT32, int32_t, max2(val, 1));
opts_set_do(fade_in_step, DOUBLE, double, normalize_d(val)); opts_set_do(fade_in_step, DOUBLE, double, normalize_d(val));
opts_set_do(fade_out_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)) { if (!strcmp("unredir_if_possible", target)) {
dbus_bool_t val = FALSE; dbus_bool_t val = FALSE;
get_msg_arg(BOOLEAN, val); get_msg_arg(BOOLEAN, val);
if (ps->o.unredir_if_possible != val) { if (options->unredir_if_possible != val) {
ps->o.unredir_if_possible = val; options->unredir_if_possible = val;
queue_redraw(ps); queue_redraw(ps);
} }
goto cdbus_process_opts_set_success; 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)) { if (!strcmp("redirected_force", target)) {
cdbus_enum_t val = UNSET; cdbus_enum_t val = UNSET;
get_msg_arg(UINT32, val); get_msg_arg(UINT32, val);
if (ps->o.redirected_force != val) { if (options->redirected_force != val) {
ps->o.redirected_force = val; options->redirected_force = val;
force_repaint(ps); force_repaint(ps);
} }
goto cdbus_process_opts_set_success; 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, "<node name='%#010x'/>\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. * 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; return DBUS_HANDLER_RESULT_HANDLED;
} }
char *ret = NULL; struct cdbus_window_root_introspect_data callback_data = {.introspect = NULL,
bool success = true; .success = true};
mstrextend(&ret, str_introspect); mstrextend(&callback_data.introspect, str_introspect);
session_foreach_win(ps, cdbus_window_root_introspect_callback, &callback_data);
mstrextend(&callback_data.introspect, "</node>");
HASH_ITER2(ps->windows, w) { if (callback_data.success) {
assert(!w->destroyed); callback_data.success = cdbus_append_string(reply, callback_data.introspect);
if (!w->managed) {
continue;
} }
char *tmp = NULL;
if (asprintf(&tmp, "<node name='%#010x'/>\n", w->id) < 0) {
log_fatal("Failed to allocate memory.");
success = false;
break;
}
mstrextend(&ret, tmp);
free(tmp);
}
mstrextend(&ret, "</node>");
if (success) { free(callback_data.introspect);
success = cdbus_append_string(reply, ret); return callback_data.success ? DBUS_HANDLER_RESULT_HANDLED
} : DBUS_HANDLER_RESULT_NEED_MEMORY;
free(ret);
return success ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NEED_MEMORY;
} }
static bool cdbus_process_window_introspect(DBusMessage *reply) { static bool cdbus_process_window_introspect(DBusMessage *reply) {

View File

@ -15,12 +15,12 @@ void print_diagnostics(session_t *ps, const char *config_file, bool compositor_r
printf("**Version:** " PICOM_VERSION "\n"); printf("**Version:** " PICOM_VERSION "\n");
// printf("**CFLAGS:** %s\n", "??"); // printf("**CFLAGS:** %s\n", "??");
printf("\n### Extensions:\n\n"); printf("\n### Extensions:\n\n");
printf("* Shape: %s\n", ps->shape_exists ? "Yes" : "No"); printf("* Shape: %s\n", session_has_shape_extension(ps) ? "Yes" : "No");
printf("* RandR: %s\n", ps->randr_exists ? "Yes" : "No"); printf("* RandR: %s\n", session_has_randr_extension(ps) ? "Yes" : "No");
printf("* Present: %s\n", ps->present_exists ? "Present" : "Not Present"); printf("* Present: %s\n", session_has_present_extension(ps) ? "Present" : "Not Present");
printf("\n### Misc:\n\n"); printf("\n### Misc:\n\n");
printf("* Use Overlay: %s\n", ps->overlay != XCB_NONE ? "Yes" : "No"); printf("* Use Overlay: %s\n", session_get_overlay(ps) != XCB_NONE ? "Yes" : "No");
if (ps->overlay == XCB_NONE) { if (session_get_overlay(ps) == XCB_NONE) {
if (compositor_running) { if (compositor_running) {
printf(" (Another compositor is already running)\n"); printf(" (Another compositor is already running)\n");
} else if (session_redirection_mode(ps) != XCB_COMPOSITE_REDIRECT_MANUAL) { } 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 #endif
printf("* Config file used: %s\n", config_file ?: "None"); printf("* Config file used: %s\n", config_file ?: "None");
printf("\n### Drivers (inaccurate):\n\n"); printf("\n### Drivers (inaccurate):\n\n");
print_drivers(ps->drivers); print_drivers(session_get_driver(ps));
for (int i = 0; i < NUM_BKEND; i++) { for (int i = 0; i < NUM_BKEND; i++) {
if (backend_list[i] && backend_list[i]->diagnostics) { if (backend_list[i] && backend_list[i]->diagnostics) {

View File

@ -23,6 +23,7 @@
#include "win.h" #include "win.h"
#include "win_defs.h" #include "win_defs.h"
#include "x.h" #include "x.h"
#include "xcb/xfixes.h"
/// Event handling with X is complicated. Handling events with other events possibly /// 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 /// 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 = ""; char *name = "";
if (wid) { if (wid) {
name = "(Failed to get title)"; 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)"; name = "(Root window)";
} else if (ps->overlay == wid) { } else if (session_get_overlay(ps) == wid) {
name = "(Overlay)"; name = "(Overlay)";
} else { } else {
auto w = find_managed_win(ps, wid); auto w = find_managed_win(ps, wid);
if (!w) { if (!w) {
w = find_toplevel(ps, wid); w = session_find_toplevel(ps, wid);
} }
if (w && w->name) { 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 PropertyNotify: return ((xcb_property_notify_event_t *)ev)->window;
case ClientMessage: return ((xcb_client_message_event_t *)ev)->window; case ClientMessage: return ((xcb_client_message_event_t *)ev)->window;
default: 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; 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; 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); 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"; 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"; return "ShapeNotify";
} }
if (ps->xsync_exists) { if (session_has_xsync_extension(ps)) {
int o = ev->response_type - ps->xsync_event; int o = ev->response_type - session_get_xsync_extention_event(ps);
switch (o) { switch (o) {
CASESTRRET(XSyncCounterNotify); CASESTRRET(XSyncCounterNotify);
CASESTRRET(XSyncAlarmNotify); 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) { 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), log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
ev_focus_detail_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) { 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), log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
ev_focus_detail_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) { static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
if (ev->parent == ps->c.screen_info->root) { if (ev->parent == session_get_x_connection(ps)->screen_info->root) {
add_win_top(ps, ev->window); session_add_win_top(ps, ev->window);
} }
} }
/// Handle configure event of a regular window /// Handle configure event of a regular window
static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { 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) { if (!w) {
return; return;
} }
if (!w->managed) { if (!w->managed) {
restack_above(ps, w, ce->above_sibling); session_restack_above(ps, w, ce->above_sibling);
return; return;
} }
auto mw = (struct managed_win *)w; 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 // We check against pending_g here, because there might have been multiple
// configure notifies in this cycle, or the window could receive multiple updates // 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); win_set_flags(mw, WIN_FLAGS_FACTOR_CHANGED);
// TODO(yshui) don't set pending_updates if the window is not // TODO(yshui) don't set pending_updates if the window is not
// visible/mapped // visible/mapped
ps->pending_updates = true; session_mark_updates_pending(ps);
// At least one of the following if's is true // At least one of the following if's is true
if (position_changed) { 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 // 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 // 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) { 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 }", log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
ev->event, ev->window, ev->above_sibling, ev->override_redirect); 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); set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
} else { } else {
configure_win(ps, ev); 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) { static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
auto w = find_win(ps, ev->window); auto w = session_find_win(ps, ev->window);
auto mw = find_toplevel(ps, ev->window); auto mw = session_find_toplevel(ps, ev->window);
if (mw && mw->client_win == mw->base.id) { if (mw && mw->client_win == mw->base.id) {
// We only want _real_ frame window // We only want _real_ frame window
assert(&mw->base == w); 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) { if (mw != NULL) {
win_unmark_client(ps, mw); win_unmark_client(ps, mw);
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE); win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true; session_mark_updates_pending(ps);
return; return;
} }
log_debug("Received a destroy notify from an unknown window, %#010x", ev->window); 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) { 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 // Unmap overlay window if it got mapped but we are currently not
// in redirected state. // 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"); log_debug("Overlay is mapped while we are not redirected");
auto e = xcb_request_check( auto e = xcb_request_check(c->c, xcb_unmap_window_checked(c->c, overlay));
ps->c.c, xcb_unmap_window_checked(ps->c.c, ps->overlay));
if (e) { if (e) {
log_error("Failed to unmap the overlay window"); log_error("Failed to unmap the overlay window");
free(e); 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 // FocusIn/Out may be ignored when the window is unmapped, so we must
// recheck focus here // 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) { 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) { 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", log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
ev->window, ev->parent, ev->override_redirect); 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) { if (w_top) {
win_unmark_client(ps, w_top); win_unmark_client(ps, w_top);
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE); 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 // 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 // change (i.e. reparent again to current parent). So we check if that's
// the case // the case
auto w = find_win(ps, ev->window); auto w = session_find_win(ps, ev->window);
if (w) { if (w) {
// This window has already been reparented to root before, // 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 // so we don't need to create a new window for it, we just need to
// move it to the top // move it to the top
restack_top(ps, w); session_restack_top(ps, w);
} else { } else {
add_win_top(ps, ev->window); session_add_win_top(ps, ev->window);
} }
} else { } else {
// otherwise, find and destroy the window first // 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) {
if (w->managed) { if (w->managed) {
auto mw = (struct managed_win *)w; 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 // Reset event mask in case something wrong happens
uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN); 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 " log_debug("Window %#010x doesn't have WM_STATE property, it is "
"probably not a client window. But we will listen for " "probably not a client window. But we will listen for "
"property change in case it gains one.", "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", "client",
w_real_top->base.id, w_real_top->name); w_real_top->base.id, w_real_top->name);
win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE); win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true; session_mark_updates_pending(ps);
} else { } else {
log_debug("parent %#010x not found", ev->parent); 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}); XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
} }
} }
static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) { 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) { if (!w) {
return; return;
} }
if (ev->place == PlaceOnTop) { if (ev->place == PlaceOnTop) {
restack_top(ps, w); session_restack_top(ps, w);
} else { } 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(&region, rects, nrects);
add_damage(ps, &region);
pixman_region32_fini(&region);
}
static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) { static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
if (ev->window == ps->c.screen_info->root || auto overlay = session_get_overlay(ps);
(ps->overlay && ev->window == ps->overlay)) { if (ev->window == session_get_x_connection(ps)->screen_info->root ||
int more = ev->count + 1; (overlay && ev->window == overlay)) {
if (ps->n_expose == ps->size_expose) { region_t region;
if (ps->expose_rects) { pixman_region32_init_rect(&region, ev->x, ev->y, ev->width, ev->height);
ps->expose_rects = add_damage(ps, &region);
crealloc(ps->expose_rects, ps->size_expose + more); pixman_region32_fini(&region);
ps->size_expose += more;
} else {
ps->expose_rects = ccalloc(more, rect_t);
ps->size_expose = more;
}
}
ps->expose_rects[ps->n_expose].x1 = ev->x;
ps->expose_rects[ps->n_expose].y1 = ev->y;
ps->expose_rects[ps->n_expose].x2 = ev->x + ev->width;
ps->expose_rects[ps->n_expose].y2 = ev->y + ev->height;
ps->n_expose++;
if (ev->count == 0) {
expose_root(ps, ps->expose_rects, ps->n_expose);
ps->n_expose = 0;
}
} }
} }
static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) { 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)) { if (unlikely(log_get_level_tls() <= LOG_LEVEL_TRACE)) {
// Print out changed atom // Print out changed atom
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply( xcb_get_atom_name_reply_t *reply =
ps->c.c, xcb_get_atom_name(ps->c.c, ev->atom), NULL); xcb_get_atom_name_reply(c->c, xcb_get_atom_name(c->c, ev->atom), NULL);
const char *name = "?"; const char *name = "?";
int name_len = 1; int name_len = 1;
if (reply) { if (reply) {
@ -483,13 +470,13 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
free(reply); free(reply);
} }
if (ps->c.screen_info->root == ev->window) { if (c->screen_info->root == ev->window) {
if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) { if (options->use_ewmh_active_win && atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
// to update focus // to update focus
ps->pending_updates = true; session_mark_updates_pending(ps);
} else { } else {
// Destroy the root "image" if the wallpaper probably changed // 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); root_damaged(ps);
} }
} }
@ -498,15 +485,15 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
return; return;
} }
ps->pending_updates = true; session_mark_updates_pending(ps);
// If WM_STATE changes // 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 // 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 // Reset event mask anyway
const uint32_t evmask = const uint32_t evmask =
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN); 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}); XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
auto w_top = find_managed_window_or_parent(ps, ev->window); 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 // If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
// there are always some stupid applications. (#144) // there are always some stupid applications. (#144)
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) { if (ev->atom == atoms->a_NET_WM_WINDOW_TYPE) {
struct managed_win *w = find_toplevel(ps, ev->window); struct managed_win *w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); 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 // Unnecessary until we remove the queue_redraw in ev_handle
queue_redraw(ps); queue_redraw(ps);
} }
// If _NET_WM_WINDOW_OPACITY changes // If _NET_WM_WINDOW_OPACITY changes
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY) { if (ev->atom == atoms->a_NET_WM_WINDOW_OPACITY) {
auto w = find_managed_win(ps, ev->window) ?: find_toplevel(ps, ev->window); auto w = find_managed_win(ps, ev->window)
?: session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
// If frame extents property changes // If frame extents property changes
if (ev->atom == ps->atoms->a_NET_FRAME_EXTENTS) { if (ev->atom == atoms->a_NET_FRAME_EXTENTS) {
auto w = find_toplevel(ps, ev->window); auto w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
// If name changes // If name changes
if (ps->atoms->aWM_NAME == ev->atom || ps->atoms->a_NET_WM_NAME == ev->atom) { if (atoms->aWM_NAME == ev->atom || atoms->a_NET_WM_NAME == ev->atom) {
auto w = find_toplevel(ps, ev->window); auto w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
// If class changes // If class changes
if (ps->atoms->aWM_CLASS == ev->atom) { if (atoms->aWM_CLASS == ev->atom) {
auto w = find_toplevel(ps, ev->window); auto w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
// If role changes // If role changes
if (ps->atoms->aWM_WINDOW_ROLE == ev->atom) { if (atoms->aWM_WINDOW_ROLE == ev->atom) {
auto w = find_toplevel(ps, ev->window); auto w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
// If _COMPTON_SHADOW changes // 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); auto w = find_managed_win(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); 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 a leader property changes
if ((ps->o.detect_transient && ps->atoms->aWM_TRANSIENT_FOR == ev->atom) || if ((options->detect_transient && atoms->aWM_TRANSIENT_FOR == ev->atom) ||
(ps->o.detect_client_leader && ps->atoms->aWM_CLIENT_LEADER == ev->atom)) { (options->detect_client_leader && atoms->aWM_CLIENT_LEADER == ev->atom)) {
auto w = find_toplevel(ps, ev->window); auto w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
if (!ps->o.no_ewmh_fullscreen && ev->atom == ps->atoms->a_NET_WM_STATE) { if (!options->no_ewmh_fullscreen && ev->atom == atoms->a_NET_WM_STATE) {
auto w = find_toplevel(ps, ev->window); auto w = session_find_toplevel(ps, ev->window);
if (w) { if (w) {
win_set_property_stale(w, ev->atom); win_set_property_stale(w, ev->atom);
} }
} }
// Check for other atoms we are tracking // 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; bool change_is_on_client = false;
auto w = find_managed_win(ps, ev->window); auto w = find_managed_win(ps, ev->window);
if (!w) { if (!w) {
w = find_toplevel(ps, ev->window); w = session_find_toplevel(ps, ev->window);
change_is_on_client = true; change_is_on_client = true;
} }
if (w) { 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); change_is_on_client);
// Set FACTOR_CHANGED so rules based on properties will be // Set FACTOR_CHANGED so rules based on properties will be
// re-evaluated. // re-evaluated.
@ -621,6 +609,8 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
// Only mapped window can receive damages // Only mapped window can receive damages
assert(w->state == WSTATE_MAPPED || win_check_flags_all(w, WIN_FLAGS_MAPPED)); 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; region_t parts;
pixman_region32_init(&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. // from the X server, which implies it has received our DamageSubtract request.
if (!w->ever_damaged) { if (!w->ever_damaged) {
auto e = xcb_request_check( auto e = xcb_request_check(
ps->c.c, c->c, xcb_damage_subtract_checked(c->c, w->damage, XCB_NONE, XCB_NONE));
xcb_damage_subtract_checked(ps->c.c, w->damage, XCB_NONE, XCB_NONE));
if (e) { if (e) {
if (ps->o.show_all_xerrors) { if (options->show_all_xerrors) {
x_print_error(e->sequence, e->major_code, e->minor_code, x_print_error(e->sequence, e->major_code, e->minor_code,
e->error_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. // We only binds the window pixmap once the window is damaged.
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE); win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
ps->pending_updates = true; session_mark_updates_pending(ps);
} else { } else {
auto cookie = xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, xcb_xfixes_region_t damage_region = session_get_damage_ring(ps)->x_region;
ps->damage_ring.x_region); auto cookie = xcb_damage_subtract(c->c, w->damage, XCB_NONE, damage_region);
if (!ps->o.show_all_xerrors) { if (!options->show_all_xerrors) {
set_ignore_cookie(&ps->c, cookie); 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, pixman_region32_translate(&parts, w->g.x + w->g.border_width,
w->g.y + 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? // Why care about damage when screen is unredirected?
// We will force full-screen repaint on redirection. // We will force full-screen repaint on redirection.
if (!ps->redirected) { if (!session_is_redirected(ps)) {
pixman_region32_fini(&parts); pixman_region32_fini(&parts);
return; return;
} }
// Remove the part in the damage area that could be ignored // 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); 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; w->reg_ignore_valid = false;
win_set_flags(w, WIN_FLAGS_SIZE_STALE); win_set_flags(w, WIN_FLAGS_SIZE_STALE);
ps->pending_updates = true; session_mark_updates_pending(ps);
} }
static inline void 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) { 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) { 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); 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\"", log_debug("event %10.10s serial %#010x window %#010x \"%s\"",
ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid)); ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid));
} else { } 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://bugs.freedesktop.org/show_bug.cgi?id=35945
// https://lists.freedesktop.org/archives/xcb/2011-November/007337.html // https://lists.freedesktop.org/archives/xcb/2011-November/007337.html
auto response_type = XCB_EVENT_RESPONSE_TYPE(ev); 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) { if (proc) {
XESetWireToEvent(ps->c.dpy, response_type, proc); XESetWireToEvent(c->dpy, response_type, proc);
XEvent dummy; XEvent dummy;
// Stop Xlib from complaining about lost sequence numbers. // 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 // We only need the low 16 bits
uint16_t seq = ev->sequence; uint16_t seq = ev->sequence;
ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->c.dpy) & 0xffff); ev->sequence = (uint16_t)(LastKnownRequestProcessed(c->dpy) & 0xffff);
proc(ps->c.dpy, &dummy, (xEvent *)ev); proc(c->dpy, &dummy, (xEvent *)ev);
// Restore the sequence number // Restore the sequence number
ev->sequence = seq; ev->sequence = seq;
} }
@ -798,18 +788,21 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
case SelectionClear: case SelectionClear:
ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev); ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev);
break; 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: 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); ev_shape_notify(ps, (xcb_shape_notify_event_t *)ev);
break; break;
} }
if (ps->randr_exists && if (session_has_randr_extension(ps) &&
ev->response_type == (ps->randr_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY)) { ev->response_type == (session_get_randr_extention_event(ps) +
XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
set_root_flags(ps, ROOT_FLAGS_SCREEN_CHANGE); set_root_flags(ps, ROOT_FLAGS_SCREEN_CHANGE);
break; 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); ev_damage_notify(ps, (xcb_damage_notify_event_t *)ev);
break; break;
} }

View File

@ -23,7 +23,9 @@
#include "config.h" #include "config.h"
#include "kernel.h" #include "kernel.h"
#include "log.h" #include "log.h"
#include "picom.h"
#include "region.h" #include "region.h"
#include "render.h"
#include "string_utils.h" #include "string_utils.h"
#include "uthash_extra.h" #include "uthash_extra.h"
#include "utils.h" #include "utils.h"
@ -39,24 +41,26 @@ static inline XVisualInfo *get_visualinfo_from_visual(session_t *ps, xcb_visuali
XVisualInfo vreq = {.visualid = visual}; XVisualInfo vreq = {.visualid = visual};
int nitems = 0; int nitems = 0;
return XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems); return XGetVisualInfo(session_get_x_connection(ps)->dpy, VisualIDMask, &vreq, &nitems);
} }
/** /**
* Initialize OpenGL. * 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; bool success = false;
XVisualInfo *pvis = NULL; XVisualInfo *pvis = NULL;
glx_session_t *psglx = NULL;
auto c = session_get_x_connection(ps);
// Check for GLX extension // Check for GLX extension
if (!ps->glx_exists) { if (!session_has_glx_extension(ps)) {
log_error("No GLX extension."); log_error("No GLX extension.");
goto glx_init_end; goto glx_init_end;
} }
// Get XVisualInfo // 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) { if (!pvis) {
log_error("Failed to acquire XVisualInfo for current visual."); log_error("Failed to acquire XVisualInfo for current visual.");
goto glx_init_end; goto glx_init_end;
@ -65,13 +69,12 @@ bool glx_init(session_t *ps, bool need_render) {
// Ensure the visual is double-buffered // Ensure the visual is double-buffered
if (need_render) { if (need_render) {
int value = 0; 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."); log_error("Root visual is not a GL visual.");
goto glx_init_end; goto glx_init_end;
} }
if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) || if (Success != glXGetConfig(c->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
!value) {
log_error("Root visual is not a double buffered GL visual."); log_error("Root visual is not a double buffered GL visual.");
goto glx_init_end; goto glx_init_end;
} }
@ -83,23 +86,22 @@ bool glx_init(session_t *ps, bool need_render) {
} }
// Initialize GLX data structure // Initialize GLX data structure
if (!ps->psglx) { auto options = session_get_options(ps);
static const glx_session_t CGLX_SESSION_DEF = CGLX_SESSION_INIT; psglx = cmalloc(glx_session_t);
ps->psglx = cmalloc(glx_session_t); *psglx = (glx_session_t){.context = NULL, .glx_prog_win = GLX_PROG_MAIN_INIT};
memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t));
// +1 for the zero terminator // +1 for the zero terminator
ps->psglx->blur_passes = ccalloc(ps->o.blur_kernel_count, glx_blur_pass_t); psglx->blur_passes = ccalloc(options->blur_kernel_count, glx_blur_pass_t);
for (int i = 0; i < ps->o.blur_kernel_count; ++i) { for (int i = 0; i < options->blur_kernel_count; ++i) {
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; glx_blur_pass_t *ppass = &psglx->blur_passes[i];
ppass->unifm_factor_center = -1; ppass->unifm_factor_center = -1;
ppass->unifm_offset_x = -1; ppass->unifm_offset_x = -1;
ppass->unifm_offset_y = -1; ppass->unifm_offset_y = -1;
} }
ps->psglx->round_passes = ccalloc(1, glx_round_pass_t); psglx->round_passes = ccalloc(1, glx_round_pass_t);
glx_round_pass_t *ppass = ps->psglx->round_passes; glx_round_pass_t *ppass = psglx->round_passes;
ppass->unifm_radius = -1; ppass->unifm_radius = -1;
ppass->unifm_texcoord = -1; ppass->unifm_texcoord = -1;
ppass->unifm_texsize = -1; ppass->unifm_texsize = -1;
@ -107,14 +109,11 @@ bool glx_init(session_t *ps, bool need_render) {
ppass->unifm_borderc = -1; ppass->unifm_borderc = -1;
ppass->unifm_resolution = -1; ppass->unifm_resolution = -1;
ppass->unifm_tex_scr = -1; ppass->unifm_tex_scr = -1;
}
glx_session_t *psglx = ps->psglx;
if (!psglx->context) { if (!psglx->context) {
// Get GLX context // Get GLX context
#ifndef DEBUG_GLX_DEBUG_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 #else
{ {
GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); 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[] = { static const int attrib_list[] = {
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None}; GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None};
psglx->context = p_glXCreateContextAttribsARB( psglx->context = p_glXCreateContextAttribsARB(
ps->c.dpy, fbconfig, NULL, GL_TRUE, attrib_list); c->dpy, fbconfig, NULL, GL_TRUE, attrib_list);
} }
#endif #endif
@ -146,7 +145,7 @@ bool glx_init(session_t *ps, bool need_render) {
} }
// Attach GLX context // 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."); log_error("Failed to attach GLX context.");
goto glx_init_end; 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 // 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 // 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? // 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; GLint val = 0;
glGetIntegerv(GL_STENCIL_BITS, &val); glGetIntegerv(GL_STENCIL_BITS, &val);
if (!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); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_BLEND); glDisable(GL_BLEND);
if (!ps->o.glx_no_stencil) { if (!options->glx_no_stencil) {
// Initialize stencil buffer // Initialize stencil buffer
glClear(GL_STENCIL_BUFFER_BIT); glClear(GL_STENCIL_BUFFER_BIT);
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
@ -204,7 +203,7 @@ bool glx_init(session_t *ps, bool need_render) {
// Clear screen // Clear screen
glClearColor(0.0F, 0.0F, 0.0F, 1.0F); glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 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; success = true;
@ -213,10 +212,11 @@ glx_init_end:
XFree(pvis); XFree(pvis);
if (!success) { 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) { 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. * Destroy GLX related resources.
*/ */
void glx_destroy(session_t *ps) { void glx_destroy(struct session *ps, struct glx_session *psglx) {
if (!ps->psglx) { if (!psglx) {
return; 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 // Free GLSL shaders/programs
for (int i = 0; i < ps->o.blur_kernel_count; ++i) { for (int i = 0; i < session_get_options(ps)->blur_kernel_count; ++i) {
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; glx_blur_pass_t *ppass = &psglx->blur_passes[i];
if (ppass->frag_shader) { if (ppass->frag_shader) {
glDeleteShader(ppass->frag_shader); glDeleteShader(ppass->frag_shader);
} }
@ -255,43 +250,44 @@ void glx_destroy(session_t *ps) {
glDeleteProgram(ppass->prog); 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) { if (ppass->frag_shader) {
glDeleteShader(ppass->frag_shader); glDeleteShader(ppass->frag_shader);
} }
if (ppass->prog) { if (ppass->prog) {
glDeleteProgram(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(); gl_check_err();
// Destroy GLX context // Destroy GLX context
if (ps->psglx->context) { auto c = session_get_x_connection(ps);
glXMakeCurrent(ps->c.dpy, None, NULL); if (psglx->context) {
glXDestroyContext(ps->c.dpy, ps->psglx->context); glXMakeCurrent(c->dpy, None, NULL);
ps->psglx->context = NULL; glXDestroyContext(c->dpy, psglx->context);
psglx->context = NULL;
} }
free(ps->psglx); free(psglx);
ps->psglx = NULL; psglx = NULL;
ps->argb_fbconfig = (struct glx_fbconfig_info){0};
} }
/** /**
* Callback to run on root window size change. * Callback to run on root window size change.
*/ */
void glx_on_root_change(session_t *ps) { 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 // Initialize matrix, copied from dcompmgr
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); 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); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
} }
@ -300,12 +296,13 @@ void glx_on_root_change(session_t *ps) {
* Initialize GLX blur filter. * Initialize GLX blur filter.
*/ */
bool glx_init_blur(session_t *ps) { bool glx_init_blur(session_t *ps) {
assert(ps->o.blur_kernel_count > 0); auto options = session_get_options(ps);
assert(ps->o.blur_kerns); assert(options->blur_kernel_count > 0);
assert(ps->o.blur_kerns[0]); assert(options->blur_kerns);
assert(options->blur_kerns[0]);
// Allocate PBO if more than one blur kernel is present // 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 // Try to generate a framebuffer
GLuint fbo = 0; GLuint fbo = 0;
glGenFramebuffers(1, &fbo); glGenFramebuffers(1, &fbo);
@ -343,7 +340,8 @@ bool glx_init_blur(session_t *ps) {
" gl_FragColor = sum / (factor_center + float(%.7g));\n" " gl_FragColor = sum / (factor_center + float(%.7g));\n"
"}\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 *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D");
const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D"); const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D");
const char *shader_add = FRAG_SHADER_BLUR_ADD; const char *shader_add = FRAG_SHADER_BLUR_ADD;
@ -356,9 +354,9 @@ bool glx_init_blur(session_t *ps) {
extension = strdup(""); extension = strdup("");
} }
for (int i = 0; i < ps->o.blur_kernel_count; ++i) { for (int i = 0; i < options->blur_kernel_count; ++i) {
auto kern = ps->o.blur_kerns[i]; auto kern = options->blur_kerns[i];
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; glx_blur_pass_t *ppass = &psglx->blur_passes[i];
// Build shader // Build shader
int width = kern->w, height = kern->h; int width = kern->w, height = kern->h;
@ -505,7 +503,8 @@ bool glx_init_rounded_corners(session_t *ps) {
"\n" "\n"
"}\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 *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D");
const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D"); const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D");
char *extension = NULL; char *extension = NULL;
@ -519,7 +518,7 @@ bool glx_init_rounded_corners(session_t *ps) {
bool success = false; bool success = false;
// Build rounded corners shader // 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) + auto len = strlen(FRAG_SHADER) + strlen(extension) + strlen(sampler_type) +
strlen(texture_func) + 1; strlen(texture_func) + 1;
char *shader_str = ccalloc(len, char); 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) { int basey, int dx, int dy, int width, int height) {
if (width > 0 && height > 0) { if (width > 0 && height > 0) {
glCopyTexSubImage2D(tex_tgt, 0, dx - basex, dy - basey, dx, 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, bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, int y,
int width, int height) { int width, int height) {
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) { if (!bkend_use_glx(ps)) {
return true; 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->width = width;
ptex->height = height; ptex->height = height;
ptex->target = GL_TEXTURE_RECTANGLE; 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; 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, 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) { 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; return true;
} }
@ -734,13 +734,14 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
// Create GLX pixmap // Create GLX pixmap
int depth = 0; int depth = 0;
auto c = session_get_x_connection(ps);
if (!ptex->glpixmap) { if (!ptex->glpixmap) {
need_release = false; need_release = false;
// Retrieve pixmap parameters, if they aren't provided // Retrieve pixmap parameters, if they aren't provided
if (!width || !height) { if (!width || !height) {
auto r = xcb_get_geometry_reply( 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) { if (!r) {
log_error("Failed to query info of pixmap %#010x.", pixmap); log_error("Failed to query info of pixmap %#010x.", pixmap);
return false; 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 // pixmap-specific parameters, and this may change in the future
GLenum tex_tgt = 0; GLenum tex_tgt = 0;
if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && 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; tex_tgt = GLX_TEXTURE_2D_EXT;
} else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) { } else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) {
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; 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, 0,
}; };
ptex->glpixmap = glXCreatePixmap(ps->c.dpy, fbcfg->cfg, pixmap, attrs); ptex->glpixmap = glXCreatePixmap(c->dpy, fbcfg->cfg, pixmap, attrs);
ptex->pixmap = pixmap; ptex->pixmap = pixmap;
ptex->target = ptex->target =
(GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE); (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... // The specification requires rebinding whenever the content changes...
// We can't follow this, too slow. // We can't follow this, too slow.
if (need_release) { 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 // Cleanup
glBindTexture(ptex->target, 0); 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. * @brief Release binding of a texture.
*/ */
void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
auto c = session_get_x_connection(ps);
// Release binding // Release binding
if (ptex->glpixmap && ptex->texture) { if (ptex->glpixmap && ptex->texture) {
glBindTexture(ptex->target, 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); glBindTexture(ptex->target, 0);
} }
// Free GLX Pixmap // Free GLX Pixmap
if (ptex->glpixmap) { if (ptex->glpixmap) {
glXDestroyPixmap(ps->c.dpy, ptex->glpixmap); glXDestroyPixmap(c->dpy, ptex->glpixmap);
ptex->glpixmap = 0; 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) { void glx_set_clip(session_t *ps, const region_t *reg) {
// Quit if we aren't using stencils // Quit if we aren't using stencils
if (ps->o.glx_no_stencil) { if (session_get_options(ps)->glx_no_stencil) {
return; return;
} }
@ -884,7 +886,7 @@ void glx_set_clip(session_t *ps, const region_t *reg) {
if (nrects == 1) { if (nrects == 1) {
glEnable(GL_SCISSOR_TEST); 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); 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, 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) { GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc) {
assert(ps->psglx->blur_passes[0].prog); auto psglx = session_get_psglx(ps);
const bool more_passes = ps->o.blur_kernel_count > 1; 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_scissors = glIsEnabled(GL_SCISSOR_TEST);
const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); const bool have_stencil = glIsEnabled(GL_STENCIL_TEST);
bool ret = false; 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; 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; 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; bool last_pass = false;
for (int i = 0; i < ps->o.blur_kernel_count; ++i) { for (int i = 0; i < options->blur_kernel_count; ++i) {
last_pass = (i == ps->o.blur_kernel_count - 1); last_pass = (i == options->blur_kernel_count - 1);
const glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; const glx_blur_pass_t *ppass = &psglx->blur_passes[i];
assert(ppass->prog); assert(ppass->prog);
assert(tex_scr); 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); auto rdy = (GLfloat)(mheight - crect.y1 + mdy);
if (last_pass) { if (last_pass) {
rdx = (GLfloat)crect.x1; 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 rdxe = rdx + (GLfloat)(crect.x2 - crect.x1);
auto rdye = rdy - (GLfloat)(crect.y2 - crect.y1); 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, bool glx_round_corners_dst(session_t *ps, struct managed_win *w,
const glx_texture_t *ptex, int dx, int dy, int width, const glx_texture_t *ptex, int dx, int dy, int width,
int height, float z, float cr, const region_t *reg_tgt) { 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; bool ret = false;
auto psglx = session_get_psglx(ps);
// log_warn("dxy(%d, %d) wh(%d %d) rwh(%d %d) b(%d), f(%d)", // 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, // 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; int mdx = dx, mdy = dy, mwidth = width, mheight = height;
log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight); log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight);
auto root_extent = session_get_root_extent(ps);
if (w->g.border_width > 0) { if (w->g.border_width > 0) {
glx_read_border_pixel(ps->root_height, ps->root_width, dx, dy, width, glx_read_border_pixel(root_extent.height, root_extent.width, dx, dy,
height, &w->border_col[0]); 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); assert(ppass->prog);
glEnable(GL_BLEND); 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]); w->border_col[1], w->border_col[2], w->border_col[3]);
} }
if (ppass->unifm_resolution >= 0) { if (ppass->unifm_resolution >= 0) {
glUniform2f(ppass->unifm_resolution, (float)ps->root_width, glUniform2f(ppass->unifm_resolution, (float)root_extent.width,
(float)ps->root_height); (float)root_extent.height);
} }
// Painting // Painting
@ -1250,7 +1256,7 @@ bool glx_round_corners_dst(session_t *ps, struct managed_win *w,
// coordinates for the texture in the target // coordinates for the texture in the target
auto rdx = (GLfloat)crect.x1; 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 rdxe = (GLfloat)rdx + (GLfloat)(crect.x2 - crect.x1);
auto rdye = (GLfloat)rdy - (GLfloat)(crect.y2 - crect.y1); 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) { P_PAINTREG_START(crect) {
// XXX what does all of these variables mean? // XXX what does all of these variables mean?
GLint rdx = crect.x1; 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 rdxe = rdx + (crect.x2 - crect.x1);
GLint rdye = rdy - (crect.y2 - crect.y1); 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 // coordinates for the texture in the target
GLint rdx = crect.x1; 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 rdxe = rdx + (crect.x2 - crect.x1);
GLint rdye = rdy - (crect.y2 - crect.y1); GLint rdye = rdy - (crect.y2 - crect.y1);

View File

@ -13,12 +13,11 @@
#include "common.h" #include "common.h"
#include "compiler.h" #include "compiler.h"
#include "log.h" #include "picom.h"
#include "region.h" #include "region.h"
#include "render.h" #include "render.h"
#include "win.h" #include "win.h"
#include <ctype.h>
#include <epoxy/gl.h> #include <epoxy/gl.h>
#include <epoxy/glx.h> #include <epoxy/glx.h>
#include <locale.h> #include <locale.h>
@ -73,6 +72,9 @@ typedef struct glx_session {
int z; int z;
glx_blur_pass_t *blur_passes; glx_blur_pass_t *blur_passes;
glx_round_pass_t *round_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; } glx_session_t;
/// @brief Wrapper of a bound GLX texture. /// @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, int width, int height, int z, double opacity, bool argb, bool neg,
const region_t *reg_tgt, const glx_prog_main_t *pprogram); 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); 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. * Check if there's a GLX context.
*/ */
static inline bool glx_has_context(session_t *ps) { 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;
} }
/** /**

View File

@ -19,6 +19,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <libgen.h> #include <libgen.h>
#include <locale.h>
#include <math.h> #include <math.h>
#include <sched.h> #include <sched.h>
#include <stddef.h> #include <stddef.h>
@ -41,6 +42,7 @@
#include <ev.h> #include <ev.h>
#include <test.h> #include <test.h>
#include "backend/driver.h"
#include "common.h" #include "common.h"
#include "compiler.h" #include "compiler.h"
#include "config.h" #include "config.h"
@ -50,6 +52,7 @@
#include "picom.h" #include "picom.h"
#include "transition.h" #include "transition.h"
#include "win_defs.h" #include "win_defs.h"
#include "xcb/xproto.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
#include "opengl.h" #include "opengl.h"
#endif #endif
@ -73,6 +76,246 @@
#include "win.h" #include "win.h"
#include "x.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 <code>XRectangle</code>-s of exposed region.
/// XXX why do we need this array?
rect_t *expose_rects;
/// Number of <code>XRectangle</code>-s in <code>expose_rects</code>.
int size_expose;
/// Index of the next free slot in <code>expose_rects</code>.
int n_expose;
// === Window related ===
/// A hash table of all windows.
struct win *windows;
/// Windows in their stacking order
struct list_node window_stack;
/// Pointer to <code>win</code> of current active window. Used by
/// EWMH <code>_NET_ACTIVE_WINDOW</code> focus detection. In theory,
/// it's more reliable to store the window ID directly here, just in
/// case the WM does something extraordinary, but caching the pointer
/// means another layer of complexity.
struct managed_win *active_win;
/// Window ID of leader window of currently active window. Used for
/// subsidiary window detection.
xcb_window_t active_leader;
// === Shadow/dimming related ===
/// 1x1 black Picture.
xcb_render_picture_t black_picture;
/// 1x1 Picture of the shadow color.
xcb_render_picture_t cshadow_picture;
/// 1x1 white Picture.
xcb_render_picture_t white_picture;
/// 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 /// Get session_t pointer from a pointer to a member of session_t
#define session_ptr(ptr, member) \ #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); auto w = find_managed_win(ps, wid);
if (!w) { if (!w) {
w = find_toplevel(ps, wid); w = session_find_toplevel(ps, wid);
} }
if (!w) { if (!w) {
w = find_managed_window_or_parent(ps, wid); 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 // Check if it's a mapped client window
if (mode == WIN_EVMODE_CLIENT || if (mode == WIN_EVMODE_CLIENT || ((w = session_find_toplevel(ps, wid)) &&
((w = find_toplevel(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) { w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE; evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
} }
@ -824,7 +1067,7 @@ static void configure_root(session_t *ps) {
rebuild_shadow_exclude_reg(ps); rebuild_shadow_exclude_reg(ps);
// Invalidate reg_ignore from the top // 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) { if (top_w) {
rc_region_unref(&top_w->reg_ignore); rc_region_unref(&top_w->reg_ignore);
top_w->reg_ignore_valid = false; 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; return ps->overlay != XCB_NONE ? ps->overlay : ps->c.screen_info->root;
} }
#ifdef CONFIG_DBUS
struct cdbus_data *session_get_cdbus(struct session *ps) { struct cdbus_data *session_get_cdbus(struct session *ps) {
return ps->dbus_data; 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) { uint8_t session_redirection_mode(session_t *ps) {
if (ps->o.debug_mode) { if (ps->o.debug_mode) {
@ -1679,7 +2418,7 @@ static void handle_new_windows(session_t *ps) {
} }
// Send D-Bus signal // Send D-Bus signal
if (ps->o.dbus) { 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_picture = XCB_NONE,
.tgt_buffer = PAINT_INIT, .tgt_buffer = PAINT_INIT,
.reg_win = XCB_NONE, .reg_win = XCB_NONE,
#ifdef CONFIG_OPENGL
.glx_prog_win = GLX_PROG_MAIN_INIT,
#endif
.redirected = false, .redirected = false,
.alpha_picts = NULL, .alpha_picts = NULL,
.fade_time = 0L, .fade_time = 0L,
@ -2413,10 +3149,13 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
apply_driver_workarounds(ps, ps->drivers); apply_driver_workarounds(ps, ps->drivers);
// Initialize filters, must be preceded by OpenGL context creation // Initialize filters, must be preceded by OpenGL context creation
if (ps->o.legacy_backends && !init_render(ps)) { 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"); log_fatal("Failed to initialize the backend");
exit(1); exit(1);
} }
}
if (ps->o.print_diagnostics) { if (ps->o.print_diagnostics) {
print_diagnostics(ps, config_file, compositor_running); print_diagnostics(ps, config_file, compositor_running);
@ -2554,7 +3293,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
nchildren = xcb_query_tree_children_length(query_tree_reply); nchildren = xcb_query_tree_children_length(query_tree_reply);
for (int i = 0; i < nchildren; i++) { 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); free(query_tree_reply);
} }
@ -2697,16 +3436,15 @@ static void session_destroy(session_t *ps) {
// backend is deinitialized in unredirect() // backend is deinitialized in unredirect()
assert(ps->backend_data == NULL); assert(ps->backend_data == NULL);
} else { } else {
// Free all GLX resources of windows
win_stack_foreach_managed(w, &ps->window_stack) {
free_win_res_glx(ps, w);
}
deinit_render(ps); 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 // Flush all events
xcb_aux_sync(ps->c.c); xcb_aux_sync(ps->c.c);
ev_io_stop(ps->loop, &ps->xiow); ev_io_stop(ps->loop, &ps->xiow);

View File

@ -1,29 +1,25 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MPL-2.0
// Copyright (c) // Copyright (c) Yuxuan Shui.
// Throw everything in here.
// !!! DON'T !!!
// === Includes === // === Includes ===
#pragma once
#include <locale.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include "backend/backend.h" #include "backend/backend.h"
#include "backend/driver.h"
#include "c2.h" #include "c2.h"
#include "common.h" #include "common.h"
#include "compiler.h"
#include "config.h" #include "config.h"
#include "log.h" // XXX clean up #include "ev.h"
#include "list.h"
#include "region.h" #include "region.h"
#include "render.h" #include "render.h"
#include "types.h" #include "types.h"
#include "utils.h"
#include "win.h" #include "win.h"
#include "x.h" #include "x.h"
#include "xcb/render.h"
enum root_flags { enum root_flags {
ROOT_FLAGS_SCREEN_CHANGE = 1, // Received RandR screen change notify, we 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 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); 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); 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 circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce);
void root_damaged(session_t *ps); void root_damaged(session_t *ps);
void queue_redraw(session_t *ps); void queue_redraw(session_t *ps);
void discard_pending(session_t *ps, uint32_t sequence); void discard_pending(session_t *ps, uint32_t sequence);
void set_root_flags(session_t *ps, uint64_t flags); void set_root_flags(session_t *ps, uint64_t flags);
void quit(session_t *ps); void quit(session_t *ps);
xcb_window_t session_get_target_window(session_t *); xcb_window_t session_get_target_window(session_t *);
uint8_t session_redirection_mode(session_t *ps); 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 #ifdef CONFIG_DBUS
struct cdbus_data *session_get_cdbus(struct session *); 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) { free_win_res_glx(session_t *ps attr_unused, struct managed_win *w attr_unused) {
} }
#endif #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);
}

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
#include "common.h" #include "common.h"
#include "log.h" #include "log.h"
#include "picom.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
#include "backend/gl/glx.h" #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?? // We don't have a context??
return false; return false;
} }
glXSwapIntervalEXT(ps->c.dpy, glXGetCurrentDrawable(), interval); glXSwapIntervalEXT(session_get_x_connection(ps)->dpy,
glXGetCurrentDrawable(), interval);
return true; return true;
} }
return false; 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) { static int vsync_opengl_oml_wait(session_t *ps) {
int64_t ust = 0, msc = 0, sbc = 0; 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); glXGetSyncValuesOML(c->dpy, session_get_target_window(ps), &ust, &msc, &sbc);
glXWaitForMscOML(ps->c.dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc); glXWaitForMscOML(c->dpy, session_get_target_window(ps), 0, 2, (msc + 1) % 2, &ust,
&msc, &sbc);
return 0; return 0;
} }
#endif #endif
@ -164,7 +168,7 @@ bool vsync_init(session_t *ps) {
log_warn("The DRM vsync method is deprecated, please don't enable it."); log_warn("The DRM vsync method is deprecated, please don't enable it.");
#endif #endif
if (!ps->o.vsync) { if (!session_get_options(ps)->vsync) {
return true; return true;
} }
@ -173,8 +177,9 @@ bool vsync_init(session_t *ps) {
if (!vsync_opengl_swc_init(ps)) { if (!vsync_opengl_swc_init(ps)) {
return false; return false;
} }
ps->vsync_wait = NULL; // glXSwapBuffers will automatically wait // glXSwapBuffers will automatically wait for vsync, we don't
// for vsync, we don't need to do anything. // need to do anything.
session_set_vsync_wait(ps, NULL);
return true; return true;
} }
#endif #endif
@ -184,13 +189,13 @@ bool vsync_init(session_t *ps) {
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
if (vsync_opengl_oml_init(ps)) { if (vsync_opengl_oml_init(ps)) {
log_info("Using the opengl-oml vsync method"); 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; return true;
} }
if (vsync_opengl_init(ps)) { if (vsync_opengl_init(ps)) {
log_info("Using the opengl vsync method"); log_info("Using the opengl vsync method");
ps->vsync_wait = vsync_opengl_wait; session_set_vsync_wait(ps, vsync_opengl_wait);
return true; return true;
} }
#endif #endif

654
src/win.c

File diff suppressed because it is too large Load Diff

View File

@ -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); void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw);
/// Recheck if a window is fullscreen /// 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 * 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 * Get a rectangular region in global coordinates a window (and possibly
* its shadow) occupies. * 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); 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 /// 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); 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` /// Query the Xorg for information about window `win`
/// `win` pointer might become invalid after this function returns /// `win` pointer might become invalid after this function returns
struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct win *win); 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. * 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. * 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 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`. * 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; return w->base.id == w->client_win && !w->a.override_redirect;
} }
/// check if reg_ignore_valid is true for all windows above us static inline const char *win_get_name_if_managed(const struct win *w) {
bool attr_pure win_is_region_ignore_valid(session_t *ps, const struct managed_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 /// Whether a given window is mapped on the X server side
bool win_is_mapped_in_x(const struct managed_win *w); 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 /// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags); void win_set_flags(struct managed_win *w, uint64_t flags);
/// Clear flags on a window. Some sanity checks are performed /// Clear flags on a window. Some sanity checks are performed

17
src/x.c
View File

@ -21,6 +21,7 @@
#include <xcb/xfixes.h> #include <xcb/xfixes.h>
#include "atom.h" #include "atom.h"
#include "picom.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
#include "backend/gl/glx.h" #include "backend/gl/glx.h"
#endif #endif
@ -49,7 +50,7 @@ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) {
xcb_err.major_code = ev->request_code; xcb_err.major_code = ev->request_code;
xcb_err.minor_code = ev->minor_code; xcb_err.minor_code = ev->minor_code;
xcb_err.error_code = ev->error_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; 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 case XCB_##s: name = #s; break
// TODO(yshui) separate error code out from session_t // 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); } 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); } switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error; o = error_code - session_get_render_extension_error(ps);
switch (o) { switch (o) {
CASESTRRET2(RENDER_PICT_FORMAT); CASESTRRET2(RENDER_PICT_FORMAT);
CASESTRRET2(RENDER_PICTURE); 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); CASESTRRET2(RENDER_GLYPH);
} }
if (ps->glx_exists) { if (session_has_glx_extension(ps)) {
o = error_code - ps->glx_error; o = error_code - session_get_glx_extension_error(ps);
switch (o) { switch (o) {
CASESTRRET2(GLX_BAD_CONTEXT); CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_CONTEXT_STATE); 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) { if (session_get_xsync_extension_error(ps)) {
o = error_code - ps->xsync_error; o = error_code - session_get_xsync_extension_error(ps);
switch (o) { switch (o) {
CASESTRRET(XSyncBadCounter); CASESTRRET(XSyncBadCounter);
CASESTRRET(XSyncBadAlarm); CASESTRRET(XSyncBadAlarm);