summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Grenville <pyxlcy@gmail.com>2013-03-15 23:16:23 +0800
committerRichard Grenville <pyxlcy@gmail.com>2013-03-15 23:16:23 +0800
commitf9f1e1f228ec21be08833f6aa86fe6ea2c64b625 (patch)
treeaab94bd7d31bc3464e4ddcdee9ee792156231738
parent690589bb343f25eec4727748a990b0b60972ed8d (diff)
downloadtdebase-f9f1e1f228ec21be08833f6aa86fe6ea2c64b625.tar.gz
tdebase-f9f1e1f228ec21be08833f6aa86fe6ea2c64b625.zip
Feature: OpenGL backend
- Add experimental OpenGL backend (--opengl). --blur-background is currently not possible with this backend, because I'm still trying to find a proper way to do blur with OpenGL. Flipping backend on-the-fly is really hard, so it isn't supported right now. No configuration file option exists to enable this, because it isn't stable enough. - Add `opengl-swc` VSync method that uses SGI_swap_control to control buffer swap, with OpenGL backend. (#7) - Fix a potential read-from-freed-memory issue in paint_all(). - Correctly reattach GLX context after fork. - Dump error text in error(). Add GLX error code handling. - Code clean-up. - Known issues: Region operations take a lot of time in glx_render(). I'm hesitating about what to do.
-rw-r--r--common.h174
-rw-r--r--compton.c712
-rw-r--r--compton.h137
-rw-r--r--opengl.c521
-rw-r--r--opengl.h46
5 files changed, 1169 insertions, 421 deletions
diff --git a/common.h b/common.h
index 96ca6021c..30c09e3a5 100644
--- a/common.h
+++ b/common.h
@@ -130,6 +130,9 @@
#error libXcomposite version unsupported
#endif
+/// @brief Length of generic buffers.
+#define BUF_LEN 80
+
#define ROUNDED_PERCENT 0.05
#define ROUNDED_PIXELS 10
@@ -152,6 +155,9 @@
#define XRFILTER_GUASSIAN "gaussian"
#define XRFILTER_BINOMIAL "binomial"
+/// @brief Maximum OpenGL FBConfig depth.
+#define OPENGL_MAX_DEPTH 32
+
// Window flags
// Window size is changed
@@ -249,9 +255,18 @@ typedef enum {
VSYNC_DRM,
VSYNC_OPENGL,
VSYNC_OPENGL_OML,
+ VSYNC_OPENGL_SWC,
NUM_VSYNC,
} vsync_t;
+/// @brief Possible backends of compton.
+enum backend {
+ BKEND_XRENDER,
+ BKEND_GLX,
+};
+
+typedef struct _glx_texture glx_texture_t;
+
#ifdef CONFIG_VSYNC_OPENGL
typedef int (*f_WaitVideoSync) (int, int, unsigned *);
typedef int (*f_GetVideoSync) (unsigned *);
@@ -259,16 +274,40 @@ typedef int (*f_GetVideoSync) (unsigned *);
typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc);
typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc);
+typedef int (*f_SwapIntervalSGI) (int interval);
+
typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list);
typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer);
-struct glx_fbconfig {
+/// @brief Wrapper of a GLX FBConfig.
+typedef struct {
GLXFBConfig cfg;
+ GLint texture_fmt;
+ GLint texture_tgts;
+ bool y_inverted;
+} glx_fbconfig_t;
+
+/// @brief Wrapper of a binded GLX texture.
+struct _glx_texture {
+ GLuint texture;
+ GLXPixmap glpixmap;
+ Pixmap pixmap;
+ int width;
+ int height;
+ int depth;
bool y_inverted;
};
#endif
typedef struct {
+ Pixmap pixmap;
+ Picture pict;
+ glx_texture_t *ptex;
+} paint_t;
+
+#define PAINT_INIT { .pixmap = None, .pict = None }
+
+typedef struct {
int size;
double *data;
} conv;
@@ -293,6 +332,8 @@ typedef struct {
/// The display name we used. NULL means we are using the value of the
/// <code>DISPLAY</code> environment variable.
char *display;
+ /// The backend in use.
+ enum backend backend;
/// Whether to try to detect WM windows and mark them as focused.
bool mark_wmwin_focused;
/// Whether to mark override-redirect windows as focused.
@@ -434,8 +475,10 @@ typedef struct {
// Damage root_damage;
/// X Composite overlay window. Used if <code>--paint-on-overlay</code>.
Window overlay;
+ /// Whether the root tile is filled by compton.
+ bool root_tile_fill;
/// Picture of the root window background.
- Picture root_tile;
+ paint_t root_tile_paint;
/// A region of the size of the screen.
XserverRegion screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting
@@ -488,6 +531,10 @@ typedef struct {
/// Pointer to the <code>next</code> member of tail element of the error
/// ignore linked list.
ignore_t **ignore_tail;
+#ifdef CONFIG_VSYNC_OPENGL
+ /// Current GLX Z value.
+ int glx_z;
+#endif
/// Reset program after next paint.
bool reset;
@@ -551,18 +598,18 @@ typedef struct {
f_GetVideoSync glXGetVideoSyncSGI;
/// Pointer to glXWaitVideoSyncSGI function.
f_WaitVideoSync glXWaitVideoSyncSGI;
- /// Pointer to glXGetSyncValuesOML function.
+ /// Pointer to glXGetSyncValuesOML function.
f_GetSyncValuesOML glXGetSyncValuesOML;
/// Pointer to glXWaitForMscOML function.
f_WaitForMscOML glXWaitForMscOML;
+ /// Pointer to glXSwapIntervalSGI function.
+ f_SwapIntervalSGI glXSwapIntervalProc;
/// Pointer to glXBindTexImageEXT function.
- f_BindTexImageEXT glXBindTexImageEXT;
+ f_BindTexImageEXT glXBindTexImageProc;
/// Pointer to glXReleaseTexImageEXT function.
- f_ReleaseTexImageEXT glXReleaseTexImageEXT;
- /// FBConfig for RGB GLX pixmap.
- struct glx_fbconfig *glx_fbconfig_rgb;
- /// FBConfig for RGBA GLX pixmap.
- struct glx_fbconfig *glx_fbconfig_rgba;
+ f_ReleaseTexImageEXT glXReleaseTexImageProc;
+ /// FBConfig-s for GLX pixmap of different depths.
+ glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1];
#endif
// === X extension related ===
@@ -664,16 +711,16 @@ typedef struct _win {
Window id;
/// Window attributes.
XWindowAttributes a;
+ /// Window visual pict format;
+ XRenderPictFormat *pictfmt;
/// Window painting mode.
winmode_t mode;
/// Whether the window has been damaged at least once.
bool damaged;
/// Damage of the window.
Damage damage;
- /// NameWindowPixmap of the window.
- Pixmap pixmap;
- /// Picture of the window.
- Picture picture;
+ /// Paint info of the window.
+ paint_t paint;
/// Bounding shape of the window.
XserverRegion border_size;
/// Region of the whole window, shadow region included.
@@ -750,8 +797,6 @@ typedef struct _win {
/// broken window managers not transferring client window's
/// _NET_WM_OPACITY value
opacity_t opacity_prop_client;
- /// Alpha mask Picture to render window with opacity.
- Picture alpha_pict;
// Fading-related members
/// Do not fade if it's false. Change on window type change.
@@ -763,8 +808,6 @@ typedef struct _win {
// Frame-opacity-related members
/// Current window frame opacity. Affected by window opacity.
double frame_opacity;
- /// Alpha mask Picture to render window frame with opacity.
- Picture frame_alpha_pict;
/// Frame widths. Determined by client window attributes.
unsigned int left_width, right_width, top_width, bottom_width;
@@ -784,9 +827,7 @@ typedef struct _win {
/// Height of shadow. Affected by window size and commandline argument.
int shadow_height;
/// Picture to render shadow. Affected by window size.
- Picture shadow_pict;
- /// Alpha mask Picture to render shadow. Affected by shadow opacity.
- Picture shadow_alpha_pict;
+ paint_t shadow_paint;
/// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
/// none.
long prop_shadow;
@@ -897,6 +938,20 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg,
// === Functions ===
/**
+ * @brief Quit if the passed-in pointer is empty.
+ */
+static inline void
+allocchk_(const char *func_name, void *ptr) {
+ if (!ptr) {
+ printf_err("%s(): Failed to allocate memory.", func_name);
+ exit(1);
+ }
+}
+
+/// @brief Wrapper of allocchk_().
+#define allocchk(ptr) allocchk_(__func__, ptr)
+
+/**
* Return whether a struct timeval value is empty.
*/
static inline bool
@@ -1305,6 +1360,14 @@ get_atom(session_t *ps, const char *atom_name) {
}
/**
+ * Return the painting target window.
+ */
+static inline Window
+get_tgt_window(session_t *ps) {
+ return ps->o.paint_on_overlay ? ps->overlay: ps->root;
+}
+
+/**
* Find a window from window id in window linked list of the session.
*/
static inline win *
@@ -1372,6 +1435,17 @@ copy_region(const session_t *ps, XserverRegion oldregion) {
}
/**
+ * Destroy a <code>XserverRegion</code>.
+ */
+static inline void
+free_region(session_t *ps, XserverRegion *p) {
+ if (*p) {
+ XFixesDestroyRegion(ps->dpy, *p);
+ *p = None;
+ }
+}
+
+/**
* Determine if a window has a specific property.
*
* @param ps current session
@@ -1454,6 +1528,66 @@ force_repaint(session_t *ps);
bool
vsync_init(session_t *ps);
+#ifdef CONFIG_VSYNC_OPENGL
+/** @name GLX
+ */
+///@{
+
+bool
+glx_init(session_t *ps, bool need_render);
+
+void
+glx_destroy(session_t *ps);
+
+void
+glx_on_root_change(session_t *ps);
+
+bool
+glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
+ int width, int height, int depth);
+
+void
+glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
+
+static inline bool
+glx_tex_binded(const glx_texture_t *ptex) {
+ return ptex && ptex->glpixmap && ptex->texture;
+}
+
+bool
+glx_render(session_t *ps, const glx_texture_t *ptex,
+ int x, int y, int dx, int dy, int width, int height, int z,
+ double opacity, bool neg, XserverRegion reg_tgt);
+#endif
+
+static inline void
+free_texture(session_t *ps, glx_texture_t **pptex) {
+#ifdef CONFIG_VSYNC_OPENGL
+ glx_texture_t *ptex = *pptex;
+
+ // Quit if there's nothing
+ if (!ptex)
+ return;
+
+ glx_release_pixmap(ps, ptex);
+
+ // Free texture
+ if (ptex->texture) {
+ glDeleteTextures(1, &ptex->texture);
+ ptex->texture = 0;
+ }
+
+ // Free structure itself
+ free(ptex);
+ *pptex = NULL;
+#endif
+}
+
+///@}
+
+/** @name DBus handling
+ */
+///@{
#ifdef CONFIG_DBUS
/** @name DBus handling
*/
diff --git a/compton.c b/compton.c
index 58a716c50..f8c33d265 100644
--- a/compton.c
+++ b/compton.c
@@ -37,6 +37,7 @@ const char * const VSYNC_STRS[NUM_VSYNC] = {
"drm", // VSYNC_DRM
"opengl", // VSYNC_OPENGL
"opengl-oml", // VSYNC_OPENGL_OML
+ "opengl-swc", // VSYNC_OPENGL_SWC
};
/// Function pointers to init VSync modes.
@@ -44,6 +45,7 @@ static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = {
[VSYNC_DRM ] = vsync_drm_init,
[VSYNC_OPENGL ] = vsync_opengl_init,
[VSYNC_OPENGL_OML ] = vsync_opengl_oml_init,
+ [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init,
};
/// Function pointers to wait for VSync.
@@ -425,8 +427,11 @@ make_shadow(session_t *ps, double opacity,
/**
* Generate shadow <code>Picture</code> for a window.
*/
-static Picture
-shadow_picture(session_t *ps, double opacity, int width, int height) {
+static bool
+win_build_shadow(session_t *ps, win *w, double opacity) {
+ const int width = w->widthb;
+ const int height = w->heightb;
+
XImage *shadow_image = NULL;
Pixmap shadow_pixmap = None, shadow_pixmap_argb = None;
Picture shadow_picture = None, shadow_picture_argb = None;
@@ -461,13 +466,16 @@ shadow_picture(session_t *ps, double opacity, int width, int height) {
shadow_picture_argb, 0, 0, 0, 0, 0, 0,
shadow_image->width, shadow_image->height);
+ w->shadow_paint.pixmap = shadow_pixmap_argb;
+ w->shadow_paint.pict = shadow_picture_argb;
+ bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32);
+
XFreeGC(ps->dpy, gc);
XDestroyImage(shadow_image);
XFreePixmap(ps->dpy, shadow_pixmap);
- XFreePixmap(ps->dpy, shadow_pixmap_argb);
XRenderFreePicture(ps->dpy, shadow_picture);
- return shadow_picture_argb;
+ return success;
shadow_picture_err:
if (shadow_image)
@@ -482,7 +490,8 @@ shadow_picture_err:
XRenderFreePicture(ps->dpy, shadow_picture_argb);
if (gc)
XFreeGC(ps->dpy, gc);
- return None;
+
+ return false;
}
/**
@@ -653,7 +662,7 @@ win_rounded_corners(session_t *ps, win *w) {
*/
static void
win_validate_pixmap(session_t *ps, win *w) {
- if (!w->pixmap)
+ if (!w->paint.pixmap)
return;
// Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
@@ -663,15 +672,13 @@ win_validate_pixmap(session_t *ps, win *w) {
Window rroot = None;
int rx = 0, ry = 0;
unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0;
- invalid = (!XGetGeometry(ps->dpy, w->pixmap, &rroot, &rx, &ry,
+ invalid = (!XGetGeometry(ps->dpy, w->paint.pixmap, &rroot, &rx, &ry,
&rwid, &rhei, &rborder, &rdepth) || !rwid || !rhei);
}
// Destroy pixmap and picture, if invalid
- if (invalid) {
- free_pixmap(ps, &w->pixmap);
- free_picture(ps, &w->picture);
- }
+ if (invalid)
+ free_paint(ps, &w->paint);
}
/**
@@ -794,23 +801,21 @@ recheck_focus(session_t *ps) {
return NULL;
}
-static Picture
-root_tile_f(session_t *ps) {
+static bool
+get_root_tile(session_t *ps) {
/*
if (ps->o.paint_on_overlay) {
return ps->root_picture;
} */
- Picture picture;
- Pixmap pixmap;
- bool fill = false;
- XRenderPictureAttributes pa;
- int p;
+ assert(!ps->root_tile_paint.pixmap);
+ ps->root_tile_fill = false;
- pixmap = None;
+ bool fill = false;
+ Pixmap pixmap = None;
// Get the values of background attributes
- for (p = 0; background_props_str[p]; p++) {
+ for (int p = 0; background_props_str[p]; p++) {
winprop_t prop = wid_get_prop(ps, ps->root,
get_atom(ps, background_props_str[p]),
1L, XA_PIXMAP, 32);
@@ -823,43 +828,52 @@ root_tile_f(session_t *ps) {
free_winprop(&prop);
}
+ // Create a pixmap if there isn't any
if (!pixmap) {
pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, ps->depth);
fill = true;
}
- pa.repeat = True;
- picture = XRenderCreatePicture(
- ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis),
- CPRepeat, &pa);
+ // Create Picture
+ {
+ XRenderPictureAttributes pa = {
+ .repeat = True,
+ };
+ ps->root_tile_paint.pict = XRenderCreatePicture(
+ ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis),
+ CPRepeat, &pa);
+ }
+ // Fill pixmap if needed
if (fill) {
XRenderColor c;
c.red = c.green = c.blue = 0x8080;
c.alpha = 0xffff;
- XRenderFillRectangle(
- ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1);
-
- free_pixmap(ps, &pixmap);
+ XRenderFillRectangle(ps->dpy, PictOpSrc, ps->root_tile_paint.pict, &c, 0, 0, 1, 1);
}
- return picture;
+ ps->root_tile_fill = fill;
+ ps->root_tile_paint.pixmap = pixmap;
+#ifdef CONFIG_VSYNC_OPENGL
+ if (BKEND_GLX == ps->o.backend)
+ return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap,
+ ps->root_width, ps->root_height, ps->depth);
+#endif
+
+ return true;
}
/**
* Paint root window content.
*/
static void
-paint_root(session_t *ps, Picture tgt_buffer) {
- if (!ps->root_tile) {
- ps->root_tile = root_tile_f(ps);
- }
+paint_root(session_t *ps, XserverRegion reg_paint) {
+ if (!ps->root_tile_paint.pixmap)
+ get_root_tile(ps);
- XRenderComposite(
- ps->dpy, PictOpSrc, ps->root_tile, None,
- tgt_buffer, 0, 0, 0, 0, 0, 0,
- ps->root_width, ps->root_height);
+ win_render(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
+ ps->root_tile_paint.pict);
}
/**
@@ -1106,12 +1120,12 @@ paint_preprocess(session_t *ps, win *list) {
run_fade(ps, w, steps);
// Give up if it's not damaged or invisible, or it's unmapped and its
- // picture is gone (for example due to a ConfigureNotify)
+ // pixmap is gone (for example due to a ConfigureNotify)
if (!w->damaged
|| w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1
|| w->a.x >= ps->root_width || w->a.y >= ps->root_height
|| ((IsUnmapped == w->a.map_state || w->destroyed)
- && !w->picture)) {
+ && !w->paint.pixmap)) {
to_paint = false;
}
@@ -1122,12 +1136,8 @@ paint_preprocess(session_t *ps, win *list) {
add_damage_win(ps, w);
}
- w->alpha_pict = get_alpha_pict_o(ps, w->opacity);
-
- // End the game if we are using the 0 opacity alpha_pict
- if (w->alpha_pict == ps->alpha_picts[0]) {
+ if (get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0])
to_paint = false;
- }
}
if (to_paint) {
@@ -1161,23 +1171,19 @@ paint_preprocess(session_t *ps, win *list) {
ps->reg_ignore_expire = true;
}
- w->frame_alpha_pict = get_alpha_pict_d(ps, w->frame_opacity);
-
// Calculate shadow opacity
if (w->frame_opacity)
w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity;
else
w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w);
- // Rebuild shadow_pict if necessary
- if (w->flags & WFLAG_SIZE_CHANGE)
- free_picture(ps, &w->shadow_pict);
-
- if (w->shadow && !w->shadow_pict) {
- w->shadow_pict = shadow_picture(ps, 1, w->widthb, w->heightb);
+ // Rebuild shadow if necessary
+ if (w->flags & WFLAG_SIZE_CHANGE) {
+ free_paint(ps, &w->shadow_paint);
}
- w->shadow_alpha_pict = get_alpha_pict_d(ps, w->shadow_opacity);
+ if (w->shadow && !paint_isvalid(ps, &w->shadow_paint))
+ win_build_shadow(ps, w, 1);
// Dimming preprocessing
if (w->dim) {
@@ -1272,22 +1278,29 @@ paint_preprocess(session_t *ps, win *list) {
// Fetch pictures only if windows are redirected
if (ps->redirected) {
for (w = t; w; w = w->prev_trans) {
- // Fetch the picture and pixmap if needed
- if (!w->picture) {
- XRenderPictureAttributes pa;
- XRenderPictFormat *format;
- Drawable draw = w->id;
-
- if (ps->has_name_pixmap && !w->pixmap) {
+ // Fetch Pixmap
+ if (!w->paint.pixmap && ps->has_name_pixmap) {
set_ignore_next(ps);
- w->pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id);
+ w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id);
+ }
+ // XRender: Build picture
+ if (BKEND_XRENDER == ps->o.backend && !w->paint.pict) {
+ Drawable draw = w->paint.pixmap;
+ if (!draw)
+ draw = w->id;
+ {
+ XRenderPictureAttributes pa = {
+ .subwindow_mode = IncludeInferiors,
+ };
+
+ w->paint.pict = XRenderCreatePicture(ps->dpy, draw, w->pictfmt,
+ CPSubwindowMode, &pa);
}
- if (w->pixmap) draw = w->pixmap;
-
- format = XRenderFindVisualFormat(ps->dpy, w->a.visual);
- pa.subwindow_mode = IncludeInferiors;
- w->picture = XRenderCreatePicture(
- ps->dpy, draw, format, CPSubwindowMode, &pa);
+ }
+ // OpenGL: Build texture
+ if (!paint_bind_tex(ps, &w->paint, w->widthb, w->heightb,
+ w->pictfmt->depth)) {
+ printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id);
}
}
}
@@ -1299,41 +1312,36 @@ paint_preprocess(session_t *ps, win *list) {
* Paint the shadow of a window.
*/
static inline void
-win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) {
- XRenderComposite(
- ps->dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict,
- tgt_buffer, 0, 0, 0, 0,
- w->a.x + w->shadow_dx, w->a.y + w->shadow_dy,
- w->shadow_width, w->shadow_height);
+win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint) {
+ if (!paint_isvalid(ps, &w->shadow_paint)) {
+ printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id);
+ return;
+ }
+
+ render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy,
+ w->shadow_width, w->shadow_height, w->shadow_opacity, true, false,
+ w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint);
}
/**
* Create an alternative picture for a window.
*/
static inline Picture
-win_build_picture(session_t *ps, win *w, Visual *visual) {
+win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) {
const int wid = w->widthb;
const int hei = w->heightb;
- int depth = 0;
- XRenderPictFormat *pictformat = NULL;
- if (visual && ps->vis != visual) {
- pictformat = XRenderFindVisualFormat(ps->dpy, visual);
- depth = pictformat->depth;
- }
- else {
- pictformat = XRenderFindVisualFormat(ps->dpy, ps->vis);
- }
+ if (!pictfmt)
+ pictfmt = XRenderFindVisualFormat(ps->dpy, ps->vis);
- if (!depth)
- depth = ps->depth;
+ int depth = pictfmt->depth;
Pixmap tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, depth);
if (!tmp_pixmap)
return None;
Picture tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap,
- pictformat, 0, 0);
+ pictfmt, 0, 0);
free_pixmap(ps, &tmp_pixmap);
return tmp_picture;
@@ -1401,23 +1409,53 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
free_picture(ps, &tmp_picture);
}
+static void
+render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
+ double opacity, bool argb, bool neg,
+ Picture pict, glx_texture_t *ptex, XserverRegion reg_paint) {
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ {
+ Picture alpha_pict = get_alpha_pict_d(ps, opacity);
+ if (alpha_pict != ps->alpha_picts[0]) {
+ int op = ((!argb && !alpha_pict) ? PictOpSrc: PictOpOver);
+ XRenderComposite(ps->dpy, op, pict, alpha_pict,
+ ps->tgt_buffer, x, y, 0, 0, dx, dy, wid, hei);
+ }
+ break;
+ }
+#ifdef CONFIG_VSYNC_OPENGL
+ case BKEND_GLX:
+ glx_render(ps, ptex, x, y, dx, dy, wid, hei,
+ ps->glx_z, opacity, neg, reg_paint);
+ ps->glx_z += 1;
+ break;
+#endif
+ default:
+ assert(0);
+ }
+}
+
/**
* Paint a window itself and dim it if asked.
*/
static inline void
-win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) {
- int x = w->a.x;
- int y = w->a.y;
- int wid = w->widthb;
- int hei = w->heightb;
+win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) {
+ if (!paint_isvalid(ps, &w->paint)) {
+ printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id);
+ return;
+ }
+
+ const int x = w->a.x;
+ const int y = w->a.y;
+ const int wid = w->widthb;
+ const int hei = w->heightb;
- Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict);
- int op = (w->mode == WMODE_SOLID ? PictOpSrc: PictOpOver);
- Picture pict = w->picture;
+ Picture pict = w->paint.pict;
// Invert window color, if required
- if (w->invert_color) {
- Picture newpict = win_build_picture(ps, w, w->a.visual);
+ if (BKEND_XRENDER == ps->o.backend && w->invert_color) {
+ Picture newpict = win_build_picture(ps, w, w->pictfmt);
if (newpict) {
// Apply clipping region to save some CPU
if (reg_paint) {
@@ -1440,9 +1478,10 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
}
}
+ double dopacity = get_opacity_percent(w);;
+
if (!w->frame_opacity) {
- XRenderComposite(ps->dpy, op, pict, alpha_mask,
- tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
+ win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict);
}
else {
// Painting parameters
@@ -1452,8 +1491,8 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
const int r = w->a.border_width + w->right_width;
#define COMP_BDR(cx, cy, cwid, chei) \
- XRenderComposite(ps->dpy, PictOpOver, pict, w->frame_alpha_pict, \
- tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei))
+ win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \
+ reg_paint, pict)
// The following complicated logic is required because some broken
// window managers (I'm talking about you, Openbox!) that makes
@@ -1488,8 +1527,7 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
pwid = wid - l - pwid;
if (pwid > 0) {
// body
- XRenderComposite(ps->dpy, op, pict, alpha_mask,
- tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei);
+ win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pict);
}
}
}
@@ -1498,13 +1536,13 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
#undef COMP_BDR
- if (pict != w->picture)
+ if (pict != w->paint.pict)
free_picture(ps, &pict);
// Dimming the window if needed
if (w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) {
XRenderComposite(ps->dpy, PictOpOver, ps->black_picture,
- w->dim_alpha_pict, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
+ w->dim_alpha_pict, ps->tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
}
}
@@ -1523,10 +1561,19 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
#ifdef DEBUG_REPAINT
static struct timespec last_paint = { 0 };
#endif
-
- win *w;
+ win *w = NULL;
XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None;
+#ifdef CONFIG_VSYNC_OPENGL
+ // GLX backend: OpenGL doesn't support partial repaint without
+ // GLX_MESA_copy_sub_buffer
+ if (BKEND_GLX == ps->o.backend) {
+ free_region(ps, &region);
+ // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ ps->glx_z = 0.0;
+ }
+#endif
+
if (!region) {
region = get_screen_region(ps);
}
@@ -1583,7 +1630,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint);
- paint_root(ps, ps->tgt_buffer);
+ paint_root(ps, reg_paint);
// Create temporary regions for use during painting
if (!reg_tmp)
@@ -1622,7 +1669,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0,
reg_paint);
- win_paint_shadow(ps, w, ps->tgt_buffer);
+ win_paint_shadow(ps, w, reg_paint);
}
}
@@ -1655,10 +1702,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
}
// Painting the window
- win_paint_win(ps, w, ps->tgt_buffer, reg_paint);
+ win_paint_win(ps, w, reg_paint);
}
-
- check_fade_fin(ps, w);
}
// Free up all temporary regions
@@ -1688,21 +1733,32 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
if (!ps->o.vsync_aggressive)
vsync_wait(ps);
- // DBE painting mode, only need to swap the buffer
- if (ps->o.dbe) {
- XdbeSwapInfo swap_info = {
- .swap_window = get_tgt_window(ps),
- // Is it safe to use XdbeUndefined?
- .swap_action = XdbeCopied
- };
- XdbeSwapBuffers(ps->dpy, &swap_info, 1);
- }
- // No-DBE painting mode
- else if (ps->tgt_buffer != ps->tgt_picture) {
- XRenderComposite(
- ps->dpy, PictOpSrc, ps->tgt_buffer, None,
- ps->tgt_picture, 0, 0, 0, 0,
- 0, 0, ps->root_width, ps->root_height);
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ // DBE painting mode, only need to swap the buffer
+ if (ps->o.dbe) {
+ XdbeSwapInfo swap_info = {
+ .swap_window = get_tgt_window(ps),
+ // Is it safe to use XdbeUndefined?
+ .swap_action = XdbeCopied
+ };
+ XdbeSwapBuffers(ps->dpy, &swap_info, 1);
+ }
+ // No-DBE painting mode
+ else if (ps->tgt_buffer != ps->tgt_picture) {
+ XRenderComposite(
+ ps->dpy, PictOpSrc, ps->tgt_buffer, None,
+ ps->tgt_picture, 0, 0, 0, 0,
+ 0, 0, ps->root_width, ps->root_height);
+ }
+ break;
+#ifdef CONFIG_VSYNC_OPENGL
+ case BKEND_GLX:
+ glXSwapBuffers(ps->dpy, get_tgt_window(ps));
+ break;
+#endif
+ default:
+ assert(0);
}
if (ps->o.vsync_aggressive)
@@ -1730,6 +1786,15 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
putchar('\n');
fflush(stdout);
#endif
+
+ // Check if fading is finished on all painted windows
+ {
+ win *pprev = NULL;
+ for (w = t; w; w = pprev) {
+ pprev = w->prev_trans;
+ check_fade_fin(ps, w);
+ }
+ }
}
static void
@@ -1822,7 +1887,7 @@ map_win(session_t *ps, Window id) {
// Make sure the XSelectInput() requests are sent
XSync(ps->dpy, False);
- // Update window mode here to do matching of ARGB
+ // Update window mode here to check for ARGB windows
win_determine_mode(ps, w);
// Detect client window here instead of in add_win() as the client
@@ -1917,10 +1982,9 @@ finish_unmap_win(session_t *ps, win *w) {
w->extents = None;
}
- free_pixmap(ps, &w->pixmap);
- free_picture(ps, &w->picture);
+ free_paint(ps, &w->paint);
free_region(ps, &w->border_size);
- free_picture(ps, &w->shadow_pict);
+ free_paint(ps, &w->shadow_paint);
}
static void
@@ -1983,16 +2047,9 @@ get_opacity_percent(win *w) {
static void
win_determine_mode(session_t *ps, win *w) {
winmode_t mode = WMODE_SOLID;
- XRenderPictFormat *format;
-
- if (w->a.class == InputOnly) {
- format = 0;
- } else {
- format = XRenderFindVisualFormat(ps->dpy, w->a.visual);
- }
- if (format && format->type == PictTypeDirect
- && format->direct.alphaMask) {
+ if (w->pictfmt && w->pictfmt->type == PictTypeDirect
+ && w->pictfmt->direct.alphaMask) {
mode = WMODE_ARGB;
} else if (w->opacity != OPAQUE) {
mode = WMODE_TRANS;
@@ -2107,7 +2164,7 @@ win_update_shape(session_t *ps, win *w) {
// If clear_shadow state on the window possibly changed, destroy the old
// shadow_pict
if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old)
- free_picture(ps, &w->shadow_pict);
+ free_paint(ps, &w->shadow_paint);
*/
}
}
@@ -2404,11 +2461,11 @@ add_win(session_t *ps, Window id, Window prev) {
.id = None,
.a = { },
+ .pictfmt = NULL,
.mode = WMODE_TRANS,
.damaged = false,
.damage = None,
- .pixmap = None,
- .picture = None,
+ .paint = PAINT_INIT,
.border_size = None,
.extents = None,
.flags = 0,
@@ -2446,13 +2503,11 @@ add_win(session_t *ps, Window id, Window prev) {
.opacity_tgt = 0,
.opacity_prop = OPAQUE,
.opacity_prop_client = OPAQUE,
- .alpha_pict = None,
.fade = false,
.fade_callback = NULL,
.frame_opacity = 0.0,
- .frame_alpha_pict = None,
.left_width = 0,
.right_width = 0,
.top_width = 0,
@@ -2465,8 +2520,7 @@ add_win(session_t *ps, Window id, Window prev) {
.shadow_dy = 0,
.shadow_width = 0,
.shadow_height = 0,
- .shadow_pict = None,
- .shadow_alpha_pict = None,
+ .shadow_paint = PAINT_INIT,
.prop_shadow = -1,
.dim = false,
@@ -2520,6 +2574,10 @@ add_win(session_t *ps, Window id, Window prev) {
assert(IsViewable == map_state || IsUnmapped == map_state);
new->a.map_state = IsUnmapped;
+ // Get window picture format
+ if (InputOutput == new->a.class)
+ new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual);
+
// Create Damage for window
if (InputOutput == new->a.class) {
set_ignore_next(ps);
@@ -2608,6 +2666,7 @@ restack_win(session_t *ps, win *w, Window new_above) {
static void
configure_win(session_t *ps, XConfigureEvent *ce) {
+ // On root window changes
if (ce->window == ps->root) {
if (ps->tgt_buffer) {
XRenderFreePicture(ps->dpy, ps->tgt_buffer);
@@ -2618,9 +2677,15 @@ configure_win(session_t *ps, XConfigureEvent *ce) {
rebuild_screen_reg(ps);
+#ifdef CONFIG_VSYNC_OPENGL
+ if (BKEND_GLX == ps->o.backend)
+ glx_on_root_change(ps);
+#endif
+
return;
}
+ // Other window changes
win *w = find_win(ps, ce->window);
XserverRegion damage = None;
@@ -2660,10 +2725,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) {
w->a.y = ce->y;
if (w->a.width != ce->width || w->a.height != ce->height
- || w->a.border_width != ce->border_width) {
- free_pixmap(ps, &w->pixmap);
- free_picture(ps, &w->picture);
- }
+ || w->a.border_width != ce->border_width)
+ free_paint(ps, &w->paint);
if (w->a.width != ce->width || w->a.height != ce->height
|| w->a.border_width != ce->border_width) {
@@ -2756,11 +2819,10 @@ destroy_win(session_t *ps, Window id) {
static inline void
root_damaged(session_t *ps) {
- if (ps->root_tile) {
+ if (ps->root_tile_paint.pixmap) {
XClearArea(ps->dpy, ps->root, 0, 0, 0, 0, true);
// if (ps->root_picture != ps->root_tile) {
- XRenderFreePicture(ps->dpy, ps->root_tile);
- ps->root_tile = None;
+ free_root_tile(ps);
/* }
if (root_damage) {
XserverRegion parts = XFixesCreateRegion(ps->dpy, 0, 0);
@@ -2829,6 +2891,21 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) {
CASESTRRET2(BadGlyph);
}
+#ifdef CONFIG_VSYNC_OPENGL
+ if (ps->glx_exists) {
+ o = ev->error_code - ps->glx_error;
+ switch (o) {
+ CASESTRRET2(GLX_BAD_SCREEN);
+ CASESTRRET2(GLX_BAD_ATTRIBUTE);
+ CASESTRRET2(GLX_NO_EXTENSION);
+ CASESTRRET2(GLX_BAD_VISUAL);
+ CASESTRRET2(GLX_BAD_CONTEXT);
+ CASESTRRET2(GLX_BAD_VALUE);
+ CASESTRRET2(GLX_BAD_ENUM);
+ }
+ }
+#endif
+
switch (ev->error_code) {
CASESTRRET2(BadAccess);
CASESTRRET2(BadAlloc);
@@ -2852,9 +2929,13 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) {
#undef CASESTRRET2
print_timestamp(ps);
- printf("error %d (%s) request %d minor %d serial %lu\n",
- ev->error_code, name, ev->request_code,
- ev->minor_code, ev->serial);
+ {
+ char buf[BUF_LEN] = "";
+ XGetErrorText(ps->dpy, ev->error_code, buf, BUF_LEN);
+ printf("error %d (%s) request %d minor %d serial %lu (\"%s\")\n",
+ ev->error_code, name, ev->request_code,
+ ev->minor_code, ev->serial, buf);
+ }
return 0;
}
@@ -3942,8 +4023,7 @@ usage(void) {
" Specify refresh rate of the screen. If not specified or 0, compton\n"
" will try detecting this with X RandR extension.\n"
"--vsync vsync-method\n"
- " Set VSync method. There are up to 3 VSync methods currently available\n"
- " depending on your compile time settings:\n"
+ " Set VSync method. There are up to 4 VSync methods currently available.\n"
" none = No VSync\n"
#undef WARNING
#ifndef CONFIG_VSYNC_DRM
@@ -3952,7 +4032,7 @@ usage(void) {
#define WARNING
#endif
" drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n"
- " drivers. Experimental." WARNING "\n"
+ " drivers." WARNING "\n"
#undef WARNING
#ifndef CONFIG_VSYNC_OPENGL
#define WARNING WARNING_DISABLED
@@ -3960,9 +4040,14 @@ usage(void) {
#define WARNING
#endif
" opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n"
- " work on some drivers. Experimental." WARNING"\n"
+ " work on some drivers." WARNING"\n"
" opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n"
" Only work on some drivers. Experimental." WARNING"\n"
+ " opengl-swc = Try to VSync with SGI_swap_control OpenGL extension.\n"
+ " Only work on some drivers. Works only with OpenGL backend.\n"
+ " Does not actually control paint timing, only buffer swap is\n"
+ " affected, so it doesn't have the effect of --sw-opti unlike\n"
+ " other methods. Experimental." WARNING "\n"
"--alpha-step val\n"
" Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n"
" 0.03.\n"
@@ -4013,6 +4098,8 @@ usage(void) {
"--invert-color-include condition\n"
" Specify a list of conditions of windows that should be painted with\n"
" inverted color. Resource-hogging, and is not well tested.\n"
+ "--opengl\n"
+ " Enable the highly experimental OpenGL backend." WARNING "\n"
#undef WARNING
#ifndef CONFIG_DBUS
#define WARNING WARNING_DISABLED
@@ -4096,6 +4183,14 @@ fork_after(session_t *ps) {
if (getppid() == 1)
return true;
+#ifdef CONFIG_VSYNC_OPENGL
+ // GLX context must be released and reattached on fork
+ if (ps->glx_context && !glXMakeCurrent(ps->dpy, None, NULL)) {
+ printf_errf("(): Failed to detach GLx context.");
+ return false;
+ }
+#endif
+
int pid = fork();
if (-1 == pid) {
@@ -4107,6 +4202,14 @@ fork_after(session_t *ps) {
setsid();
+#ifdef CONFIG_VSYNC_OPENGL
+ if (ps->glx_context
+ && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
+ printf_errf("(): Failed to make GLX context current.");
+ return false;
+ }
+#endif
+
// Mainly to suppress the _FORTIFY_SOURCE warning
bool success = freopen("/dev/null", "r", stdin);
if (!success) {
@@ -4448,6 +4551,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
{ "dbus", no_argument, NULL, 286 },
{ "logpath", required_argument, NULL, 287 },
{ "invert-color-include", required_argument, NULL, 288 },
+ { "opengl", no_argument, NULL, 289 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@@ -4701,6 +4805,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
// --invert-color-include
condlst_add(ps, &ps->o.invert_color_list, optarg);
break;
+ case 289:
+ // --opengl
+ ps->o.backend = BKEND_GLX;
+ break;
default:
usage();
break;
@@ -4889,155 +4997,6 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv) {
}
}
-#ifdef CONFIG_VSYNC_OPENGL
-/**
- * Get a GLX FBConfig.
- */
-static inline bool
-opengl_update_fbconfig(session_t *ps, bool alpha, struct glx_fbconfig **ppcfg) {
- const int FBCONFIG_ATTRS[] = {
- (alpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT: GLX_BIND_TO_TEXTURE_RGB_EXT), True,
- GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
- GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
- GLX_DOUBLEBUFFER, True,
- GLX_Y_INVERTED_EXT, GLX_DONT_CARE,
- None
- };
-
- if (*ppcfg) {
- free(*ppcfg);
- *ppcfg = NULL;
- }
-
- int count = 0;
- GLXFBConfig *cfgs = glXChooseFBConfig(ps->dpy, ps->scr, FBCONFIG_ATTRS, &count);
- if (!count || !cfgs) {
- if (cfgs)
- XFree(cfgs);
- return false;
- };
-
- *ppcfg = malloc(sizeof(struct glx_fbconfig));
- (*ppcfg)->cfg = cfgs[0];
-
- {
- int inverted = 0;
- glXGetFBConfigAttrib(ps->dpy, (*ppcfg)->cfg, GLX_Y_INVERTED_EXT, &inverted);
- (*ppcfg)->y_inverted = inverted;
- }
-
- XFree(cfgs);
-
- return true;
-}
-
-/**
- * Initialize OpenGL.
- */
-static bool
-opengl_init(session_t *ps, bool need_render) {
- // Check for GLX extension
- if (!ps->glx_exists) {
- if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
- ps->glx_exists = true;
- else {
- printf_errf("(): No GLX extension.");
- goto opengl_init_err;
- }
- }
-
- // Ensure GLX_EXT_texture_from_pixmap exists
- if (need_render && !opengl_hasext(ps, "GLX_EXT_texture_from_pixmap")) {
- goto opengl_init_err;
- }
-
- {
- // Get XVisualInfo
- XVisualInfo *pvis = NULL;
- {
- XVisualInfo vreq = { .visualid = XVisualIDFromVisual(ps->vis) };
- int nitems = 0;
- pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
- }
-
- if (!pvis) {
- printf_errf("(): Failed to acquire XVisualInfo for current visual.");
- goto opengl_init_err;
- }
-
- // Ensure the visual is double-buffered
- if (need_render) {
- int value = 0;
- glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value);
- if (!value) {
- XFree(pvis);
- printf_errf("(): Default GLX visual is not double-buffered.");
- goto opengl_init_err;
- }
- }
-
- // Get GLX context
- ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
- XFree(pvis);
- }
-
- if (!ps->glx_context) {
- printf_errf("(): Failed to get GLX context.");
- goto opengl_init_err;
- }
-
- // Attach GLX context
- if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
- printf_errf("(): Failed to attach GLX context.");
- goto opengl_init_err;
- }
-
- // Acquire function addresses
- if (need_render) {
- ps->glXBindTexImageEXT = (f_BindTexImageEXT)
- glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT");
- ps->glXReleaseTexImageEXT = (f_ReleaseTexImageEXT)
- glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
- if (!ps->glXBindTexImageEXT || !ps->glXReleaseTexImageEXT) {
- printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
- goto opengl_init_err;
- }
- }
-
- // Acquire FBConfigs
- if (need_render
- && (!(ps->glx_fbconfig_rgb || opengl_update_fbconfig(ps, false, &ps->glx_fbconfig_rgb))
- || !(ps->glx_fbconfig_rgba || opengl_update_fbconfig(ps, true, &ps->glx_fbconfig_rgba)))) {
- printf_errf("(): Failed to acquire GLX fbconfig.");
- goto opengl_init_err;
- }
-
- if (need_render) {
- glViewport(0, 0, ps->root_width, ps->root_height);
- }
-
- return true;
-
-opengl_init_err:
- opengl_destroy(ps);
-
- return false;
-}
-
-static void
-opengl_destroy(session_t *ps) {
- if (ps->glx_context) {
- glXDestroyContext(ps->dpy, ps->glx_context);
- ps->glx_context = None;
- }
-
- free(ps->glx_fbconfig_rgb);
- ps->glx_fbconfig_rgb = NULL;
- free(ps->glx_fbconfig_rgba);
- ps->glx_fbconfig_rgba = NULL;
-}
-#endif
-
/**
* Initialize DRM VSync.
*
@@ -5148,6 +5107,34 @@ vsync_opengl_oml_init(session_t *ps) {
#endif
}
+static bool
+vsync_opengl_swc_init(session_t *ps) {
+#ifdef CONFIG_VSYNC_OPENGL
+ if (!ensure_glx_context(ps))
+ return false;
+
+ if (BKEND_GLX != ps->o.backend) {
+ printf_errf("(): I'm afraid glXSwapIntervalSGI wouldn't help if you are "
+ "not using OpenGL backend. You could try, nonetheless.");
+ }
+
+ // Get video sync functions
+ if (!ps->glXSwapIntervalProc)
+ ps->glXSwapIntervalProc = (f_SwapIntervalSGI)
+ glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI");
+ if (!ps->glXSwapIntervalProc) {
+ printf_errf("(): Failed to get SGI_swap_control function.");
+ return false;
+ }
+ ps->glXSwapIntervalProc(1);
+
+ return true;
+#else
+ printf_errf("(): Program not compiled with OpenGL VSync support.");
+ return false;
+#endif
+}
+
#ifdef CONFIG_VSYNC_OPENGL
/**
* Wait for next VSync, OpenGL method.
@@ -5202,8 +5189,6 @@ vsync_wait(session_t *ps) {
if (!ps->o.vsync)
return;
- assert(VSYNC_FUNCS_WAIT[ps->o.vsync]);
-
if (VSYNC_FUNCS_WAIT[ps->o.vsync])
VSYNC_FUNCS_WAIT[ps->o.vsync](ps);
}
@@ -5271,29 +5256,42 @@ init_overlay(session_t *ps) {
}
/**
- * Query needed X Render filters to check for their existence.
+ * Query needed X Render / OpenGL filters to check for their existence.
*/
-static void
+static bool
init_filters(session_t *ps) {
+ // Blur filter
if (ps->o.blur_background || ps->o.blur_background_frame) {
- // Query filters
- XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps));
- if (pf) {
- for (int i = 0; i < pf->nfilter; ++i) {
- // Convolution filter
- if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION))
- ps->xrfilter_convolution_exists = true;
- }
- }
- XFree(pf);
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ {
+ // Query filters
+ XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps));
+ if (pf) {
+ for (int i = 0; i < pf->nfilter; ++i) {
+ // Convolution filter
+ if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION))
+ ps->xrfilter_convolution_exists = true;
+ }
+ }
+ XFree(pf);
- // Turn features off if any required filter is not present
- if (!ps->xrfilter_convolution_exists) {
- fprintf(stderr, "X Render convolution filter unsupported by your X server. Background blur disabled.\n");
- ps->o.blur_background = false;
- ps->o.blur_background_frame = false;
+ // Turn features off if any required filter is not present
+ if (!ps->xrfilter_convolution_exists) {
+ printf_errf("(): X Render convolution filter unsupported by your X server. Background blur is not possible.");
+ return false;
+ }
+ break;
+ }
+ case BKEND_GLX:
+ {
+ printf_errf("(): OpenGL blur is not implemented yet.");
+ return false;
+ }
}
}
+
+ return true;
}
/**
@@ -5477,10 +5475,8 @@ redir_stop(session_t *ps) {
// Destroy all Pictures as they expire once windows are unredirected
// If we don't destroy them here, looks like the resources are just
// kept inaccessible somehow
- for (win *w = ps->list; w; w = w->next) {
- free_pixmap(ps, &w->pixmap);
- free_picture(ps, &w->picture);
- }
+ for (win *w = ps->list; w; w = w->next)
+ free_paint(ps, &w->paint);
XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual);
// Unmap overlay window
@@ -5597,14 +5593,17 @@ session_init(session_t *ps_old, int argc, char **argv) {
.root_width = 0,
// .root_damage = None,
.overlay = None,
- .root_tile = None,
+ .root_tile_fill = false,
+ .root_tile_paint = PAINT_INIT,
.screen_reg = None,
.tgt_picture = None,
.tgt_buffer = None,
.root_dbe = None,
.reg_win = None,
.o = {
+ .config_file = NULL,
.display = NULL,
+ .backend = BKEND_XRENDER,
.mark_wmwin_focused = false,
.mark_ovredir_focused = false,
.fork_after_register = false,
@@ -5863,14 +5862,35 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->o.dbe = false;
}
+ ps->root_width = DisplayWidth(ps->dpy, ps->scr);
+ ps->root_height = DisplayHeight(ps->dpy, ps->scr);
+
+ rebuild_screen_reg(ps);
+
// Overlay must be initialized before double buffer, and before creation
// of OpenGL context.
if (ps->o.paint_on_overlay)
init_overlay(ps);
+ // Initialize DBE
+ if (ps->o.dbe && BKEND_GLX == ps->o.backend) {
+ printf_errf("(): DBE couldn't be used on OpenGL backend.");
+ ps->o.dbe = false;
+ }
+
if (ps->o.dbe && !init_dbe(ps))
exit(1);
+ // Initialize OpenGL as early as possible
+ if (BKEND_GLX == ps->o.backend) {
+#ifdef CONFIG_VSYNC_OPENGL
+ if (!glx_init(ps, true))
+ exit(1);
+#else
+ printf_errfq(1, "(): OpenGL backend support not compiled in.");
+#endif
+ }
+
// Initialize software optimization
if (ps->o.sw_opti)
ps->o.sw_opti = swopti_init(ps);
@@ -5889,11 +5909,6 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->gaussian_map = make_gaussian_map(ps->o.shadow_radius);
presum_gaussian(ps, ps->gaussian_map);
- ps->root_width = DisplayWidth(ps->dpy, ps->scr);
- ps->root_height = DisplayHeight(ps->dpy, ps->scr);
-
- rebuild_screen_reg(ps);
-
{
XRenderPictureAttributes pa;
pa.subwindow_mode = IncludeInferiors;
@@ -5911,7 +5926,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
}
}
- init_filters(ps);
+ if (!init_filters(ps))
+ exit(1);
ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0);
ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1);
@@ -6092,7 +6108,7 @@ session_destroy(session_t *ps) {
free_picture(ps, &ps->root_picture);
// Free other X resources
- free_picture(ps, &ps->root_tile);
+ free_root_tile(ps);
free_region(ps, &ps->screen_reg);
free_region(ps, &ps->all_damage);
free(ps->expose_rects);
@@ -6106,14 +6122,8 @@ session_destroy(session_t *ps) {
free(ps->pfds_write);
free(ps->pfds_except);
- // Free reg_win and glx_context
- if (ps->reg_win) {
- XDestroyWindow(ps->dpy, ps->reg_win);
- ps->reg_win = None;
- }
-
#ifdef CONFIG_VSYNC_OPENGL
- opengl_destroy(ps);
+ glx_destroy(ps);
#endif
// Free double buffer
@@ -6136,6 +6146,12 @@ session_destroy(session_t *ps) {
ps->overlay = None;
}
+ // Free reg_win
+ if (ps->reg_win) {
+ XDestroyWindow(ps->dpy, ps->reg_win);
+ ps->reg_win = None;
+ }
+
// Flush all events
XSync(ps->dpy, True);
@@ -6236,7 +6252,7 @@ main(int argc, char **argv) {
while (1) {
ps_g = session_init(ps_old, argc, argv);
if (!ps_g) {
- printf_errf("Failed to create new session.");
+ printf_errf("(): Failed to create new session.");
return 1;
}
session_run(ps_g);
diff --git a/compton.h b/compton.h
index f0af56482..0479b3d0a 100644
--- a/compton.h
+++ b/compton.h
@@ -9,7 +9,6 @@
#include "common.h"
-#include <ctype.h>
#include <math.h>
#include <sys/select.h>
#include <limits.h>
@@ -53,14 +52,6 @@ static int
should_ignore(session_t *ps, unsigned long sequence);
/**
- * Return the painting target window.
- */
-static inline Window
-get_tgt_window(session_t *ps) {
- return ps->o.paint_on_overlay ? ps->overlay: ps->root;
-}
-
-/**
* Reset filter on a <code>Picture</code>.
*/
static inline void
@@ -119,16 +110,6 @@ array_wid_exists(const Window *arr, int count, Window wid) {
return false;
}
-/**
- * Destroy a <code>XserverRegion</code>.
- */
-inline static void
-free_region(session_t *ps, XserverRegion *p) {
- if (*p) {
- XFixesDestroyRegion(ps->dpy, *p);
- *p = None;
- }
-}
/**
* Destroy a <code>Picture</code>.
@@ -177,15 +158,57 @@ free_wincondlst(c2_lptr_t **pcondlst) {
#endif
/**
+ * Check whether a paint_t contains enough data.
+ */
+static inline bool
+paint_isvalid(session_t *ps, const paint_t *ppaint) {
+ if (!ppaint || !ppaint->pixmap)
+ return false;
+
+ if (BKEND_XRENDER == ps->o.backend && !ppaint->pict)
+ return false;
+
+#ifdef CONFIG_VSYNC_OPENGL
+ if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex))
+ return false;
+#endif
+
+ return true;
+}
+/**
+ * Bind texture in paint_t if we are using OpenGL backend.
+ */
+static inline bool
+paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth) {
+#ifdef CONFIG_VSYNC_OPENGL
+ // TODO: Make sure we have the same Pixmap binded?
+ if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) {
+ return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth);
+ }
+#endif
+
+ return true;
+}
+
+/**
+ * Free paint_t.
+ */
+static inline void
+free_paint(session_t *ps, paint_t *ppaint) {
+ free_texture(ps, &ppaint->ptex);
+ free_picture(ps, &ppaint->pict);
+ free_pixmap(ps, &ppaint->pixmap);
+}
+
+/**
* Destroy all resources in a <code>struct _win</code>.
*/
-inline static void
+static inline void
free_win_res(session_t *ps, win *w) {
free_region(ps, &w->extents);
- free_pixmap(ps, &w->pixmap);
- free_picture(ps, &w->picture);
+ free_paint(ps, &w->paint);
free_region(ps, &w->border_size);
- free_picture(ps, &w->shadow_pict);
+ free_paint(ps, &w->shadow_paint);
free_damage(ps, &w->damage);
free_region(ps, &w->reg_ignore);
free(w->name);
@@ -195,6 +218,19 @@ free_win_res(session_t *ps, win *w) {
}
/**
+ * Free root tile related things.
+ */
+static inline void
+free_root_tile(session_t *ps) {
+ free_picture(ps, &ps->root_tile_paint.pict);
+ free_texture(ps, &ps->root_tile_paint.ptex);
+ if (ps->root_tile_fill)
+ free_pixmap(ps, &ps->root_tile_paint.pixmap);
+ ps->root_tile_paint.pixmap = None;
+ ps->root_tile_fill = false;
+}
+
+/**
* Get current system clock in milliseconds.
*/
static inline time_ms_t
@@ -274,8 +310,8 @@ presum_gaussian(session_t *ps, conv *map);
static XImage *
make_shadow(session_t *ps, double opacity, int width, int height);
-static Picture
-shadow_picture(session_t *ps, double opacity, int width, int height);
+static bool
+win_build_shadow(session_t *ps, win *w, double opacity);
static Picture
solid_picture(session_t *ps, bool argb, double a,
@@ -463,11 +499,11 @@ group_is_focused(session_t *ps, Window leader) {
static win *
recheck_focus(session_t *ps);
-static Picture
-root_tile_f(session_t *ps);
+static bool
+get_root_tile(session_t *ps);
static void
-paint_root(session_t *ps, Picture tgt_buffer);
+paint_root(session_t *ps, XserverRegion reg_paint);
static XserverRegion
win_get_region(session_t *ps, win *w, bool use_offset);
@@ -491,6 +527,22 @@ static win *
paint_preprocess(session_t *ps, win *list);
static void
+render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
+ double opacity, bool argb, bool neg,
+ Picture pict, glx_texture_t *ptex, XserverRegion reg_paint);
+
+static inline void
+win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, Picture pict) {
+ const int dx = (w ? w->a.x: 0) + x;
+ const int dy = (w ? w->a.y: 0) + y;
+ const bool argb = (w && w->mode == WMODE_ARGB);
+ const bool neg = (w && w->invert_color);
+
+ render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg,
+ pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint);
+}
+
+static void
paint_all(session_t *ps, XserverRegion region, win *t);
static void
@@ -887,39 +939,15 @@ swopti_init(session_t *ps);
static void
swopti_handle_timeout(session_t *ps, struct timeval *ptv);
-static bool
-opengl_init(session_t *ps, bool need_render);
-
-static void
-opengl_destroy(session_t *ps);
-
#ifdef CONFIG_VSYNC_OPENGL
/**
- * Check if a GLX extension exists.
- */
-static inline bool
-opengl_hasext(session_t *ps, const char *ext) {
- const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
- const char *pos = strstr(glx_exts, ext);
- // Make sure the extension string is matched as a whole word
- if (!pos
- || ((pos - glx_exts) && !isspace(*(pos - 1)))
- || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) {
- printf_errf("(): Missing OpenGL extension %s.", ext);
- return false;
- }
-
- return true;
-}
-
-/**
* Ensure we have a GLX context.
*/
static inline bool
ensure_glx_context(session_t *ps) {
// Create GLX context
if (!ps->glx_context)
- opengl_init(ps, false);
+ glx_init(ps, false);
return ps->glx_context;
}
@@ -939,6 +967,9 @@ vsync_opengl_init(session_t *ps);
static bool
vsync_opengl_oml_init(session_t *ps);
+static bool
+vsync_opengl_swc_init(session_t *ps);
+
#ifdef CONFIG_VSYNC_OPENGL
static int
vsync_opengl_wait(session_t *ps);
diff --git a/opengl.c b/opengl.c
new file mode 100644
index 000000000..16e99de02
--- /dev/null
+++ b/opengl.c
@@ -0,0 +1,521 @@
+/*
+ * Compton - a compositor for X11
+ *
+ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
+ *
+ * Copyright (c) 2011-2013, Christopher Jeffrey
+ * See LICENSE for more information.
+ *
+ */
+
+#include "opengl.h"
+
+/**
+ * Initialize OpenGL.
+ */
+bool
+glx_init(session_t *ps, bool need_render) {
+ bool success = false;
+ XVisualInfo *pvis = NULL;
+
+ // Check for GLX extension
+ if (!ps->glx_exists) {
+ if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
+ ps->glx_exists = true;
+ else {
+ printf_errf("(): No GLX extension.");
+ goto glx_init_end;
+ }
+ }
+
+ // Get XVisualInfo
+ pvis = get_visualinfo_from_visual(ps, ps->vis);
+ if (!pvis) {
+ printf_errf("(): Failed to acquire XVisualInfo for current visual.");
+ goto glx_init_end;
+ }
+
+ // Ensure the visual is double-buffered
+ if (need_render) {
+ int value = 0;
+ if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
+ printf_errf("(): Root visual is not a GL visual.");
+ goto glx_init_end;
+ }
+
+ if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value)
+ || !value) {
+ printf_errf("(): Root visual is not a double buffered GL visual.");
+ goto glx_init_end;
+ }
+ }
+
+ // Ensure GLX_EXT_texture_from_pixmap exists
+ if (need_render && !glx_hasext(ps, "GLX_EXT_texture_from_pixmap"))
+ goto glx_init_end;
+
+ // Get GLX context
+ ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
+
+ if (!ps->glx_context) {
+ printf_errf("(): Failed to get GLX context.");
+ goto glx_init_end;
+ }
+
+ // Attach GLX context
+ if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
+ printf_errf("(): Failed to attach GLX context.");
+ goto glx_init_end;
+ }
+
+ // Acquire function addresses
+ if (need_render) {
+ ps->glXBindTexImageProc = (f_BindTexImageEXT)
+ glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT");
+ ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT)
+ glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
+ if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) {
+ printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
+ goto glx_init_end;
+ }
+ }
+
+ // Acquire FBConfigs
+ if (need_render && !glx_update_fbconfig(ps))
+ goto glx_init_end;
+
+ if (need_render) {
+ // Adjust viewport
+ glViewport(0, 0, ps->root_width, ps->root_height);
+
+ // Initialize settings, copied from dcompmgr
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ // glEnable(GL_DEPTH_TEST);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_BLEND);
+
+ // Clear screen
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glXSwapBuffers(ps->dpy, get_tgt_window(ps));
+ }
+
+ success = true;
+
+glx_init_end:
+ if (pvis)
+ XFree(pvis);
+
+ if (!success)
+ glx_destroy(ps);
+
+ return success;
+}
+
+/**
+ * Destroy GLX related resources.
+ */
+void
+glx_destroy(session_t *ps) {
+ // Free FBConfigs
+ for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) {
+ free(ps->glx_fbconfigs[i]);
+ ps->glx_fbconfigs[i] = NULL;
+ }
+
+ // Destroy GLX context
+ if (ps->glx_context) {
+ glXDestroyContext(ps->dpy, ps->glx_context);
+ ps->glx_context = 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);
+}
+
+/**
+ * @brief Update the FBConfig of given depth.
+ */
+static inline void
+glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) {
+ // Make sure the depth is sane
+ if (depth < 0 || depth > OPENGL_MAX_DEPTH)
+ return;
+
+ // Compare new FBConfig with current one
+ if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) {
+#ifdef DEBUG_GLX
+ printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->glx_fbconfigs[depth] ? (unsigned) ps->glx_fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts);
+#endif
+ if (!ps->glx_fbconfigs[depth]) {
+ ps->glx_fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t));
+ allocchk(ps->glx_fbconfigs[depth]);
+ }
+ (*ps->glx_fbconfigs[depth]) = *pfbcfg;
+ }
+}
+
+/**
+ * Get GLX FBConfigs for all depths.
+ */
+static bool
+glx_update_fbconfig(session_t *ps) {
+ // Acquire all FBConfigs and loop through them
+ int nele = 0;
+ GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele);
+
+ for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) {
+ glx_fbconfig_t fbinfo = {
+ .cfg = *pcur,
+ .texture_fmt = 0,
+ .texture_tgts = 0,
+ .y_inverted = false,
+ };
+ int depth = 0, depth_alpha = 0, val = 0;
+
+ if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth)
+ || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) {
+ printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", (int) (pcur - pfbcfgs));
+ continue;
+ }
+ if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) {
+ printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", (int) (pcur - pfbcfgs));
+ continue;
+ }
+
+ bool rgb = false;
+ bool rgba = false;
+
+ if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val)
+ rgba = true;
+
+ if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val)
+ rgb = true;
+
+ if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val))
+ fbinfo.y_inverted = val;
+
+ if ((depth - depth_alpha) < 32 && rgb) {
+ fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT;
+ glx_update_fbconfig_bydepth(ps, depth - depth_alpha, &fbinfo);
+ }
+
+ if (rgba) {
+ fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT;
+ glx_update_fbconfig_bydepth(ps, depth, &fbinfo);
+ }
+ }
+
+ if (pfbcfgs)
+ XFree(pfbcfgs);
+
+ // Sanity checks
+ if (!ps->glx_fbconfigs[ps->depth]) {
+ printf_errf("(): No FBConfig found for default depth %d.", ps->depth);
+ return false;
+ }
+
+ if (!ps->glx_fbconfigs[32]) {
+ printf_errf("(): No FBConfig found for depth 32. Expect crazy things.");
+ }
+
+ return true;
+}
+
+static inline int
+glx_cmp_fbconfig_cmpattr(session_t *ps,
+ const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b,
+ int attr) {
+ int attr_a = 0, attr_b = 0;
+
+ // TODO: Error checking
+ glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a);
+ glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b);
+
+ return attr_a - attr_b;
+}
+
+/**
+ * Compare two GLX FBConfig's to find the preferred one.
+ */
+static int
+glx_cmp_fbconfig(session_t *ps,
+ const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) {
+ int result = 0;
+
+ if (!pfbc_a)
+ return -1;
+ if (!pfbc_b)
+ return 1;
+
+#define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; }
+#define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; }
+
+ P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT);
+ P_CMPATTR_LT(GLX_DOUBLEBUFFER);
+ P_CMPATTR_LT(GLX_STENCIL_SIZE);
+ P_CMPATTR_LT(GLX_DEPTH_SIZE);
+ P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT);
+
+ return 0;
+}
+
+/**
+ * Bind a X pixmap to an OpenGL texture.
+ */
+bool
+glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
+ int width, int height, int depth) {
+ if (depth > OPENGL_MAX_DEPTH) {
+ printf_errf("(%d): Requested depth higher than %d.", depth,
+ OPENGL_MAX_DEPTH);
+ return false;
+ }
+ const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth];
+ if (!pcfg) {
+ printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth);
+ return false;
+ }
+
+ const GLenum target = GL_TEXTURE_2D;
+ glx_texture_t *ptex = *pptex;
+ bool need_release = true;
+
+ // Allocate structure
+ if (!ptex) {
+ static const glx_texture_t GLX_TEX_DEF = {
+ .texture = 0,
+ .glpixmap = 0,
+ .pixmap = 0,
+ .width = 0,
+ .height = 0,
+ .depth = 0,
+ .y_inverted = false,
+ };
+
+ ptex = malloc(sizeof(glx_texture_t));
+ allocchk(ptex);
+ memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
+ *pptex = ptex;
+ }
+
+ glEnable(target);
+
+ // Release pixmap if parameters are inconsistent
+ if (ptex->texture && !(width == ptex->width && height == ptex->height
+ && ptex->pixmap == pixmap && depth == ptex->depth)) {
+ glx_release_pixmap(ps, ptex);
+ }
+
+ // Create texture
+ if (!ptex->texture) {
+ need_release = false;
+
+ GLuint texture = 0;
+ glGenTextures(1, &texture);
+ glBindTexture(target, texture);
+
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(target, 0);
+
+ ptex->texture = texture;
+ ptex->y_inverted = pcfg->y_inverted;
+ }
+ if (!ptex->texture) {
+ printf_errf("(): Failed to allocate texture.");
+ return false;
+ }
+
+ glBindTexture(target, ptex->texture);
+
+ // Create GLX pixmap
+ if (!ptex->glpixmap) {
+ need_release = false;
+
+#ifdef DEBUG_GLX
+ printf_dbgf("(): depth %d rgba %d\n", depth, (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
+#endif
+
+ int attrs[] = {
+ GLX_TEXTURE_FORMAT_EXT,
+ pcfg->texture_fmt,
+ // GLX_TEXTURE_TARGET_EXT,
+ // ,
+ 0,
+ };
+
+ ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs);
+ }
+ if (!ptex->glpixmap) {
+ printf_errf("(): Failed to allocate GLX pixmap.");
+ return false;
+ }
+
+ // The specification requires rebinding whenever the content changes...
+ // We can't follow this, too slow.
+ if (need_release)
+ ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
+
+ ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
+
+ // Cleanup
+ glBindTexture(target, 0);
+ glDisable(target);
+
+ ptex->width = width;
+ ptex->height = height;
+ ptex->depth = depth;
+ ptex->pixmap = pixmap;
+
+ return true;
+}
+
+/**
+ * @brief Release binding of a texture.
+ */
+void
+glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
+ // Release binding
+ if (ptex->glpixmap && ptex->texture) {
+ glBindTexture(GL_TEXTURE_2D, ptex->texture);
+ ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ // Free GLX Pixmap
+ if (ptex->glpixmap) {
+ glXDestroyPixmap(ps->dpy, ptex->glpixmap);
+ ptex->glpixmap = 0;
+ }
+}
+
+/**
+ * @brief Render a region with texture data.
+ */
+bool
+glx_render(session_t *ps, const glx_texture_t *ptex,
+ int x, int y, int dx, int dy, int width, int height, int z,
+ double opacity, bool neg, XserverRegion reg_tgt) {
+ if (!ptex || !ptex->texture) {
+ printf_errf("(): Missing texture.");
+ return false;
+ }
+
+ // Enable blending if needed
+ if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT ==
+ ps->glx_fbconfigs[ptex->depth]->texture_fmt) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ // Needed for handling opacity of ARGB texture
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColor4f(1.0f, 1.0f, 1.0f, opacity);
+ }
+
+ // Color negation
+ if (neg) {
+ // Simple color negation
+ if (!glIsEnabled(GL_BLEND)) {
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(GL_COPY_INVERTED);
+ }
+ // Blending color negation
+ else {
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+ glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, ptex->texture);
+
+ glBegin(GL_QUADS);
+
+ {
+ XserverRegion reg_new = None;
+
+ XRectangle rec_all = {
+ .x = dx,
+ .y = dy,
+ .width = width,
+ .height = height
+ };
+
+ XRectangle *rects = &rec_all;
+ int nrects = 1;
+
+#ifdef DEBUG_GLX
+ printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d \n", x, y, width, height, dx, dy, ptex->width, ptex->height, z);
+#endif
+
+ if (reg_tgt) {
+ reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1);
+ XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt);
+
+ nrects = 0;
+ rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects);
+ }
+
+ for (int i = 0; i < nrects; ++i) {
+ GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width;
+ GLfloat ry = (double) (rects[i].y - dy + y) / ptex->height;
+ GLfloat rxe = rx + (double) rects[i].width / ptex->width;
+ GLfloat rye = ry + (double) rects[i].height / ptex->height;
+ GLint rdx = rects[i].x;
+ GLint rdy = rects[i].y;
+ GLint rdxe = rdx + rects[i].width;
+ GLint rdye = rdy + rects[i].height;
+
+ // Invert Y if needed, this may not work as expected, though. I don't
+ // have such a FBConfig to test with.
+ if (!ptex->y_inverted) {
+ ry = 1.0 - ry;
+ rye = 1.0 - rye;
+ }
+
+#ifdef DEBUG_GLX
+ printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
+#endif
+
+ glTexCoord2f(rx, ry);
+ glVertex3i(rdx, rdy, z);
+
+ glTexCoord2f(rxe, ry);
+ glVertex3i(rdxe, rdy, z);
+
+ glTexCoord2f(rxe, rye);
+ glVertex3i(rdxe, rdye, z);
+
+ glTexCoord2f(rx, rye);
+ glVertex3i(rdx, rdye, z);
+ }
+
+ if (rects && rects != &rec_all)
+ XFree(rects);
+ free_region(ps, &reg_new);
+ }
+ glEnd();
+
+ // Cleanup
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glDisable(GL_BLEND);
+ glDisable(GL_COLOR_LOGIC_OP);
+
+ return true;
+}
diff --git a/opengl.h b/opengl.h
new file mode 100644
index 000000000..b48b441d0
--- /dev/null
+++ b/opengl.h
@@ -0,0 +1,46 @@
+/*
+ * Compton - a compositor for X11
+ *
+ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
+ *
+ * Copyright (c) 2011-2013, Christopher Jeffrey
+ * See LICENSE for more information.
+ *
+ */
+
+#include "common.h"
+
+#include <ctype.h>
+
+/**
+ * Check if a GLX extension exists.
+ */
+static inline bool
+glx_hasext(session_t *ps, const char *ext) {
+ const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
+ const char *pos = strstr(glx_exts, ext);
+ // Make sure the extension string is matched as a whole word
+ if (!pos
+ || ((pos - glx_exts) && !isspace(*(pos - 1)))
+ || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) {
+ printf_errf("(): Missing OpenGL extension %s.", ext);
+ return false;
+ }
+
+ return true;
+}
+
+static inline XVisualInfo *
+get_visualinfo_from_visual(session_t *ps, Visual *visual) {
+ XVisualInfo vreq = { .visualid = XVisualIDFromVisual(visual) };
+ int nitems = 0;
+
+ return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
+}
+
+static bool
+glx_update_fbconfig(session_t *ps);
+
+static int
+glx_cmp_fbconfig(session_t *ps,
+ const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b);