Merge branch 'yshui:next' into next

This commit is contained in:
Arda Atci
2024-02-04 22:49:35 +03:00
committed by GitHub
28 changed files with 195 additions and 121 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
# Build files
.deps
.direnv
aclocal.m4
autom4te.cache
config.log

View File

@@ -1,5 +1,15 @@
# Unreleased
## New features
* Allow `corner-radius-rules` to override `corner-radius = 0`. Previously setting corner radius to 0 globally disables rounded corners. (#1170)
## Bug fixes
* Workaround a NVIDIA problem that causes high CPU usage after suspend/resume (#1172, #1168)
# v11.1 (2024-Jan-28)
## Bug fixes
* Fix missing fading on window close for some window managers. (#704)

View File

@@ -55,7 +55,7 @@ libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libpcre2-dev libpixma
On Fedora, the needed packages are
```
dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel
dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel xcb-util-devel
```
To build the documents, you need `asciidoc`

View File

@@ -209,7 +209,7 @@ struct backend_operations {
* @param backend_data backend data
* @param pixmap X pixmap to bind
* @param fmt information of the pixmap's visual
* @param owned whether the ownership of the pixmap is transfered to the backend
* @param owned whether the ownership of the pixmap is transferred to the backend
* @return backend internal data structure bound with this pixmap
*/
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
@@ -225,7 +225,7 @@ struct backend_operations {
struct backend_shadow_context *ctx);
/// Create a shadow image based on the parameters. Resulting image should have a
/// size of `width + radisu * 2` x `height + radius * 2`. Radius is set when the
/// size of `width + radius * 2` x `height + radius * 2`. Radius is set when the
/// shadow context is created.
/// Default implementation: default_render_shadow
///
@@ -284,9 +284,9 @@ struct backend_operations {
bool (*is_image_transparent)(backend_t *backend_data, void *image_data)
attr_nonnull(1, 2);
/// Get the age of the buffer content we are currently rendering ontop
/// Get the age of the buffer content we are currently rendering on top
/// of. The buffer that has just been `present`ed has a buffer age of 1.
/// Everytime `present` is called, buffers get older. Return -1 if the
/// Every time `present` is called, buffers get older. Return -1 if the
/// buffer is empty.
///
/// Optional
@@ -295,7 +295,7 @@ struct backend_operations {
/// Get the render time of the last frame. If the render is still in progress,
/// returns false. The time is returned in `ts`. Frames are delimited by the
/// present() calls. i.e. after a present() call, last_render_time() should start
/// reporting the time of the just presen1ted frame.
/// reporting the time of the just presented frame.
///
/// Optional, if not available, the most conservative estimation will be used.
bool (*last_render_time)(backend_t *backend_data, struct timespec *ts);

View File

@@ -19,11 +19,14 @@ void apply_driver_workarounds(struct session *ps, enum driver driver) {
}
}
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver) {
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver attr_unused) {
enum vblank_scheduler_type type = VBLANK_SCHEDULER_PRESENT;
#ifdef CONFIG_OPENGL
if (driver & DRIVER_NVIDIA) {
return VBLANK_SCHEDULER_SGI_VIDEO_SYNC;
type = VBLANK_SCHEDULER_SGI_VIDEO_SYNC;
}
return VBLANK_SCHEDULER_PRESENT;
#endif
return type;
}
enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) {

View File

@@ -185,7 +185,7 @@ void gl_destroy_window_shader(backend_t *backend_data attr_unused, void *shader)
* @note In order to reduce number of textures which needs to be
* allocated and deleted during this recursive render
* we reuse the same two textures for render source and
* destination simply by alterating between them.
* destination simply by alternating between them.
* Unfortunately on first iteration source_texture might
* be read-only. In this case we will select auxiliary_texture as
* destination_texture in order not to touch that read-only source
@@ -253,7 +253,7 @@ _gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destina
/*
* @brief Builds a 1x1 texture which has color corresponding to the average of all
* pixels of img by recursively rendering into texture of quorter the size (half
* pixels of img by recursively rendering into texture of quarter the size (half
* width and half height).
* Returned texture must not be deleted, since it's owned by the gl_image. It will be
* deleted when the gl_image is released.

View File

@@ -78,7 +78,7 @@ typedef struct {
GLint color_loc;
} gl_fill_shader_t;
/// @brief Wrapper of a binded GL texture.
/// @brief Wrapper of a bound GL texture.
struct gl_texture {
int refcount;
bool has_alpha;

View File

@@ -244,8 +244,8 @@ typedef struct session {
/// 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 the current render to finish, or the current
/// back buffer to be become available again. In either case, we will be wasting
/// 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
@@ -279,7 +279,7 @@ typedef struct session {
struct x_convolution_kernel **blur_kerns_cache;
/// If we should quit
bool quit : 1;
// TODO(yshui) use separate flags for dfferent kinds of updates so we don't
// 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;

View File

@@ -52,7 +52,7 @@
#else
# define attr_warn_unused_result
#endif
// An alias for conveninence
// An alias for convenience
#define must_use attr_warn_unused_result
#if __has_attribute(nonnull)

View File

@@ -563,7 +563,7 @@ static char *locate_auxiliary_file_at(const char *base, const char *scope, const
}
/**
* Get a path of an auxiliary file to read, could be a shader file, or any supplimenrary
* Get a path of an auxiliary file to read, could be a shader file, or any supplementary
* file.
*
* Follows the XDG specification to search for the shader file in configuration locations.

View File

@@ -366,7 +366,7 @@ char **xdg_config_dirs(void);
/// Parse a configuration file
/// Returns the actually config_file name used, allocated on heap
/// Outputs:
/// shadow_enable = whether shaodw is enabled globally
/// shadow_enable = whether shadow is enabled globally
/// fading_enable = whether fading is enabled globally
/// win_option_mask = whether option overrides for specific window type is set for given
/// options

View File

@@ -29,7 +29,7 @@
/// made the query when those events were already in the queue. so the reply you got is
/// more up-to-date than the events). Also, handling events when other client are making
/// concurrent requests is not good. Because the server states are changing without you
/// knowning them. This is super racy, and can cause lots of potential problems.
/// knowing them. This is super racy, and can cause lots of potential problems.
///
/// All of above mandates we do these things:
/// 1. Grab server when handling events
@@ -324,7 +324,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
}
if (ev->parent == ps->c.screen_info->root) {
// X will generate reparent notifiy even if the parent didn't actually
// X will generate reparent notify even if the parent didn't actually
// change (i.e. reparent again to current parent). So we check if that's
// the case
auto w = find_win(ps, ev->window);
@@ -465,7 +465,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
}
}
// Unconcerned about any other proprties on root window
// Unconcerned about any other properties on root window
return;
}
@@ -499,7 +499,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
}
if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
// Unnecessay until we remove the queue_redraw in ev_handle
// Unnecessary until we remove the queue_redraw in ev_handle
queue_redraw(ps);
}

View File

@@ -158,7 +158,7 @@ bool file_watch_add(void *_fwr, const char *filename, file_watch_cb_t cb, void *
fflags |= NOTE_CLOSE_WRITE;
#else
// NOTE_WRITE will receive notification more frequent than necessary, so is less
// preferrable
// preferable
fflags |= NOTE_WRITE;
#endif
struct kevent ev = {

View File

@@ -96,7 +96,7 @@ static inline double estimate_first_row_sum(double size, double r) {
// `a` is gaussian at (size, 0)
double a = exp(-0.5 * size * size / (r * r)) / sqrt(2 * M_PI) / r;
// The sum of the whole kernel is normalized to 1, i.e. each element is divided by
// factor sqaured. So the sum of the first row is a * factor / factor^2 = a /
// factor squared. So the sum of the first row is a * factor / factor^2 = a /
// factor
return a / factor;
}

View File

@@ -1129,7 +1129,7 @@ glx_blur_dst_end:
// TODO(bhagwan) this is a mess and needs a more consistent way of getting the border
// pixel I tried looking for a notify event for XCB_CW_BORDER_PIXEL (in
// xcb_create_window()) or a way to get the pixels from xcb_render_picture_t but the
// documentation for the xcb_xrender extension is literaly non existent...
// documentation for the xcb_xrender extension is literally non existent...
//
// NOTE(yshui) There is no consistent way to get the "border" color of a X window. From
// the WM's perspective there are multiple ways to implement window borders. Using

View File

@@ -75,7 +75,7 @@ typedef struct glx_session {
glx_round_pass_t *round_passes;
} glx_session_t;
/// @brief Wrapper of a binded GLX texture.
/// @brief Wrapper of a bound GLX texture.
typedef struct _glx_texture {
GLuint texture;
GLXPixmap glpixmap;
@@ -121,9 +121,9 @@ bool glx_bind_texture(session_t *ps, glx_texture_t **pptex, int x, int y, int wi
void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
/**
* Check if a texture is binded, or is binded to the given pixmap.
* Check if a texture is bound, or is bound to the given pixmap.
*/
static inline bool glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
static inline bool glx_tex_bound(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
return ptex && ptex->glpixmap && ptex->texture && (!pixmap || pixmap == ptex->pixmap);
}

View File

@@ -325,9 +325,9 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
int o = 0, longopt_idx = -1;
// Pre-parse the commandline arguments to check for --config and invalid
// Pre-parse the command line arguments to check for --config and invalid
// switches
// Must reset optind to 0 here in case we reread the commandline
// Must reset optind to 0 here in case we reread the command line
// arguments
optind = 1;
*config_file = NULL;
@@ -379,7 +379,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// instead of commas in atof().
setlocale(LC_NUMERIC, "C");
// Parse commandline arguments. Range checking will be done later.
// Parse command line arguments. Range checking will be done later.
bool failed = false;
const char *deprecation_message attr_unused =
@@ -731,7 +731,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
opt->blur_strength = atoi(optarg);
break;
case 333:
// --cornor-radius
// --corner-radius
opt->corner_radius = atoi(optarg);
break;
case 334:

View File

@@ -270,7 +270,7 @@ enum vblank_callback_action reschedule_render_at_vblank(struct vblank_event *e,
/// is no render currently scheduled. i.e. render_queued == false.
/// 2. then, we need to figure out the best time to start rendering. we need to
/// at least know when the next vblank will start, as we can't start render
/// before the current rendered frame is diplayed on screen. we have this
/// before the current rendered frame is displayed on screen. we have this
/// information from the vblank scheduler, it will notify us when that happens.
/// we might also want to delay the rendering even further to reduce latency,
/// this is discussed below, in FUTURE WORKS.
@@ -1330,14 +1330,43 @@ void root_damaged(session_t *ps) {
}
auto pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
if (pixmap != XCB_NONE) {
xcb_get_geometry_reply_t *r = xcb_get_geometry_reply(
ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
if (!r) {
goto err;
}
// We used to assume that pixmaps pointed by the root background
// pixmap atoms are owned by the root window and have the same
// depth and hence the same visual that we can use to bind them.
// However, some applications break this assumption, e.g. the
// Xfce's desktop manager xfdesktop that sets the _XROOTPMAP_ID
// atom to a pixmap owned by it that seems to always have 32 bpp
// depth when the common root window's depth is 24 bpp. So use the
// root window's visual only if the root background pixmap's depth
// matches the root window's depth. Otherwise, find a suitable
// visual for the root background pixmap's depth and use it.
//
// We can't obtain a suitable visual for the root background
// pixmap the same way as the win_bind_pixmap function because it
// requires a window and we have only a pixmap. We also can't not
// bind the root background pixmap in case of depth mismatch
// because some options rely on it's content, e.g.
// transparent-clipping.
xcb_visualid_t visual =
r->depth == ps->c.screen_info->root_depth
? ps->c.screen_info->root_visual
: x_get_visual_for_depth(&ps->c, r->depth);
free(r);
ps->root_image = ps->backend_data->ops->bind_pixmap(
ps->backend_data, pixmap,
x_get_visual_info(&ps->c, ps->c.screen_info->root_visual), false);
ps->backend_data, pixmap, x_get_visual_info(&ps->c, visual), false);
if (ps->root_image) {
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
ps->root_image, (int[]){ps->root_width, ps->root_height});
} else {
err:
log_error("Failed to bind root back pixmap");
}
}
@@ -2040,7 +2069,7 @@ static void x_event_callback(EV_P attr_unused, ev_io *w, int revents attr_unused
/**
* Turn on the program reset flag.
*
* This will result in the compostior resetting itself after next paint.
* This will result in the compositor resetting itself after next paint.
*/
static void reset_enable(EV_P_ ev_signal *w attr_unused, int revents attr_unused) {
log_info("picom is resetting...");
@@ -2117,11 +2146,11 @@ static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data)
/**
* Initialize a session.
*
* @param argc number of commandline arguments
* @param argv commandline arguments
* @param argc number of command line arguments
* @param argv command line arguments
* @param dpy the X Display
* @param config_file the path to the config file
* @param all_xerros whether we should report all X errors
* @param all_xerrors whether we should report all X errors
* @param fork whether we will fork after initialization
*/
static session_t *session_init(int argc, char **argv, Display *dpy,
@@ -2673,7 +2702,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
ps->server_grabbed = true;
// We are going to pull latest information from X server now, events sent by X
// earlier is irrelavant at this point.
// earlier is irrelevant at this point.
// A better solution is probably grabbing the server from the very start. But I
// think there still could be race condition that mandates discarding the events.
x_discard_events(&ps->c);

View File

@@ -89,7 +89,7 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
fbcfg = ppaint->fbcfg;
}
if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) {
if (force || !glx_tex_bound(ppaint->ptex, ppaint->pixmap)) {
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
repeat, fbcfg);
}
@@ -378,7 +378,7 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
}
#ifdef CONFIG_OPENGL
if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, XCB_NONE)) {
if (BKEND_GLX == ps->o.backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) {
return false;
}
#endif
@@ -604,20 +604,27 @@ static bool get_root_tile(session_t *ps) {
bool fill = false;
xcb_pixmap_t pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
// Make sure the pixmap we got is valid
if (pixmap && !x_validate_pixmap(&ps->c, pixmap)) {
pixmap = XCB_NONE;
xcb_get_geometry_reply_t *r;
if (pixmap) {
r = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
}
// Create a pixmap if there isn't any
if (!pixmap) {
xcb_visualid_t visual;
if (!pixmap || !r) {
pixmap =
x_create_pixmap(&ps->c, (uint8_t)ps->c.screen_info->root_depth, 1, 1);
if (pixmap == XCB_NONE) {
log_error("Failed to create pixmaps for root tile.");
return false;
}
visual = ps->c.screen_info->root_visual;
fill = true;
} else {
visual = r->depth == ps->c.screen_info->root_depth
? ps->c.screen_info->root_visual
: x_get_visual_for_depth(&ps->c, r->depth);
free(r);
}
// Create Picture
@@ -625,7 +632,7 @@ static bool get_root_tile(session_t *ps) {
.repeat = true,
};
ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
&ps->c, ps->c.screen_info->root_visual, pixmap, XCB_RENDER_CP_REPEAT, &pa);
&ps->c, visual, pixmap, XCB_RENDER_CP_REPEAT, &pa);
// Fill pixmap if needed
if (fill) {
@@ -646,8 +653,7 @@ static bool get_root_tile(session_t *ps) {
ps->root_tile_paint.pixmap = pixmap;
#ifdef CONFIG_OPENGL
if (BKEND_GLX == ps->o.backend) {
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0,
ps->c.screen_info->root_visual, false);
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, visual, false);
}
#endif

View File

@@ -51,10 +51,6 @@ void render_statistics_add_render_time_sample(struct render_statistics *rs, int
}
/// How much time budget we should give to the backend for rendering, in microseconds.
///
/// A `divisor` is also returned, indicating the target framerate. The divisor is
/// the number of vblanks we should wait between each frame. A divisor of 1 means
/// full framerate, 2 means half framerate, etc.
unsigned int render_statistics_get_budget(struct render_statistics *rs) {
if (rs->render_times.nelem < rs->render_times.window_size) {
// No valid render time estimates yet. Assume maximum budget.

View File

@@ -141,10 +141,10 @@ void rolling_max_pop_front(struct rolling_max *rm, int front) {
}
void rolling_max_push_back(struct rolling_max *rm, int val) {
// Update the prority queue.
// Update the priority queue.
// Remove all elements smaller than the new element from the queue. Because
// the new element will become the maximum element before them, and since they
// come b1efore the new element, they will have been popped before the new
// come before the new element, they will have been popped before the new
// element, so they will never become the maximum element.
while (rm->np) {
int p_tail = IDX(rm->p_head + rm->np - 1);

View File

@@ -236,7 +236,7 @@ allocchk_(const char *func_name, const char *file, unsigned int line, void *ptr)
((type *)allocchk(calloc((size_t)tmp, sizeof(type)))); \
})
/// @brief Wrapper of ealloc().
/// @brief Wrapper of realloc().
#define crealloc(ptr, nmemb) \
({ \
auto tmp = (nmemb); \

View File

@@ -18,7 +18,6 @@
#include <X11/Xutil.h>
#include <pthread.h>
#include "backend/gl/glx.h"
#endif
#include "compiler.h"
@@ -63,9 +62,9 @@ struct present_vblank_scheduler {
struct vblank_scheduler_ops {
size_t size;
void (*init)(struct vblank_scheduler *self);
bool (*init)(struct vblank_scheduler *self);
void (*deinit)(struct vblank_scheduler *self);
void (*schedule)(struct vblank_scheduler *self);
bool (*schedule)(struct vblank_scheduler *self);
bool (*handle_x_events)(struct vblank_scheduler *self);
};
@@ -78,13 +77,14 @@ struct sgi_video_sync_vblank_scheduler {
// Since glXWaitVideoSyncSGI blocks, we need to run it in a separate thread.
// ... and all the thread shenanigans that come with it.
_Atomic unsigned int last_msc;
_Atomic uint64_t last_ust;
_Atomic unsigned int current_msc;
_Atomic uint64_t current_ust;
ev_async notify;
pthread_t sync_thread;
bool running, error;
unsigned int last_msc;
/// Protects `running`, `error` and `base.vblank_event_requested`
/// Protects `running`, and `base.vblank_event_requested`
pthread_mutex_t vblank_requested_mtx;
pthread_cond_t vblank_requested_cnd;
};
@@ -96,6 +96,8 @@ struct sgi_video_sync_thread_args {
pthread_cond_t start_cnd;
};
static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
static bool check_sgi_video_sync_extension(Display *dpy, int screen) {
const char *glx_ext = glXQueryExtensionsString(dpy, screen);
const char *needle = "GLX_SGI_video_sync";
@@ -207,8 +209,8 @@ static void *sgi_video_sync_thread(void *data) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
atomic_store(&self->last_msc, last_msc);
atomic_store(&self->last_ust,
atomic_store(&self->current_msc, last_msc);
atomic_store(&self->current_ust,
(uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000));
ev_async_send(self->base.loop, &self->notify);
pthread_mutex_lock(&self->vblank_requested_mtx);
@@ -239,34 +241,30 @@ cleanup:
return NULL;
}
static void sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) {
static bool sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) {
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
log_verbose("Requesting vblank event for msc %d", self->last_msc + 1);
if (self->error) {
return false;
}
log_verbose("Requesting vblank event for msc %d", self->current_msc + 1);
pthread_mutex_lock(&self->vblank_requested_mtx);
assert(!base->vblank_event_requested);
base->vblank_event_requested = true;
pthread_cond_signal(&self->vblank_requested_cnd);
pthread_mutex_unlock(&self->vblank_requested_mtx);
return true;
}
static void
sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) {
auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify);
auto event = (struct vblank_event){
.msc = atomic_load(&sched->last_msc),
.ust = atomic_load(&sched->last_ust),
};
sched->base.vblank_event_requested = false;
log_verbose("Received vblank event for msc %lu", event.msc);
vblank_scheduler_invoke_callbacks(&sched->base, &event);
}
sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents);
static void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
static bool sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
auto args = (struct sgi_video_sync_thread_args){
.self = self,
.start_status = -1,
};
bool succeeded = true;
pthread_mutex_init(&args.start_mtx, NULL);
pthread_cond_init(&args.start_cnd, NULL);
@@ -286,11 +284,15 @@ static void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
if (args.start_status != 0) {
log_fatal("Failed to start sgi_video_sync_thread, error code: %d",
args.start_status);
abort();
succeeded = false;
} else {
log_info("Started sgi_video_sync_thread");
}
self->error = !succeeded;
self->last_msc = 0;
pthread_mutex_destroy(&args.start_mtx);
pthread_cond_destroy(&args.start_cnd);
log_info("Started sgi_video_sync_thread");
return succeeded;
}
static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
@@ -306,15 +308,45 @@ static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
pthread_mutex_destroy(&self->vblank_requested_mtx);
pthread_cond_destroy(&self->vblank_requested_cnd);
}
static void
sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) {
auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify);
auto msc = atomic_load(&sched->current_msc);
if (sched->last_msc == msc) {
// NVIDIA spams us with duplicate vblank events after a suspend/resume
// cycle. Recreating the X connection and GLX context seems to fix this.
// Oh NVIDIA.
log_warn("Duplicate vblank event found with msc %d. Possible NVIDIA bug?", msc);
log_warn("Resetting the vblank scheduler");
sgi_video_sync_scheduler_deinit(&sched->base);
sched->base.vblank_event_requested = false;
if (!sgi_video_sync_scheduler_init(&sched->base)) {
log_error("Failed to reset the vblank scheduler");
} else {
sgi_video_sync_scheduler_schedule(&sched->base);
}
return;
}
auto event = (struct vblank_event){
.msc = msc,
.ust = atomic_load(&sched->current_ust),
};
sched->base.vblank_event_requested = false;
sched->last_msc = msc;
log_verbose("Received vblank event for msc %lu", event.msc);
vblank_scheduler_invoke_callbacks(&sched->base, &event);
}
#endif
static void present_vblank_scheduler_schedule(struct vblank_scheduler *base) {
static bool present_vblank_scheduler_schedule(struct vblank_scheduler *base) {
auto self = (struct present_vblank_scheduler *)base;
log_verbose("Requesting vblank event for window 0x%08x, msc %" PRIu64,
base->target_window, self->last_msc + 1);
assert(!base->vblank_event_requested);
x_request_vblank_event(base->c, base->target_window, self->last_msc + 1);
base->vblank_event_requested = true;
return true;
}
static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unused revents) {
@@ -327,7 +359,7 @@ static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unus
vblank_scheduler_invoke_callbacks(&sched->base, &event);
}
static void present_vblank_scheduler_init(struct vblank_scheduler *base) {
static bool present_vblank_scheduler_init(struct vblank_scheduler *base) {
auto self = (struct present_vblank_scheduler *)base;
base->type = VBLANK_SCHEDULER_PRESENT;
ev_timer_init(&self->callback_timer, present_vblank_callback, 0, 0);
@@ -339,6 +371,7 @@ static void present_vblank_scheduler_init(struct vblank_scheduler *base) {
set_cant_fail_cookie(base->c, select_input);
self->event =
xcb_register_for_special_xge(base->c->c, &xcb_present_id, self->event_id, NULL);
return true;
}
static void present_vblank_scheduler_deinit(struct vblank_scheduler *base) {
@@ -439,17 +472,19 @@ static const struct vblank_scheduler_ops vblank_scheduler_ops[LAST_VBLANK_SCHEDU
#endif
};
static void vblank_scheduler_schedule_internal(struct vblank_scheduler *self) {
static bool vblank_scheduler_schedule_internal(struct vblank_scheduler *self) {
assert(self->type < LAST_VBLANK_SCHEDULER);
auto fn = vblank_scheduler_ops[self->type].schedule;
assert(fn != NULL);
fn(self);
return fn(self);
}
bool vblank_scheduler_schedule(struct vblank_scheduler *self,
vblank_callback_t vblank_callback, void *user_data) {
if (self->callback_count == 0 && self->wind_down == 0) {
vblank_scheduler_schedule_internal(self);
if (!vblank_scheduler_schedule_internal(self)) {
return false;
}
}
if (self->callback_count == self->callback_capacity) {
size_t new_capacity =

View File

@@ -1222,7 +1222,7 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// Delayed update of shadow image
// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to
// re-create or release the shaodw in based on whether w->shadow
// re-create or release the shadow in based on whether w->shadow
// is set.
win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
@@ -1407,16 +1407,16 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
* Determine if a window should have rounded corners.
*/
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
if (ps->o.corner_radius == 0) {
w->corner_radius = 0;
return;
}
void *radius_override = NULL;
if (c2_match(ps, w, ps->o.corner_radius_rules, &radius_override)) {
log_debug("Matched corner rule! %d", w->corner_radius);
}
if (ps->o.corner_radius == 0 && !radius_override) {
w->corner_radius = 0;
return;
}
// Don't round full screen windows & excluded windows,
// unless we find a corner override in corner_radius_rules
if (!radius_override && ((w && win_is_fullscreen(ps, w)) ||
@@ -2259,7 +2259,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
// Add border width because we are using a different origin.
// X thinks the top left of the inner window is the origin
// (for the bounding shape, althought xcb_get_geometry thinks
// (for the bounding shape, although xcb_get_geometry thinks
// the outer top left (outer means outside of the window
// border) is the origin),
// We think the top left of the border is the origin
@@ -2602,7 +2602,7 @@ bool destroy_win_start(session_t *ps, struct win *w) {
HASH_DEL(ps->windows, w);
if (!w->managed || mw->state == WSTATE_UNMAPPED) {
// Window is already unmapped, or is an unmanged window, just
// Window is already unmapped, or is an unmanaged window, just
// destroy it
destroy_win_finish(ps, w);
return true;
@@ -3031,7 +3031,7 @@ static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid
}
/**
* Check if a window is fulscreen using EWMH
* Check if a window is full-screen using EWMH
*
* TODO(yshui) cache this property
*/

View File

@@ -278,13 +278,13 @@ struct managed_win {
switch_t shadow_force;
/// Opacity of the shadow. Affected by window opacity and frame opacity.
double shadow_opacity;
/// X offset of shadow. Affected by commandline argument.
/// X offset of shadow. Affected by command line argument.
int shadow_dx;
/// Y offset of shadow. Affected by commandline argument.
/// Y offset of shadow. Affected by command line argument.
int shadow_dy;
/// Width of shadow. Affected by window size and commandline argument.
/// Width of shadow. Affected by window size and command line argument.
int shadow_width;
/// Height of shadow. Affected by window size and commandline argument.
/// Height of shadow. Affected by window size and command line argument.
int shadow_height;
/// Picture to render shadow. Affected by window size.
paint_t shadow_paint;

View File

@@ -68,7 +68,7 @@ typedef enum {
} winstate_t;
enum win_flags {
// Note: *_NONE flags are mostly redudant and meant for detecting logical errors
// Note: *_NONE flags are mostly redundant and meant for detecting logical errors
// in the code
/// pixmap is out of date, will be update in win_process_flags

36
src/x.c
View File

@@ -321,6 +321,21 @@ xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standa
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
}
xcb_visualid_t x_get_visual_for_depth(struct x_connection *c, uint8_t depth) {
xcb_screen_iterator_t screen_it = xcb_setup_roots_iterator(xcb_get_setup(c->c));
for (; screen_it.rem; xcb_screen_next(&screen_it)) {
xcb_depth_iterator_t depth_it =
xcb_screen_allowed_depths_iterator(screen_it.data);
for (; depth_it.rem; xcb_depth_next(&depth_it)) {
if (depth_it.data->depth == depth) {
return xcb_depth_visuals_iterator(depth_it.data).data->visual_id;
}
}
}
return XCB_NONE;
}
xcb_render_pictformat_t
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
x_get_server_pictfmts(c);
@@ -689,27 +704,6 @@ xcb_pixmap_t x_create_pixmap(struct x_connection *c, uint8_t depth, int width, i
return XCB_NONE;
}
/**
* Validate a pixmap.
*
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
* are better ways.
*/
bool x_validate_pixmap(struct x_connection *c, xcb_pixmap_t pixmap) {
if (pixmap == XCB_NONE) {
return false;
}
auto r = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, pixmap), NULL);
if (!r) {
return false;
}
bool ret = r->width && r->height;
free(r);
return ret;
}
/// We don't use the _XSETROOT_ID root window property as a source of the background
/// pixmap because it most likely points to a dummy pixmap used to keep the colormap
/// associated with the background pixmap alive but we listen for it's changes and update

View File

@@ -356,8 +356,6 @@ const char *x_strerror(xcb_generic_error_t *e);
xcb_pixmap_t x_create_pixmap(struct x_connection *, uint8_t depth, int width, int height);
bool x_validate_pixmap(struct x_connection *, xcb_pixmap_t pxmap);
/**
* Free a <code>winprop_t</code>.
*
@@ -408,6 +406,8 @@ struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t vis
xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standard_t std);
xcb_visualid_t x_get_visual_for_depth(struct x_connection *c, uint8_t depth);
xcb_render_pictformat_t
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std);