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

View File

@ -12,6 +12,7 @@
#include "config.h"
#include "kernel.h"
#include "log.h"
#include "picom.h"
#include "utils.h"
#include "win.h"
#include "x.h"
@ -508,8 +509,8 @@ struct backend_image *default_new_backend_image(int w, int h) {
}
void init_backend_base(struct backend_base *base, session_t *ps) {
base->c = &ps->c;
base->loop = ps->loop;
base->c = session_get_x_connection(ps);
base->loop = session_get_mainloop(ps);
base->busy = false;
base->ops = NULL;
}

View File

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

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) {
auto ret = (struct backend_base *)ccalloc(1, struct dummy_data);
ret->c = &ps->c;
ret->loop = ps->loop;
ret->busy = false;
init_backend_base(ret, ps);
return ret;
}

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->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, ps->c.dpy,
auto c = session_get_x_connection(ps);
gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, c->dpy,
(EGLint[]){
EGL_PLATFORM_X11_SCREEN_EXT,
ps->c.screen,
c->screen,
EGL_NONE,
});
if (gd->display == EGL_NO_DISPLAY) {
@ -172,7 +173,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
goto end;
}
auto visual_info = x_get_visual_info(&ps->c, ps->c.screen_info->root_visual);
auto visual_info = x_get_visual_info(c, c->screen_info->root_visual);
EGLConfig config = NULL;
int nconfigs = 1;
// clang-format off
@ -228,7 +229,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
gd->gl.decouple_texture_user_data = egl_decouple_user_data;
gd->gl.release_user_data = egl_release_image;
if (ps->o.vsync) {
if (session_get_options(ps)->vsync) {
if (!egl_set_swap_interval(1, gd->display)) {
log_error("Failed to enable vsync. %#x", eglGetError());
}

View File

@ -13,6 +13,7 @@
#include "config.h"
#include "kernel.h"
#include "log.h"
#include "picom.h"
#include "region.h"
#include "string_utils.h"
#include "types.h"
@ -623,7 +624,8 @@ static bool gl_win_shader_from_stringv(const char **vshader_strv,
void gl_root_change(backend_t *base, session_t *ps) {
auto gd = (struct gl_data *)base;
gl_resize(gd, ps->root_width, ps->root_height);
auto root_extent = session_get_root_extent(ps);
gl_resize(gd, root_extent.width, root_extent.height);
}
/**
@ -889,7 +891,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);
gd->dithered_present = ps->o.dithered_present;
gd->dithered_present = session_get_options(ps)->dithered_present;
gd->dummy_prog =
gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL},
(const char *[]){dummy_frag, NULL});
@ -949,8 +951,9 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
const GLint *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16}
: (const GLint[]){GL_RGB8, GL_RGBA8};
for (int i = 0; i < 2; i++) {
auto root_extent = session_get_root_extent(ps);
gd->back_format = format[i];
gl_resize(gd, ps->root_width, ps->root_height);
gl_resize(gd, root_extent.width, root_extent.height);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, gd->back_texture, 0);

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) {
bool success = false;
glxext_init(ps->c.dpy, ps->c.screen);
auto c = session_get_x_connection(ps);
glxext_init(c->dpy, c->screen);
auto gd = ccalloc(1, struct _glx_data);
init_backend_base(&gd->gl.base, ps);
@ -254,15 +255,15 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
XVisualInfo *pvis = NULL;
// Check for GLX extension
if (!ps->glx_exists) {
if (!session_has_glx_extension(ps)) {
log_error("No GLX extension.");
goto end;
}
// Get XVisualInfo
int nitems = 0;
XVisualInfo vreq = {.visualid = ps->c.screen_info->root_visual};
pvis = XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems);
XVisualInfo vreq = {.visualid = c->screen_info->root_visual};
pvis = XGetVisualInfo(c->dpy, VisualIDMask, &vreq, &nitems);
if (!pvis) {
log_error("Failed to acquire XVisualInfo for current visual.");
goto end;
@ -270,22 +271,22 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
// Ensure the visual is double-buffered
int value = 0;
if (glXGetConfig(ps->c.dpy, pvis, GLX_USE_GL, &value) || !value) {
if (glXGetConfig(c->dpy, pvis, GLX_USE_GL, &value) || !value) {
log_error("Root visual is not a GL visual.");
goto end;
}
if (glXGetConfig(ps->c.dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
if (glXGetConfig(c->dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
log_error("Root visual lacks stencil buffer.");
goto end;
}
if (glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
if (glXGetConfig(c->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
log_error("Root visual is not a double buffered GL visual.");
goto end;
}
if (glXGetConfig(ps->c.dpy, pvis, GLX_RGBA, &value) || !value) {
if (glXGetConfig(c->dpy, pvis, GLX_RGBA, &value) || !value) {
log_error("Root visual is a color index visual, not supported");
goto end;
}
@ -303,11 +304,11 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
// Find a fbconfig with visualid matching the one from the target win, so we can
// be sure that the fbconfig is compatible with our target window.
int ncfgs;
GLXFBConfig *cfg = glXGetFBConfigs(ps->c.dpy, ps->c.screen, &ncfgs);
GLXFBConfig *cfg = glXGetFBConfigs(c->dpy, c->screen, &ncfgs);
bool found = false;
for (int i = 0; i < ncfgs; i++) {
int visualid;
glXGetFBConfigAttribChecked(ps->c.dpy, cfg[i], GLX_VISUAL_ID, &visualid);
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_VISUAL_ID, &visualid);
if ((VisualID)visualid != pvis->visualid) {
continue;
}
@ -326,7 +327,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
attributes[7] = GLX_LOSE_CONTEXT_ON_RESET_ARB;
}
gd->ctx = glXCreateContextAttribsARB(ps->c.dpy, cfg[i], 0, true, attributes);
gd->ctx = glXCreateContextAttribsARB(c->dpy, cfg[i], 0, true, attributes);
free(cfg);
if (!gd->ctx) {
@ -344,7 +345,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
// Attach GLX context
GLXDrawable tgt = gd->target_win;
if (!glXMakeCurrent(ps->c.dpy, tgt, gd->ctx)) {
if (!glXMakeCurrent(c->dpy, tgt, gd->ctx)) {
log_error("Failed to attach GLX context.");
goto end;
}
@ -357,12 +358,12 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
gd->gl.decouple_texture_user_data = glx_decouple_user_data;
gd->gl.release_user_data = glx_release_image;
if (ps->o.vsync) {
if (!glx_set_swap_interval(1, ps->c.dpy, tgt)) {
if (session_get_options(ps)->vsync) {
if (!glx_set_swap_interval(1, c->dpy, tgt)) {
log_error("Failed to enable vsync.");
}
} else {
glx_set_swap_interval(0, ps->c.dpy, tgt);
glx_set_swap_interval(0, c->dpy, tgt);
}
success = true;

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

View File

@ -145,249 +145,7 @@ struct damage_ring {
/// Number of damage regions we track
int count;
};
/// Structure containing all necessary data for a session.
typedef struct session {
// === Event handlers ===
/// ev_io for X connection
ev_io xiow;
/// Timeout for delayed unredirection.
ev_timer unredir_timer;
/// Timer for fading
ev_timer fade_timer;
/// Use an ev_timer callback for drawing
ev_timer draw_timer;
/// Called every time we have timeouts or new data on socket,
/// so we can be sure if xcb read from X socket at anytime during event
/// handling, we will not left any event unhandled in the queue
ev_prepare event_check;
/// Signal handler for SIGUSR1
ev_signal usr1_signal;
/// Signal handler for SIGINT
ev_signal int_signal;
// === Backend related ===
/// backend data
backend_t *backend_data;
/// backend blur context
void *backend_blur_context;
/// graphic drivers used
enum driver drivers;
/// file watch handle
void *file_watch_handle;
/// libev mainloop
struct ev_loop *loop;
/// Shaders
struct shader_info *shaders;
// === Display related ===
/// X connection
struct x_connection c;
/// Whether the X server is grabbed by us
bool server_grabbed;
/// Width of root window.
int root_width;
/// Height of root window.
int root_height;
/// X Composite overlay window.
xcb_window_t overlay;
/// The target window for debug mode
xcb_window_t debug_window;
/// Whether the root tile is filled by us.
bool root_tile_fill;
/// Picture of the root window background.
paint_t root_tile_paint;
/// The backend data the root pixmap bound to
image_handle root_image;
/// A region of the size of the screen.
region_t screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting
/// mode.
xcb_render_picture_t root_picture;
/// A Picture acting as the painting target.
xcb_render_picture_t tgt_picture;
/// Temporary buffer to paint to before sending to display.
paint_t tgt_buffer;
/// Window ID of the window we register as a symbol.
xcb_window_t reg_win;
#ifdef CONFIG_OPENGL
/// Pointer to GLX data.
struct glx_session *psglx;
/// Custom GLX program used for painting window.
// XXX should be in struct glx_session
glx_prog_main_t glx_prog_win;
struct glx_fbconfig_info argb_fbconfig;
#endif
/// Sync fence to sync draw operations
xcb_sync_fence_t sync_fence;
/// Whether we are rendering the first frame after screen is redirected
bool first_frame;
/// Whether screen has been turned off
bool screen_is_off;
/// When last MSC event happened, in useconds.
uint64_t last_msc_instant;
/// The last MSC number
uint64_t last_msc;
/// The delay between when the last frame was scheduled to be rendered, and when
/// the render actually started.
uint64_t last_schedule_delay;
/// When do we want our next frame to start rendering.
uint64_t next_render;
/// Whether we can perform frame pacing.
bool frame_pacing;
/// Vblank event scheduler
struct vblank_scheduler *vblank_scheduler;
/// Render statistics
struct render_statistics render_stats;
// === Operation related ===
/// Flags related to the root window
uint64_t root_flags;
/// Program options.
options_t o;
/// State object for c2.
struct c2_state *c2_state;
/// Whether we have hit unredirection timeout.
bool tmout_unredir_hit;
/// If the backend is busy. This means two things:
/// Either the backend is currently rendering a frame, or a frame has been
/// rendered but has yet to be presented. In either case, we should not start
/// another render right now. As if we start issuing rendering commands now, we
/// will have to wait for either the current render to finish, or the current
/// back buffer to become available again. In either case, we will be wasting
/// time.
bool backend_busy;
/// Whether a render is queued. This generally means there are pending updates
/// to the screen that's neither included in the current render, nor on the
/// screen.
bool render_queued;
/// For tracking damage regions
struct damage_ring damage_ring;
/// Whether all windows are currently redirected.
bool redirected;
/// Pre-generated alpha pictures.
xcb_render_picture_t *alpha_picts;
/// Time of last fading. In milliseconds.
long long fade_time;
// Cached blur convolution kernels.
struct x_convolution_kernel **blur_kerns_cache;
/// If we should quit
bool quit : 1;
// TODO(yshui) use separate flags for different kinds of updates so we don't
// waste our time.
/// Whether there are pending updates, like window creation, etc.
bool pending_updates : 1;
// === Expose event related ===
/// Pointer to an array of <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;
struct session;
/// Enumeration for window event hints.
typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, WIN_EVMODE_CLIENT } win_evmode_t;
@ -461,18 +219,13 @@ static inline struct timespec get_time_timespec(void) {
return tm;
}
/**
* Return the painting target window.
*/
static inline xcb_window_t get_tgt_window(session_t *ps) {
return ps->overlay != XCB_NONE ? ps->overlay : ps->c.screen_info->root;
}
struct options *session_get_options(session_t *ps);
/**
* Check if current backend uses GLX.
*/
static inline bool bkend_use_glx(session_t *ps) {
return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
auto backend = session_get_options(ps)->backend;
return backend == BKEND_GLX || backend == BKEND_XR_GLX_HYBRID;
}
/**
@ -520,7 +273,7 @@ static inline void damage_ring_advance(struct damage_ring *ring) {
pixman_region32_clear(&ring->damages[ring->cursor]);
}
static inline void damage_ring_collect(struct damage_ring *ring, region_t *all_region,
static inline void damage_ring_collect(const struct damage_ring *ring, region_t *all_region,
region_t *region, int buffer_age) {
if (buffer_age == -1 || buffer_age > ring->count) {
pixman_region32_copy(region, all_region);

View File

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

View File

@ -24,12 +24,11 @@
#include "config.h"
#include "dbus/dbus-protocol.h"
#include "dbus/dbus-shared.h"
#include "list.h"
#include "log.h"
#include "picom.h"
#include "string_utils.h"
#include "transition.h"
#include "types.h"
#include "uthash_extra.h"
#include "utils.h"
#include "win.h"
#include "win_defs.h"
@ -163,7 +162,7 @@ struct cdbus_data *cdbus_init(session_t *ps, const char *uniq) {
}
// Add watch handlers
cd->loop = ps->loop;
cd->loop = session_get_mainloop(ps);
if (!dbus_connection_set_watch_functions(cd->dbus_conn, cdbus_callback_add_watch,
cdbus_callback_remove_watch,
cdbus_callback_watch_toggled, cd, NULL)) {
@ -472,41 +471,45 @@ static bool cdbus_append_string_variant(DBusMessage *msg, const char *data) {
return true;
}
struct cdbus_append_wids_data {
DBusMessageIter iter;
bool success;
};
static void cdbus_append_wids_callback(struct win *w, void *user_data) {
auto data = (struct cdbus_append_wids_data *)user_data;
assert(!w->destroyed);
if (data->success) {
data->success =
dbus_message_iter_append_basic(&data->iter, CDBUS_TYPE_WINDOW, &w->id);
}
}
/// Append all window IDs in the window list of a session to a D-Bus message
static bool cdbus_append_wids(DBusMessage *msg, session_t *ps) {
// Get the number of wids we are to include
unsigned count = 0;
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
++count;
}
auto count = session_get_window_count(ps);
if (!count) {
// Nothing to append
return true;
}
// Allocate memory for an array of window IDs
auto arr = ccalloc(count, cdbus_window_t);
// Build the array
cdbus_window_t *pcur = arr;
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
*pcur = w->id;
++pcur;
}
assert(pcur == arr + count);
// Append arguments
if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, &arr,
count, DBUS_TYPE_INVALID)) {
DBusMessageIter iter;
struct cdbus_append_wids_data callback_data = {.success = true};
dbus_message_iter_init_append(msg, &iter);
if (!dbus_message_iter_open_container(
&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &callback_data.iter)) {
log_error("Failed to append argument.");
free(arr);
return false;
}
free(arr);
session_foreach_win(ps, cdbus_append_wids_callback, &callback_data);
if (callback_data.success) {
callback_data.success =
dbus_message_iter_close_container(&iter, &callback_data.iter);
}
if (!callback_data.success) {
log_error("Failed to append argument.");
return false;
}
return true;
}
@ -615,12 +618,8 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
append_win_property(Name, name, string_variant);
if (!strcmp("Next", target)) {
cdbus_window_t next_id = 0;
if (!list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)) {
next_id = list_entry(w->base.stack_neighbour.next, struct win,
stack_neighbour)
->id;
}
auto next_win = session_get_next_win_in_stack(ps, &w->base);
cdbus_window_t next_id = next_win ? next_win->id : XCB_NONE;
if (!cdbus_append_wid_variant(reply, next_id)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
@ -640,7 +639,7 @@ static DBusHandlerResult cdbus_process_reset(session_t *ps, DBusMessage *msg att
DBusMessage *reply, DBusError *e attr_unused) {
// Reset the compositor
log_info("picom is resetting...");
ev_break(ps->loop, EVBREAK_ALL);
ev_break(session_get_mainloop(ps), EVBREAK_ALL);
if (reply != NULL && !cdbus_append_boolean(reply, true)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
@ -694,11 +693,8 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
#define append_win_property(tgt, type) append(tgt, type, w->tgt)
if (!strcmp("next", target)) {
xcb_window_t next_id =
list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)
? 0
: list_entry(w->base.stack_neighbour.next, struct win, stack_neighbour)
->id;
auto next_win = session_get_next_win_in_stack(ps, &w->base);
xcb_window_t next_id = next_win ? next_win->id : XCB_NONE;
if (!cdbus_append_wid(reply, next_id)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
@ -818,14 +814,15 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
return DBUS_HANDLER_RESULT_HANDLED;
}
auto w = find_toplevel(ps, client);
auto w = session_find_toplevel(ps, client);
if (w) {
wid = w->base.id;
}
} else if (!strcmp("focused", target)) {
// Find focused window
if (ps->active_win && ps->active_win->state != WSTATE_UNMAPPED) {
wid = ps->active_win->base.id;
auto active_win = session_get_active_win(ps);
if (active_win && active_win->state != WSTATE_UNMAPPED) {
wid = active_win->base.id;
}
} else {
log_debug(CDBUS_ERROR_BADTGT_S, target);
@ -853,7 +850,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
return DBUS_HANDLER_RESULT_HANDLED;
}
assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0]));
#define append(tgt, type, ret) \
if (!strcmp(#tgt, target)) { \
@ -862,20 +858,21 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
} \
return DBUS_HANDLER_RESULT_HANDLED; \
}
#define append_session_option(tgt, type) append(tgt, type, ps->o.tgt)
#define append_session_option(tgt, type) append(tgt, type, options->tgt)
auto options = session_get_options(ps);
assert(options->backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0]));
append(version, string, PICOM_VERSION);
append(pid, int32, getpid());
append(display, string, DisplayString(ps->c.dpy));
append(display, string, DisplayString(session_get_x_connection(ps)->dpy));
append(config_file, string, "Unknown");
append(paint_on_overlay, boolean, ps->overlay != XCB_NONE);
append(paint_on_overlay_id, uint32, ps->overlay); // Sending ID of the X
// composite overlay
// window
append(unredir_if_possible_delay, int32, (int32_t)ps->o.unredir_if_possible_delay);
append(paint_on_overlay, boolean, session_get_overlay(ps) != XCB_NONE);
append(paint_on_overlay_id, uint32, session_get_overlay(ps));
append(unredir_if_possible_delay, int32, (int32_t)options->unredir_if_possible_delay);
append(refresh_rate, int32, 0);
append(sw_opti, boolean, false);
append(backend, string, BACKEND_STRS[ps->o.backend]);
append(backend, string, BACKEND_STRS[options->backend]);
append_session_option(unredir_if_possible, boolean);
append_session_option(write_pid_path, string);
@ -928,9 +925,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
return DBUS_HANDLER_RESULT_HANDLED;
}
// XXX Remove this after header clean up
void queue_redraw(session_t *ps);
/**
* Process a opts_set D-Bus request.
*/
@ -953,7 +947,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
if (strcmp(#tgt, target) == 0) { \
type val; \
get_msg_arg(dbus_type, val); \
ps->o.tgt = expr; \
options->tgt = expr; \
goto cdbus_process_opts_set_success; \
}
@ -961,6 +955,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
goto cdbus_process_opts_set_success;
}
auto options = session_get_options(ps);
opts_set_do(fade_delta, INT32, int32_t, max2(val, 1));
opts_set_do(fade_in_step, DOUBLE, double, normalize_d(val));
opts_set_do(fade_out_step, DOUBLE, double, normalize_d(val));
@ -970,8 +965,8 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
if (!strcmp("unredir_if_possible", target)) {
dbus_bool_t val = FALSE;
get_msg_arg(BOOLEAN, val);
if (ps->o.unredir_if_possible != val) {
ps->o.unredir_if_possible = val;
if (options->unredir_if_possible != val) {
options->unredir_if_possible = val;
queue_redraw(ps);
}
goto cdbus_process_opts_set_success;
@ -980,8 +975,8 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
if (!strcmp("redirected_force", target)) {
cdbus_enum_t val = UNSET;
get_msg_arg(UINT32, val);
if (ps->o.redirected_force != val) {
ps->o.redirected_force = val;
if (options->redirected_force != val) {
options->redirected_force = val;
force_repaint(ps);
}
goto cdbus_process_opts_set_success;
@ -1068,6 +1063,24 @@ static DBusHandlerResult cdbus_process_introspect(DBusMessage *reply) {
}
///@}
struct cdbus_window_root_introspect_data {
char *introspect;
bool success;
};
static void cdbus_window_root_introspect_callback(struct win *w, void *user_data) {
auto data = (struct cdbus_window_root_introspect_data *)user_data;
assert(!w->destroyed);
if (data->success) {
char *tmp = NULL;
if (asprintf(&tmp, "<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.
*/
@ -1088,31 +1101,19 @@ cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *reply) {
return DBUS_HANDLER_RESULT_HANDLED;
}
char *ret = NULL;
bool success = true;
mstrextend(&ret, str_introspect);
struct cdbus_window_root_introspect_data callback_data = {.introspect = NULL,
.success = true};
mstrextend(&callback_data.introspect, str_introspect);
session_foreach_win(ps, cdbus_window_root_introspect_callback, &callback_data);
mstrextend(&callback_data.introspect, "</node>");
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
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);
if (callback_data.success) {
callback_data.success = cdbus_append_string(reply, callback_data.introspect);
}
mstrextend(&ret, "</node>");
if (success) {
success = cdbus_append_string(reply, ret);
}
free(ret);
return success ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NEED_MEMORY;
free(callback_data.introspect);
return callback_data.success ? DBUS_HANDLER_RESULT_HANDLED
: DBUS_HANDLER_RESULT_NEED_MEMORY;
}
static bool cdbus_process_window_introspect(DBusMessage *reply) {

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

View File

@ -23,6 +23,7 @@
#include "win.h"
#include "win_defs.h"
#include "x.h"
#include "xcb/xfixes.h"
/// Event handling with X is complicated. Handling events with other events possibly
/// in-flight is no good. Because your internal state won't be up to date. Also, querying
@ -66,14 +67,14 @@ static inline const char *ev_window_name(session_t *ps, xcb_window_t wid) {
char *name = "";
if (wid) {
name = "(Failed to get title)";
if (ps->c.screen_info->root == wid) {
if (session_get_x_connection(ps)->screen_info->root == wid) {
name = "(Root window)";
} else if (ps->overlay == wid) {
} else if (session_get_overlay(ps) == wid) {
name = "(Overlay)";
} else {
auto w = find_managed_win(ps, wid);
if (!w) {
w = find_toplevel(ps, wid);
w = session_find_toplevel(ps, wid);
}
if (w && w->name) {
@ -99,11 +100,13 @@ static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_
case PropertyNotify: return ((xcb_property_notify_event_t *)ev)->window;
case ClientMessage: return ((xcb_client_message_event_t *)ev)->window;
default:
if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
if (session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY ==
ev->response_type) {
return ((xcb_damage_notify_event_t *)ev)->drawable;
}
if (ps->shape_exists && ev->response_type == ps->shape_event) {
if (session_has_shape_extension(ps) &&
ev->response_type == session_get_shape_extention_event(ps)) {
return ((xcb_shape_notify_event_t *)ev)->affected_window;
}
@ -131,16 +134,17 @@ static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) {
CASESTRRET(ClientMessage);
}
if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
if (session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY == ev->response_type) {
return "Damage";
}
if (ps->shape_exists && ev->response_type == ps->shape_event) {
if (session_has_shape_extension(ps) &&
ev->response_type == session_get_shape_extention_event(ps)) {
return "ShapeNotify";
}
if (ps->xsync_exists) {
int o = ev->response_type - ps->xsync_event;
if (session_has_xsync_extension(ps)) {
int o = ev->response_type - session_get_xsync_extention_event(ps);
switch (o) {
CASESTRRET(XSyncCounterNotify);
CASESTRRET(XSyncAlarmNotify);
@ -183,37 +187,37 @@ static inline const char *attr_pure ev_focus_detail_name(xcb_focus_in_event_t *e
static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) {
log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
ev_focus_detail_name(ev));
ps->pending_updates = true;
session_mark_updates_pending(ps);
}
static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
ev_focus_detail_name(ev));
ps->pending_updates = true;
session_mark_updates_pending(ps);
}
static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
if (ev->parent == ps->c.screen_info->root) {
add_win_top(ps, ev->window);
if (ev->parent == session_get_x_connection(ps)->screen_info->root) {
session_add_win_top(ps, ev->window);
}
}
/// Handle configure event of a regular window
static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
auto w = find_win(ps, ce->window);
auto w = session_find_win(ps, ce->window);
if (!w) {
return;
}
if (!w->managed) {
restack_above(ps, w, ce->above_sibling);
session_restack_above(ps, w, ce->above_sibling);
return;
}
auto mw = (struct managed_win *)w;
restack_above(ps, w, ce->above_sibling);
session_restack_above(ps, w, ce->above_sibling);
// We check against pending_g here, because there might have been multiple
// configure notifies in this cycle, or the window could receive multiple updates
@ -227,7 +231,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
win_set_flags(mw, WIN_FLAGS_FACTOR_CHANGED);
// TODO(yshui) don't set pending_updates if the window is not
// visible/mapped
ps->pending_updates = true;
session_mark_updates_pending(ps);
// At least one of the following if's is true
if (position_changed) {
@ -248,7 +252,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
}
// Recalculate which monitor this window is on
win_update_monitor(&ps->monitors, mw);
win_update_monitor(session_get_monitors(ps), mw);
}
// override_redirect flag cannot be changed after window creation, as far
@ -259,7 +263,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) {
log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
ev->event, ev->window, ev->above_sibling, ev->override_redirect);
if (ev->window == ps->c.screen_info->root) {
if (ev->window == session_get_x_connection(ps)->screen_info->root) {
set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
} else {
configure_win(ps, ev);
@ -267,8 +271,8 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event
}
static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
auto w = find_win(ps, ev->window);
auto mw = find_toplevel(ps, ev->window);
auto w = session_find_win(ps, ev->window);
auto mw = session_find_toplevel(ps, ev->window);
if (mw && mw->client_win == mw->base.id) {
// We only want _real_ frame window
assert(&mw->base == w);
@ -291,7 +295,7 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *
if (mw != NULL) {
win_unmark_client(ps, mw);
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
session_mark_updates_pending(ps);
return;
}
log_debug("Received a destroy notify from an unknown window, %#010x", ev->window);
@ -300,10 +304,11 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *
static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
// Unmap overlay window if it got mapped but we are currently not
// in redirected state.
if (ps->overlay && ev->window == ps->overlay && !ps->redirected) {
auto overlay = session_get_overlay(ps);
auto c = session_get_x_connection(ps);
if (overlay && ev->window == overlay && !session_is_redirected(ps)) {
log_debug("Overlay is mapped while we are not redirected");
auto e = xcb_request_check(
ps->c.c, xcb_unmap_window_checked(ps->c.c, ps->overlay));
auto e = xcb_request_check(c->c, xcb_unmap_window_checked(c->c, overlay));
if (e) {
log_error("Failed to unmap the overlay window");
free(e);
@ -328,7 +333,7 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
// FocusIn/Out may be ignored when the window is unmapped, so we must
// recheck focus here
ps->pending_updates = true; // to update focus
session_mark_updates_pending(ps); // to update focus
}
static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) {
@ -341,30 +346,31 @@ static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev)
static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
ev->window, ev->parent, ev->override_redirect);
auto w_top = find_toplevel(ps, ev->window);
auto w_top = session_find_toplevel(ps, ev->window);
if (w_top) {
win_unmark_client(ps, w_top);
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
session_mark_updates_pending(ps);
}
if (ev->parent == ps->c.screen_info->root) {
auto c = session_get_x_connection(ps);
if (ev->parent == c->screen_info->root) {
// X will generate reparent notify even if the parent didn't actually
// change (i.e. reparent again to current parent). So we check if that's
// the case
auto w = find_win(ps, ev->window);
auto w = session_find_win(ps, ev->window);
if (w) {
// This window has already been reparented to root before,
// so we don't need to create a new window for it, we just need to
// move it to the top
restack_top(ps, w);
session_restack_top(ps, w);
} else {
add_win_top(ps, ev->window);
session_add_win_top(ps, ev->window);
}
} else {
// otherwise, find and destroy the window first
{
auto w = find_win(ps, ev->window);
auto w = session_find_win(ps, ev->window);
if (w) {
if (w->managed) {
auto mw = (struct managed_win *)w;
@ -395,7 +401,8 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
// Reset event mask in case something wrong happens
uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN);
if (!wid_has_prop(ps->c.c, ev->window, ps->atoms->aWM_STATE)) {
auto atoms = session_get_atoms(ps);
if (!wid_has_prop(c->c, ev->window, atoms->aWM_STATE)) {
log_debug("Window %#010x doesn't have WM_STATE property, it is "
"probably not a client window. But we will listen for "
"property change in case it gains one.",
@ -408,70 +415,50 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
"client",
w_real_top->base.id, w_real_top->name);
win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
session_mark_updates_pending(ps);
} else {
log_debug("parent %#010x not found", ev->parent);
}
}
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, ev->window,
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
}
}
static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
auto w = find_win(ps, ev->window);
auto w = session_find_win(ps, ev->window);
if (!w) {
return;
}
if (ev->place == PlaceOnTop) {
restack_top(ps, w);
session_restack_top(ps, w);
} else {
restack_bottom(ps, w);
session_restack_bottom(ps, w);
}
}
static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) {
region_t region;
pixman_region32_init_rects(&region, rects, nrects);
add_damage(ps, &region);
pixman_region32_fini(&region);
}
static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
if (ev->window == ps->c.screen_info->root ||
(ps->overlay && ev->window == ps->overlay)) {
int more = ev->count + 1;
if (ps->n_expose == ps->size_expose) {
if (ps->expose_rects) {
ps->expose_rects =
crealloc(ps->expose_rects, ps->size_expose + more);
ps->size_expose += more;
} else {
ps->expose_rects = ccalloc(more, rect_t);
ps->size_expose = more;
}
}
ps->expose_rects[ps->n_expose].x1 = ev->x;
ps->expose_rects[ps->n_expose].y1 = ev->y;
ps->expose_rects[ps->n_expose].x2 = ev->x + ev->width;
ps->expose_rects[ps->n_expose].y2 = ev->y + ev->height;
ps->n_expose++;
if (ev->count == 0) {
expose_root(ps, ps->expose_rects, ps->n_expose);
ps->n_expose = 0;
}
auto overlay = session_get_overlay(ps);
if (ev->window == session_get_x_connection(ps)->screen_info->root ||
(overlay && ev->window == overlay)) {
region_t region;
pixman_region32_init_rect(&region, ev->x, ev->y, ev->width, ev->height);
add_damage(ps, &region);
pixman_region32_fini(&region);
}
}
static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) {
auto options = session_get_options(ps);
auto atoms = session_get_atoms(ps);
auto c = session_get_x_connection(ps);
auto c2_state = session_get_c2(ps);
if (unlikely(log_get_level_tls() <= LOG_LEVEL_TRACE)) {
// Print out changed atom
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(
ps->c.c, xcb_get_atom_name(ps->c.c, ev->atom), NULL);
xcb_get_atom_name_reply_t *reply =
xcb_get_atom_name_reply(c->c, xcb_get_atom_name(c->c, ev->atom), NULL);
const char *name = "?";
int name_len = 1;
if (reply) {
@ -483,13 +470,13 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
free(reply);
}
if (ps->c.screen_info->root == ev->window) {
if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
if (c->screen_info->root == ev->window) {
if (options->use_ewmh_active_win && atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
// to update focus
ps->pending_updates = true;
session_mark_updates_pending(ps);
} else {
// Destroy the root "image" if the wallpaper probably changed
if (x_is_root_back_pixmap_atom(ps->atoms, ev->atom)) {
if (x_is_root_back_pixmap_atom(atoms, ev->atom)) {
root_damaged(ps);
}
}
@ -498,15 +485,15 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
return;
}
ps->pending_updates = true;
session_mark_updates_pending(ps);
// If WM_STATE changes
if (ev->atom == ps->atoms->aWM_STATE) {
if (ev->atom == atoms->aWM_STATE) {
// Check whether it could be a client window
if (!find_toplevel(ps, ev->window)) {
if (!session_find_toplevel(ps, ev->window)) {
// Reset event mask anyway
const uint32_t evmask =
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN);
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
XCB_AWAIT_VOID(xcb_change_window_attributes, c->c, ev->window,
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
auto w_top = find_managed_window_or_parent(ps, ev->window);
@ -521,60 +508,61 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
// If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
// there are always some stupid applications. (#144)
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) {
struct managed_win *w = find_toplevel(ps, ev->window);
if (ev->atom == atoms->a_NET_WM_WINDOW_TYPE) {
struct managed_win *w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
if (ev->atom == atoms->a_NET_WM_BYPASS_COMPOSITOR) {
// Unnecessary until we remove the queue_redraw in ev_handle
queue_redraw(ps);
}
// If _NET_WM_WINDOW_OPACITY changes
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY) {
auto w = find_managed_win(ps, ev->window) ?: find_toplevel(ps, ev->window);
if (ev->atom == atoms->a_NET_WM_WINDOW_OPACITY) {
auto w = find_managed_win(ps, ev->window)
?: session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// If frame extents property changes
if (ev->atom == ps->atoms->a_NET_FRAME_EXTENTS) {
auto w = find_toplevel(ps, ev->window);
if (ev->atom == atoms->a_NET_FRAME_EXTENTS) {
auto w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// If name changes
if (ps->atoms->aWM_NAME == ev->atom || ps->atoms->a_NET_WM_NAME == ev->atom) {
auto w = find_toplevel(ps, ev->window);
if (atoms->aWM_NAME == ev->atom || atoms->a_NET_WM_NAME == ev->atom) {
auto w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// If class changes
if (ps->atoms->aWM_CLASS == ev->atom) {
auto w = find_toplevel(ps, ev->window);
if (atoms->aWM_CLASS == ev->atom) {
auto w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// If role changes
if (ps->atoms->aWM_WINDOW_ROLE == ev->atom) {
auto w = find_toplevel(ps, ev->window);
if (atoms->aWM_WINDOW_ROLE == ev->atom) {
auto w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// If _COMPTON_SHADOW changes
if (ps->atoms->a_COMPTON_SHADOW == ev->atom) {
if (atoms->a_COMPTON_SHADOW == ev->atom) {
auto w = find_managed_win(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
@ -582,31 +570,31 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
}
// If a leader property changes
if ((ps->o.detect_transient && ps->atoms->aWM_TRANSIENT_FOR == ev->atom) ||
(ps->o.detect_client_leader && ps->atoms->aWM_CLIENT_LEADER == ev->atom)) {
auto w = find_toplevel(ps, ev->window);
if ((options->detect_transient && atoms->aWM_TRANSIENT_FOR == ev->atom) ||
(options->detect_client_leader && atoms->aWM_CLIENT_LEADER == ev->atom)) {
auto w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
if (!ps->o.no_ewmh_fullscreen && ev->atom == ps->atoms->a_NET_WM_STATE) {
auto w = find_toplevel(ps, ev->window);
if (!options->no_ewmh_fullscreen && ev->atom == atoms->a_NET_WM_STATE) {
auto w = session_find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// Check for other atoms we are tracking
if (c2_state_is_property_tracked(ps->c2_state, ev->atom)) {
if (c2_state_is_property_tracked(c2_state, ev->atom)) {
bool change_is_on_client = false;
auto w = find_managed_win(ps, ev->window);
if (!w) {
w = find_toplevel(ps, ev->window);
w = session_find_toplevel(ps, ev->window);
change_is_on_client = true;
}
if (w) {
c2_window_state_mark_dirty(ps->c2_state, &w->c2_state, ev->atom,
c2_window_state_mark_dirty(c2_state, &w->c2_state, ev->atom,
change_is_on_client);
// Set FACTOR_CHANGED so rules based on properties will be
// re-evaluated.
@ -621,6 +609,8 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
// Only mapped window can receive damages
assert(w->state == WSTATE_MAPPED || win_check_flags_all(w, WIN_FLAGS_MAPPED));
auto options = session_get_options(ps);
auto c = session_get_x_connection(ps);
region_t parts;
pixman_region32_init(&parts);
@ -632,10 +622,9 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
// from the X server, which implies it has received our DamageSubtract request.
if (!w->ever_damaged) {
auto e = xcb_request_check(
ps->c.c,
xcb_damage_subtract_checked(ps->c.c, w->damage, XCB_NONE, XCB_NONE));
c->c, xcb_damage_subtract_checked(c->c, w->damage, XCB_NONE, XCB_NONE));
if (e) {
if (ps->o.show_all_xerrors) {
if (options->show_all_xerrors) {
x_print_error(e->sequence, e->major_code, e->minor_code,
e->error_code);
}
@ -645,14 +634,14 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
// We only binds the window pixmap once the window is damaged.
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
ps->pending_updates = true;
session_mark_updates_pending(ps);
} else {
auto cookie = xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE,
ps->damage_ring.x_region);
if (!ps->o.show_all_xerrors) {
set_ignore_cookie(&ps->c, cookie);
xcb_xfixes_region_t damage_region = session_get_damage_ring(ps)->x_region;
auto cookie = xcb_damage_subtract(c->c, w->damage, XCB_NONE, damage_region);
if (!options->show_all_xerrors) {
set_ignore_cookie(c, cookie);
}
x_fetch_region(&ps->c, ps->damage_ring.x_region, &parts);
x_fetch_region(c, damage_region, &parts);
pixman_region32_translate(&parts, w->g.x + w->g.border_width,
w->g.y + w->g.border_width);
}
@ -663,13 +652,13 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
// Why care about damage when screen is unredirected?
// We will force full-screen repaint on redirection.
if (!ps->redirected) {
if (!session_is_redirected(ps)) {
pixman_region32_fini(&parts);
return;
}
// Remove the part in the damage area that could be ignored
if (w->reg_ignore && win_is_region_ignore_valid(ps, w)) {
if (w->reg_ignore && session_is_win_region_ignore_valid(ps, w)) {
pixman_region32_subtract(&parts, &parts, w->reg_ignore);
}
@ -714,7 +703,7 @@ static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev)
w->reg_ignore_valid = false;
win_set_flags(w, WIN_FLAGS_SIZE_STALE);
ps->pending_updates = true;
session_mark_updates_pending(ps);
}
static inline void
@ -727,12 +716,13 @@ ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) {
}
void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
auto c = session_get_x_connection(ps);
if (XCB_EVENT_RESPONSE_TYPE(ev) != KeymapNotify) {
x_discard_pending(&ps->c, ev->full_sequence);
x_discard_pending(c, ev->full_sequence);
}
xcb_window_t wid = ev_window(ps, ev);
if (ev->response_type != ps->damage_event + XCB_DAMAGE_NOTIFY) {
if (ev->response_type != session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY) {
log_debug("event %10.10s serial %#010x window %#010x \"%s\"",
ev_name(ps, ev), ev->full_sequence, wid, ev_window_name(ps, wid));
} else {
@ -750,9 +740,9 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
// https://bugs.freedesktop.org/show_bug.cgi?id=35945
// https://lists.freedesktop.org/archives/xcb/2011-November/007337.html
auto response_type = XCB_EVENT_RESPONSE_TYPE(ev);
auto proc = XESetWireToEvent(ps->c.dpy, response_type, 0);
auto proc = XESetWireToEvent(c->dpy, response_type, 0);
if (proc) {
XESetWireToEvent(ps->c.dpy, response_type, proc);
XESetWireToEvent(c->dpy, response_type, proc);
XEvent dummy;
// Stop Xlib from complaining about lost sequence numbers.
@ -762,8 +752,8 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
//
// We only need the low 16 bits
uint16_t seq = ev->sequence;
ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->c.dpy) & 0xffff);
proc(ps->c.dpy, &dummy, (xEvent *)ev);
ev->sequence = (uint16_t)(LastKnownRequestProcessed(c->dpy) & 0xffff);
proc(c->dpy, &dummy, (xEvent *)ev);
// Restore the sequence number
ev->sequence = seq;
}
@ -798,18 +788,21 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
case SelectionClear:
ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev);
break;
case 0: x_handle_error(&ps->c, (xcb_generic_error_t *)ev); break;
case 0: x_handle_error(c, (xcb_generic_error_t *)ev); break;
default:
if (ps->shape_exists && ev->response_type == ps->shape_event) {
if (session_has_shape_extension(ps) &&
ev->response_type == session_get_shape_extention_event(ps)) {
ev_shape_notify(ps, (xcb_shape_notify_event_t *)ev);
break;
}
if (ps->randr_exists &&
ev->response_type == (ps->randr_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
if (session_has_randr_extension(ps) &&
ev->response_type == (session_get_randr_extention_event(ps) +
XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
set_root_flags(ps, ROOT_FLAGS_SCREEN_CHANGE);
break;
}
if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) {
if (session_get_damage_extention_event(ps) + XCB_DAMAGE_NOTIFY ==
ev->response_type) {
ev_damage_notify(ps, (xcb_damage_notify_event_t *)ev);
break;
}

View File

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

View File

@ -13,12 +13,11 @@
#include "common.h"
#include "compiler.h"
#include "log.h"
#include "picom.h"
#include "region.h"
#include "render.h"
#include "win.h"
#include <ctype.h>
#include <epoxy/gl.h>
#include <epoxy/glx.h>
#include <locale.h>
@ -73,6 +72,9 @@ typedef struct glx_session {
int z;
glx_blur_pass_t *blur_passes;
glx_round_pass_t *round_passes;
/// Custom GLX program used for painting window.
glx_prog_main_t glx_prog_win;
struct glx_fbconfig_info argb_fbconfig;
} glx_session_t;
/// @brief Wrapper of a bound GLX texture.
@ -96,9 +98,9 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
int width, int height, int z, double opacity, bool argb, bool neg,
const region_t *reg_tgt, const glx_prog_main_t *pprogram);
bool glx_init(session_t *ps, bool need_render);
struct glx_session *glx_init(session_t *ps, bool need_render);
void glx_destroy(session_t *ps);
void glx_destroy(struct session *ps, struct glx_session *psglx);
void glx_on_root_change(session_t *ps);
@ -148,7 +150,8 @@ unsigned char *glx_take_screenshot(session_t *ps, int *out_length);
* Check if there's a GLX context.
*/
static inline bool glx_has_context(session_t *ps) {
return ps->psglx && ps->psglx->context;
auto psglx = session_get_psglx(ps);
return psglx && psglx->context;
}
/**

View File

@ -19,6 +19,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <libgen.h>
#include <locale.h>
#include <math.h>
#include <sched.h>
#include <stddef.h>
@ -41,6 +42,7 @@
#include <ev.h>
#include <test.h>
#include "backend/driver.h"
#include "common.h"
#include "compiler.h"
#include "config.h"
@ -50,6 +52,7 @@
#include "picom.h"
#include "transition.h"
#include "win_defs.h"
#include "xcb/xproto.h"
#ifdef CONFIG_OPENGL
#include "opengl.h"
#endif
@ -73,6 +76,246 @@
#include "win.h"
#include "x.h"
/// Structure containing all necessary data for a session.
typedef struct session {
// === Event handlers ===
/// ev_io for X connection
ev_io xiow;
/// Timeout for delayed unredirection.
ev_timer unredir_timer;
/// Timer for fading
ev_timer fade_timer;
/// Use an ev_timer callback for drawing
ev_timer draw_timer;
/// Called every time we have timeouts or new data on socket,
/// so we can be sure if xcb read from X socket at anytime during event
/// handling, we will not left any event unhandled in the queue
ev_prepare event_check;
/// Signal handler for SIGUSR1
ev_signal usr1_signal;
/// Signal handler for SIGINT
ev_signal int_signal;
// === Backend related ===
/// backend data
backend_t *backend_data;
/// backend blur context
void *backend_blur_context;
/// graphic drivers used
enum driver drivers;
/// file watch handle
void *file_watch_handle;
/// libev mainloop
struct ev_loop *loop;
/// Shaders
struct shader_info *shaders;
// === Display related ===
/// X connection
struct x_connection c;
/// Whether the X server is grabbed by us
bool server_grabbed;
/// Width of root window.
int root_width;
/// Height of root window.
int root_height;
/// X Composite overlay window.
xcb_window_t overlay;
/// The target window for debug mode
xcb_window_t debug_window;
/// Whether the root tile is filled by us.
bool root_tile_fill;
/// Picture of the root window background.
paint_t root_tile_paint;
/// The backend data the root pixmap bound to
image_handle root_image;
/// A region of the size of the screen.
region_t screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting
/// mode.
xcb_render_picture_t root_picture;
/// A Picture acting as the painting target.
xcb_render_picture_t tgt_picture;
/// Temporary buffer to paint to before sending to display.
paint_t tgt_buffer;
/// Window ID of the window we register as a symbol.
xcb_window_t reg_win;
#ifdef CONFIG_OPENGL
/// Pointer to GLX data.
struct glx_session *psglx;
#endif
/// Sync fence to sync draw operations
xcb_sync_fence_t sync_fence;
/// Whether we are rendering the first frame after screen is redirected
bool first_frame;
/// Whether screen has been turned off
bool screen_is_off;
/// When last MSC event happened, in useconds.
uint64_t last_msc_instant;
/// The last MSC number
uint64_t last_msc;
/// The delay between when the last frame was scheduled to be rendered, and when
/// the render actually started.
uint64_t last_schedule_delay;
/// When do we want our next frame to start rendering.
uint64_t next_render;
/// Whether we can perform frame pacing.
bool frame_pacing;
/// Vblank event scheduler
struct vblank_scheduler *vblank_scheduler;
/// Render statistics
struct render_statistics render_stats;
// === Operation related ===
/// Flags related to the root window
uint64_t root_flags;
/// Program options.
options_t o;
/// State object for c2.
struct c2_state *c2_state;
/// Whether we have hit unredirection timeout.
bool tmout_unredir_hit;
/// If the backend is busy. This means two things:
/// Either the backend is currently rendering a frame, or a frame has been
/// rendered but has yet to be presented. In either case, we should not start
/// another render right now. As if we start issuing rendering commands now, we
/// will have to wait for either the current render to finish, or the current
/// back buffer to become available again. In either case, we will be wasting
/// time.
bool backend_busy;
/// Whether a render is queued. This generally means there are pending updates
/// to the screen that's neither included in the current render, nor on the
/// screen.
bool render_queued;
/// For tracking damaged regions
struct damage_ring damage_ring;
/// Whether all windows are currently redirected.
bool redirected;
/// Pre-generated alpha pictures.
xcb_render_picture_t *alpha_picts;
/// Time of last fading. In milliseconds.
long long fade_time;
// Cached blur convolution kernels.
struct x_convolution_kernel **blur_kerns_cache;
/// If we should quit
bool quit : 1;
// TODO(yshui) use separate flags for different kinds of updates so we don't
// waste our time.
/// Whether there are pending updates, like window creation, etc.
bool pending_updates : 1;
// === Expose event related ===
/// Pointer to an array of <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
#define session_ptr(ptr, member) \
({ \
@ -156,7 +399,7 @@ static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t
auto w = find_managed_win(ps, wid);
if (!w) {
w = find_toplevel(ps, wid);
w = session_find_toplevel(ps, wid);
}
if (!w) {
w = find_managed_window_or_parent(ps, wid);
@ -536,8 +779,8 @@ uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode) {
}
// Check if it's a mapped client window
if (mode == WIN_EVMODE_CLIENT ||
((w = find_toplevel(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
if (mode == WIN_EVMODE_CLIENT || ((w = session_find_toplevel(ps, wid)) &&
w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
}
@ -824,7 +1067,7 @@ static void configure_root(session_t *ps) {
rebuild_shadow_exclude_reg(ps);
// Invalidate reg_ignore from the top
auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
auto top_w = session_get_next_managed_win_in_stack(ps, &ps->window_stack);
if (top_w) {
rc_region_unref(&top_w->reg_ignore);
top_w->reg_ignore_valid = false;
@ -1459,9 +1702,505 @@ xcb_window_t session_get_target_window(session_t *ps) {
return ps->overlay != XCB_NONE ? ps->overlay : ps->c.screen_info->root;
}
#ifdef CONFIG_DBUS
struct cdbus_data *session_get_cdbus(struct session *ps) {
return ps->dbus_data;
}
#endif
struct ev_loop *session_get_mainloop(struct session *ps) {
return ps->loop;
}
struct options *session_get_options(struct session *ps) {
return &ps->o;
}
enum driver session_get_driver(struct session *ps) {
return ps->drivers;
}
unsigned int session_get_window_count(session_t *ps) {
unsigned int count = 0;
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
++count;
}
return count;
}
struct managed_win *session_get_active_win(session_t *ps) {
return ps->active_win;
}
void session_set_active_win(session_t *ps, struct managed_win *w) {
ps->active_win = w;
}
xcb_window_t session_get_active_leader(session_t *ps) {
return ps->active_leader;
}
void session_set_active_leader(session_t *ps, xcb_window_t leader) {
ps->active_leader = leader;
}
struct x_connection *session_get_x_connection(session_t *ps) {
return &ps->c;
}
struct win *session_get_next_win_in_stack(struct session *ps, struct win *w) {
if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) {
return list_entry(w->stack_neighbour.next, struct win, stack_neighbour);
}
return NULL;
}
// Find the managed window immediately below `i` in the window stack
struct managed_win *
session_get_next_managed_win_in_stack(const session_t *ps, const struct list_node *cursor) {
while (!list_node_is_last(&ps->window_stack, cursor)) {
auto next = list_entry(cursor->next, struct win, stack_neighbour);
if (next->managed) {
return (struct managed_win *)next;
}
cursor = &next->stack_neighbour;
}
return NULL;
}
xcb_window_t session_get_overlay(session_t *ps) {
return ps->overlay;
}
struct damage_ring *session_get_damage_ring(session_t *ps) {
return &ps->damage_ring;
}
void session_assert_server_grabbed(session_t *ps attr_unused) {
assert(ps->server_grabbed);
}
image_handle session_get_root_image(session_t *ps) {
return ps->root_image;
}
void session_record_cpu_time(session_t *ps) {
auto now = get_time_timespec();
auto now_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
if (ps->next_render > 0) {
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %" PRIu64,
labs((long)now_us - (long)ps->next_render),
now_us < ps->next_render ? "early" : "late", now_us,
ps->next_render);
ps->last_schedule_delay = 0;
if (now_us > ps->next_render) {
ps->last_schedule_delay = now_us - ps->next_render;
}
}
}
typeof(session_get_root_extent(NULL)) session_get_root_extent(session_t *ps) {
typeof(session_get_root_extent(ps)) ret = {.height = ps->root_height,
.width = ps->root_width};
return ret;
}
struct atom *session_get_atoms(session_t *ps) {
return ps->atoms;
}
struct c2_state *session_get_c2(session_t *ps) {
return ps->c2_state;
}
void session_mark_updates_pending(session_t *ps) {
ps->pending_updates = true;
}
struct backend_base *session_get_backend_data(session_t *ps) {
return ps->backend_data;
}
struct backend_shadow_context *session_get_backend_shadow_context(session_t *ps) {
return ps->shadow_context;
}
void *session_get_backend_blur_context(session_t *ps) {
return ps->backend_blur_context;
}
struct x_convolution_kernel **session_get_blur_kern_cache(session_t *ps) {
return ps->blur_kerns_cache;
}
void session_set_blur_kern_cache(session_t *ps, struct x_convolution_kernel **cache) {
ps->blur_kerns_cache = cache;
}
void session_foreach_win(session_t *ps, void (*func)(struct win *, void *), void *data) {
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
func(w, data);
}
}
struct shader_info *session_get_shader_info(session_t *ps, const char *key) {
struct shader_info *info = NULL;
if (key) {
HASH_FIND_STR(ps->shaders, key, info);
}
return info;
}
bool session_is_redirected(session_t *ps) {
return ps->redirected;
}
void session_delete_win(session_t *ps, struct win *w) {
HASH_DEL(ps->windows, w);
}
/**
* Find a managed window from window id in window linked list of the session.
*/
struct win *session_find_win(session_t *ps, xcb_window_t id) {
if (!id) {
return NULL;
}
struct win *w = NULL;
HASH_FIND_INT(ps->windows, &id, w);
assert(w == NULL || !w->destroyed);
return w;
}
/// Insert a new window after list_node `prev`
/// New window will be in unmapped state
static struct win *session_add_win(session_t *ps, xcb_window_t id, struct list_node *prev) {
log_debug("Adding window %#010x", id);
struct win *old_w = NULL;
HASH_FIND_INT(ps->windows, &id, old_w);
assert(old_w == NULL);
auto new_w = cmalloc(struct win);
list_insert_after(prev, &new_w->stack_neighbour);
new_w->id = id;
new_w->managed = false;
new_w->is_new = true;
new_w->destroyed = false;
HASH_ADD_INT(ps->windows, id, new_w);
session_mark_updates_pending(ps);
return new_w;
}
/// Insert a new win entry at the top of the stack
struct win *session_add_win_top(session_t *ps, xcb_window_t id) {
return session_add_win(ps, id, &ps->window_stack);
}
/// Insert a new window above window with id `below`, if there is no window, add
/// to top New window will be in unmapped state
struct win *session_add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below) {
struct win *w = NULL;
HASH_FIND_INT(ps->windows, &below, w);
if (!w) {
if (!list_is_empty(&ps->window_stack)) {
// `below` window is not found even if the window stack is
// not empty
return NULL;
}
return session_add_win_top(ps, id);
}
// we found something from the hash table, so if the stack is
// empty, we are in an inconsistent state.
assert(!list_is_empty(&ps->window_stack));
return session_add_win(ps, id, w->stack_neighbour.prev);
}
/// Move window `w` so it's before `next` in the list
static inline void session_restack_win(session_t *ps, struct win *w, struct list_node *next) {
struct managed_win *mw = NULL;
if (w->managed) {
mw = (struct managed_win *)w;
}
if (mw) {
// This invalidates all reg_ignore below the new stack position of
// `w`
mw->reg_ignore_valid = false;
rc_region_unref(&mw->reg_ignore);
// This invalidates all reg_ignore below the old stack position of
// `w`
auto next_w = session_get_next_managed_win_in_stack(ps, &w->stack_neighbour);
if (next_w) {
next_w->reg_ignore_valid = false;
rc_region_unref(&next_w->reg_ignore);
}
}
list_move_before(&w->stack_neighbour, next);
// add damage for this window
if (mw) {
add_damage_from_win(ps, mw);
}
#ifdef DEBUG_RESTACK
log_trace("Window stack modified. Current stack:");
for (auto c = ps->list; c; c = c->next) {
const char *desc = "";
if (c->state == WSTATE_DESTROYING) {
desc = "(D) ";
}
log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
}
#endif
}
struct list_node *session_get_win_stack(session_t *ps) {
return &ps->window_stack;
}
/// Move window `w` so it's right above `below`
void session_restack_above(session_t *ps, struct win *w, xcb_window_t below) {
xcb_window_t old_below;
if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) {
old_below = list_next_entry(w, stack_neighbour)->id;
} else {
old_below = XCB_NONE;
}
log_debug("Restack %#010x (%s), old_below: %#010x, new_below: %#010x", w->id,
win_get_name_if_managed(w), old_below, below);
if (old_below != below) {
struct list_node *new_next;
if (!below) {
new_next = &ps->window_stack;
} else {
struct win *tmp_w = NULL;
HASH_FIND_INT(ps->windows, &below, tmp_w);
if (!tmp_w) {
log_error("Failed to found new below window %#010x.", below);
return;
}
new_next = &tmp_w->stack_neighbour;
}
session_restack_win(ps, w, new_next);
}
}
void session_restack_bottom(session_t *ps, struct win *w) {
session_restack_above(ps, w, 0);
}
void session_restack_top(session_t *ps, struct win *w) {
log_debug("Restack %#010x (%s) to top", w->id, win_get_name_if_managed(w));
if (&w->stack_neighbour == ps->window_stack.next) {
// already at top
return;
}
session_restack_win(ps, w, ps->window_stack.next);
}
/**
* Clear leader cache of all windows.
*/
void session_clear_cache_win_leaders(session_t *ps) {
win_stack_foreach_managed(w, &ps->window_stack) {
w->cache_leader = XCB_NONE;
}
}
bool session_is_win_region_ignore_valid(session_t *ps, const struct managed_win *w) {
win_stack_foreach_managed(i, &ps->window_stack) {
if (i == w) {
break;
}
if (!i->reg_ignore_valid) {
return false;
}
}
return true;
}
/**
* Find out the WM frame of a client window using existing data.
*
* @param id window ID
* @return struct win object of the found window, NULL if not found
*/
struct managed_win *session_find_toplevel(session_t *ps, xcb_window_t id) {
if (!id) {
return NULL;
}
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
if (!w->managed) {
continue;
}
auto mw = (struct managed_win *)w;
if (mw->client_win == id) {
return mw;
}
}
return NULL;
}
#ifdef CONFIG_OPENGL
struct glx_session *session_get_psglx(session_t *ps) {
return ps->psglx;
}
void session_set_psglx(session_t *ps, struct glx_session *psglx) {
ps->psglx = psglx;
}
#endif
xcb_render_picture_t *session_get_alpha_pictures(session_t *ps) {
return ps->alpha_picts;
}
struct x_monitors *session_get_monitors(session_t *ps) {
return &ps->monitors;
}
paint_t *session_get_tgt_buffer(session_t *ps) {
return &ps->tgt_buffer;
}
xcb_render_picture_t session_get_tgt_picture(session_t *ps) {
return ps->tgt_picture;
}
paint_t *session_get_root_tile_paint(session_t *ps) {
return &ps->root_tile_paint;
}
bool session_get_root_tile_fill(session_t *ps) {
return ps->root_tile_fill;
}
void session_set_root_tile_fill(session_t *ps, bool fill) {
ps->root_tile_fill = fill;
}
void session_vsync_wait(session_t *ps) {
if (ps->vsync_wait) {
ps->vsync_wait(ps);
}
}
xcb_render_picture_t session_get_white_picture(session_t *ps) {
return ps->white_picture;
}
xcb_render_picture_t session_get_black_picture(session_t *ps) {
return ps->black_picture;
}
xcb_render_picture_t session_get_cshadow_picture(session_t *ps) {
return ps->cshadow_picture;
}
void session_set_white_picture(session_t *ps, xcb_render_picture_t p) {
ps->white_picture = p;
}
void session_set_black_picture(session_t *ps, xcb_render_picture_t p) {
ps->black_picture = p;
}
void session_set_cshadow_picture(session_t *ps, xcb_render_picture_t p) {
ps->cshadow_picture = p;
}
region_t *session_get_screen_reg(session_t *ps) {
return &ps->screen_reg;
}
region_t *session_get_shadow_exclude_reg(session_t *ps) {
return &ps->shadow_exclude_reg;
}
void session_set_vsync_wait(session_t *ps, int (*vsync_wait)(session_t *)) {
ps->vsync_wait = vsync_wait;
}
bool session_has_glx_extension(session_t *ps) {
return ps->glx_exists;
}
bool session_has_shape_extension(session_t *ps) {
return ps->shape_exists;
}
bool session_has_present_extension(session_t *ps) {
return ps->present_exists;
}
bool session_has_xsync_extension(session_t *ps) {
return ps->xsync_exists;
}
bool session_has_randr_extension(session_t *ps) {
return ps->randr_exists;
}
int session_get_xfixes_extension_error(session_t *ps) {
return ps->xfixes_error;
}
int session_get_render_extension_error(session_t *ps) {
return ps->render_error;
}
int session_get_damage_extension_error(session_t *ps) {
return ps->damage_error;
}
int session_get_glx_extension_error(session_t *ps) {
return ps->glx_error;
}
int session_get_xsync_extension_error(session_t *ps) {
return ps->xsync_error;
}
int session_get_xsync_extention_event(session_t *ps) {
return ps->xsync_event;
}
int session_get_damage_extention_event(session_t *ps) {
return ps->damage_event;
}
int session_get_shape_extention_event(session_t *ps) {
return ps->shape_event;
}
int session_get_randr_extention_event(session_t *ps) {
return ps->randr_event;
}
void session_xsync_wait_fence(session_t *ps) {
if (ps->o.xrender_sync_fence && ps->xsync_exists &&
!x_fence_sync(&ps->c, ps->sync_fence)) {
log_error("x_fence_sync failed, xrender-sync-fence will be "
"disabled from now on.");
xcb_sync_destroy_fence(ps->c.c, ps->sync_fence);
ps->sync_fence = XCB_NONE;
ps->o.xrender_sync_fence = false;
ps->xsync_exists = false;
}
}
uint8_t session_redirection_mode(session_t *ps) {
if (ps->o.debug_mode) {
@ -1679,7 +2418,7 @@ static void handle_new_windows(session_t *ps) {
}
// Send D-Bus signal
if (ps->o.dbus) {
cdbus_ev_win_added(ps->dbus_data, new_w);
cdbus_ev_win_added(session_get_cdbus(ps), new_w);
}
}
}
@ -2031,9 +2770,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
.tgt_picture = XCB_NONE,
.tgt_buffer = PAINT_INIT,
.reg_win = XCB_NONE,
#ifdef CONFIG_OPENGL
.glx_prog_win = GLX_PROG_MAIN_INIT,
#endif
.redirected = false,
.alpha_picts = NULL,
.fade_time = 0L,
@ -2413,9 +3149,12 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
apply_driver_workarounds(ps, ps->drivers);
// Initialize filters, must be preceded by OpenGL context creation
if (ps->o.legacy_backends && !init_render(ps)) {
log_fatal("Failed to initialize the backend");
exit(1);
if (ps->o.legacy_backends) {
ps->alpha_picts = ccalloc(MAX_ALPHA + 1, xcb_render_picture_t);
if (!init_render(ps)) {
log_fatal("Failed to initialize the backend");
exit(1);
}
}
if (ps->o.print_diagnostics) {
@ -2554,7 +3293,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
nchildren = xcb_query_tree_children_length(query_tree_reply);
for (int i = 0; i < nchildren; i++) {
add_win_above(ps, children[i], i ? children[i - 1] : XCB_NONE);
session_add_win_above(ps, children[i], i ? children[i - 1] : XCB_NONE);
}
free(query_tree_reply);
}
@ -2697,16 +3436,15 @@ static void session_destroy(session_t *ps) {
// backend is deinitialized in unredirect()
assert(ps->backend_data == NULL);
} else {
// Free all GLX resources of windows
win_stack_foreach_managed(w, &ps->window_stack) {
free_win_res_glx(ps, w);
}
deinit_render(ps);
free(ps->alpha_picts);
ps->alpha_picts = NULL;
}
#if CONFIG_OPENGL
if (glx_has_context(ps)) {
// GLX context created, but not for rendering
glx_destroy(ps);
}
#endif
// Flush all events
xcb_aux_sync(ps->c.c);
ev_io_stop(ps->loop, &ps->xiow);

View File

@ -1,29 +1,25 @@
// SPDX-License-Identifier: MIT
// Copyright (c)
// Throw everything in here.
// !!! DON'T !!!
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui.
// === Includes ===
#include <locale.h>
#pragma once
#include <stdbool.h>
#include <stdlib.h>
#include <xcb/xproto.h>
#include <X11/Xutil.h>
#include "backend/backend.h"
#include "backend/driver.h"
#include "c2.h"
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "log.h" // XXX clean up
#include "ev.h"
#include "list.h"
#include "region.h"
#include "render.h"
#include "types.h"
#include "utils.h"
#include "win.h"
#include "x.h"
#include "xcb/render.h"
enum root_flags {
ROOT_FLAGS_SCREEN_CHANGE = 1, // Received RandR screen change notify, we
@ -31,28 +27,120 @@ enum root_flags {
ROOT_FLAGS_CONFIGURED = 2 // Received configure notify on the root window
};
// == Functions ==
// TODO(yshui) move static inline functions that are only used in picom.c, into picom.c
void add_damage(session_t *ps, const region_t *damage);
uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode);
void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce);
void root_damaged(session_t *ps);
void queue_redraw(session_t *ps);
void discard_pending(session_t *ps, uint32_t sequence);
void set_root_flags(session_t *ps, uint64_t flags);
void quit(session_t *ps);
xcb_window_t session_get_target_window(session_t *);
uint8_t session_redirection_mode(session_t *ps);
struct options *session_get_options(session_t *ps);
struct ev_loop *session_get_mainloop(session_t *ps);
struct x_connection *session_get_x_connection(session_t *ps);
xcb_window_t session_get_overlay(session_t *ps);
struct atom *session_get_atoms(session_t *ps);
struct c2_state *session_get_c2(session_t *ps);
void session_mark_updates_pending(session_t *ps);
// TODO(yshui) can we lump these 3 backend related functions together?
struct backend_base *session_get_backend_data(session_t *ps);
struct backend_shadow_context *session_get_backend_shadow_context(session_t *ps);
void *session_get_backend_blur_context(session_t *ps);
struct shader_info *session_get_shader_info(session_t *ps, const char *key);
bool session_is_redirected(session_t *ps);
void session_xsync_wait_fence(session_t *ps);
enum driver session_get_driver(session_t *ps);
struct damage_ring *session_get_damage_ring(session_t *ps);
void session_assert_server_grabbed(session_t *ps);
image_handle session_get_root_image(session_t *ps);
/// Record the amount of CPU time spent in the render cycle. This is called in
/// backend.c just before we start sending backend rendering commands. `struct session`
/// keeps track when each render call started, when this function is called, the time
/// difference is used as the CPU time spent in the render cycle.
void session_record_cpu_time(session_t *ps);
// TODO(yshui) move window related data out of session_t, and remove these getters
void session_delete_win(session_t *ps, struct win *w);
struct list_node *session_get_win_stack(session_t *ps);
struct managed_win *session_get_active_win(session_t *ps);
void session_set_active_win(session_t *ps, struct managed_win *w);
struct win *session_get_next_win_in_stack(session_t *ps, struct win *w);
unsigned int session_get_window_count(session_t *ps);
void session_foreach_win(session_t *ps, void (*func)(struct win *w, void *data), void *data);
xcb_window_t session_get_active_leader(session_t *ps);
void session_set_active_leader(session_t *ps, xcb_window_t leader);
void session_clear_cache_win_leaders(session_t *ps);
bool session_is_win_region_ignore_valid(session_t *ps, const struct managed_win *w);
struct win *session_find_win(session_t *ps, xcb_window_t id);
/// Insert a new window above window with id `below`, if there is no window, add to top
/// New window will be in unmapped state
struct win *session_add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below);
/// Insert a new win entry at the top of the stack
struct win *session_add_win_top(session_t *ps, xcb_window_t id);
/// Move window `w` to be right above `below`
void session_restack_above(session_t *ps, struct win *w, xcb_window_t below);
/// Move window `w` to the bottom of the stack
void session_restack_bottom(session_t *ps, struct win *w);
/// Move window `w` to the top of the stack
void session_restack_top(session_t *ps, struct win *w);
/**
* Find out the WM frame of a client window using existing data.
*
* @param id window ID
* @return struct win object of the found window, NULL if not found
*/
struct managed_win *session_find_toplevel(session_t *ps, xcb_window_t id);
// Find the managed window immediately below `w` in the window stack
struct managed_win *
session_get_next_managed_win_in_stack(const session_t *ps, const struct list_node *cursor);
// TODO(yshui) Legacy backend stuff, should probably lump them together. Also remove them
// once the legacy backends are removed.
#ifdef CONFIG_OPENGL
struct glx_session *session_get_psglx(session_t *ps);
void session_set_psglx(session_t *ps, struct glx_session *psglx);
#endif
struct {
int height, width;
} session_get_root_extent(session_t *ps);
xcb_render_picture_t *session_get_alpha_pictures(session_t *ps);
struct x_monitors *session_get_monitors(session_t *ps);
paint_t *session_get_tgt_buffer(session_t *ps);
xcb_render_picture_t session_get_tgt_picture(session_t *ps);
paint_t *session_get_root_tile_paint(session_t *ps);
bool session_get_root_tile_fill(session_t *ps);
void session_set_root_tile_fill(session_t *ps, bool fill);
void session_vsync_wait(session_t *ps);
void session_set_vsync_wait(session_t *ps, int (*vsync_wait)(session_t *));
xcb_render_picture_t session_get_white_picture(session_t *ps);
xcb_render_picture_t session_get_black_picture(session_t *ps);
xcb_render_picture_t session_get_cshadow_picture(session_t *ps);
void session_set_black_picture(session_t *ps, xcb_render_picture_t p);
void session_set_white_picture(session_t *ps, xcb_render_picture_t p);
void session_set_cshadow_picture(session_t *ps, xcb_render_picture_t p);
region_t *session_get_screen_reg(session_t *ps);
region_t *session_get_shadow_exclude_reg(session_t *ps);
struct x_convolution_kernel **session_get_blur_kern_cache(session_t *ps);
void session_set_blur_kern_cache(session_t *ps, struct x_convolution_kernel **cache);
// TODO(yshui) has_extension and get_*_extension_error should probably be in struct
// x_connection, or just use xcb functions instead - those are cached as well.
bool session_has_glx_extension(session_t *ps);
bool session_has_shape_extension(session_t *ps);
bool session_has_present_extension(session_t *ps);
bool session_has_randr_extension(session_t *ps);
bool session_has_xsync_extension(session_t *ps);
int session_get_xfixes_extension_error(session_t *ps);
int session_get_render_extension_error(session_t *ps);
int session_get_damage_extension_error(session_t *ps);
int session_get_glx_extension_error(session_t *ps);
int session_get_xsync_extension_error(session_t *ps);
int session_get_xsync_extention_event(session_t *ps);
int session_get_damage_extention_event(session_t *ps);
int session_get_shape_extention_event(session_t *ps);
int session_get_randr_extention_event(session_t *ps);
#ifdef CONFIG_DBUS
struct cdbus_data *session_get_cdbus(struct session *);
@ -99,17 +187,3 @@ static inline void
free_win_res_glx(session_t *ps attr_unused, struct managed_win *w attr_unused) {
}
#endif
/**
* Dump an drawable's info.
*/
static inline void dump_drawable(session_t *ps, xcb_drawable_t drawable) {
auto r = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, drawable), NULL);
if (!r) {
log_trace("Drawable %#010x: Failed", drawable);
return;
}
log_trace("Drawable %#010x: x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u",
drawable, r->x, r->y, r->width, r->height, r->border_width, r->depth);
free(r);
}

File diff suppressed because it is too large Load Diff

View File

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

662
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);
/// Recheck if a window is fullscreen
void win_update_is_fullscreen(const session_t *ps, struct managed_win *w);
void win_update_is_fullscreen(session_t *ps, struct managed_win *w);
/**
* Check if a window has BYPASS_COMPOSITOR property set
*/
bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w);
bool win_is_bypassing_compositor(session_t *ps, const struct managed_win *w);
/**
* Get a rectangular region in global coordinates a window (and possibly
* its shadow) occupies.
@ -373,20 +373,9 @@ void win_get_region_noframe_local_without_corners(const struct managed_win *w, r
void win_get_region_frame_local(const struct managed_win *w, region_t *res);
/// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const struct managed_win *w);
/// Insert a new window above window with id `below`, if there is no window, add to top
/// New window will be in unmapped state
struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below);
/// Insert a new win entry at the top of the stack
struct win *add_win_top(session_t *ps, xcb_window_t id);
/// Query the Xorg for information about window `win`
/// `win` pointer might become invalid after this function returns
struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct win *win);
/// Move window `w` to be right above `below`
void restack_above(session_t *ps, struct win *w, xcb_window_t below);
/// Move window `w` to the bottom of the stack
void restack_bottom(session_t *ps, struct win *w);
/// Move window `w` to the top of the stack
void restack_top(session_t *ps, struct win *w);
/**
* Release a destroyed window that is no longer needed.
@ -403,8 +392,6 @@ void win_skip_fading(struct managed_win *w);
* Find a managed window from window id in window linked list of the session.
*/
struct managed_win *find_managed_win(session_t *ps, xcb_window_t id);
struct win *find_win(session_t *ps, xcb_window_t id);
struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
/**
* Find a managed window that is, or is a parent of `wid`.
*
@ -429,15 +416,16 @@ static inline bool attr_pure win_is_wmwin(const struct managed_win *w) {
return w->base.id == w->client_win && !w->a.override_redirect;
}
/// check if reg_ignore_valid is true for all windows above us
bool attr_pure win_is_region_ignore_valid(session_t *ps, const struct managed_win *w);
static inline const char *win_get_name_if_managed(const struct win *w) {
if (!w->managed) {
return "(unmanaged)";
}
auto mw = (struct managed_win *)w;
return mw->name;
}
/// Whether a given window is mapped on the X server side
bool win_is_mapped_in_x(const struct managed_win *w);
// Find the managed window immediately below `w` in the window stack
struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps,
const struct list_node *w);
/// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags);
/// Clear flags on a window. Some sanity checks are performed

17
src/x.c
View File

@ -21,6 +21,7 @@
#include <xcb/xfixes.h>
#include "atom.h"
#include "picom.h"
#ifdef CONFIG_OPENGL
#include "backend/gl/glx.h"
#endif
@ -49,7 +50,7 @@ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) {
xcb_err.major_code = ev->request_code;
xcb_err.minor_code = ev->minor_code;
xcb_err.error_code = ev->error_code;
x_handle_error(&ps_g->c, &xcb_err);
x_handle_error(session_get_x_connection(ps_g), &xcb_err);
return 0;
}
@ -588,13 +589,13 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
case XCB_##s: name = #s; break
// TODO(yshui) separate error code out from session_t
o = error_code - ps->xfixes_error;
o = error_code - session_get_xfixes_extension_error(ps);
switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
o = error_code - ps->damage_error;
o = error_code - session_get_damage_extension_error(ps);
switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error;
o = error_code - session_get_render_extension_error(ps);
switch (o) {
CASESTRRET2(RENDER_PICT_FORMAT);
CASESTRRET2(RENDER_PICTURE);
@ -603,8 +604,8 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
CASESTRRET2(RENDER_GLYPH);
}
if (ps->glx_exists) {
o = error_code - ps->glx_error;
if (session_has_glx_extension(ps)) {
o = error_code - session_get_glx_extension_error(ps);
switch (o) {
CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_CONTEXT_STATE);
@ -623,8 +624,8 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
}
}
if (ps->xsync_exists) {
o = error_code - ps->xsync_error;
if (session_get_xsync_extension_error(ps)) {
o = error_code - session_get_xsync_extension_error(ps);
switch (o) {
CASESTRRET(XSyncBadCounter);
CASESTRRET(XSyncBadAlarm);