From f9f1e1f228ec21be08833f6aa86fe6ea2c64b625 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 15 Mar 2013 23:16:23 +0800 Subject: [PATCH] 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. --- common.h | 174 +++++++++++-- compton.c | 712 ++++++++++++++++++++++++++++-------------------------- compton.h | 137 +++++++---- opengl.c | 521 +++++++++++++++++++++++++++++++++++++++ opengl.h | 46 ++++ 5 files changed, 1169 insertions(+), 421 deletions(-) create mode 100644 opengl.c create mode 100644 opengl.h 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,15 +274,39 @@ 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; @@ -293,6 +332,8 @@ typedef struct { /// The display name we used. NULL means we are using the value of the /// DISPLAY 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 --paint-on-overlay. 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 next 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; @@ -896,6 +937,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. */ @@ -1304,6 +1359,14 @@ get_atom(session_t *ps, const char *atom_name) { return XInternAtom(ps->dpy, atom_name, False); } +/** + * 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. */ @@ -1371,6 +1434,17 @@ copy_region(const session_t *ps, XserverRegion oldregion) { return region; } +/** + * Destroy a XserverRegion. + */ +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. * @@ -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 Picture 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, ®ion); + // 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 #include #include #include @@ -52,14 +51,6 @@ set_ignore_next(session_t *ps) { 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 Picture. */ @@ -119,16 +110,6 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } -/** - * Destroy a XserverRegion. - */ -inline static void -free_region(session_t *ps, XserverRegion *p) { - if (*p) { - XFixesDestroyRegion(ps->dpy, *p); - *p = None; - } -} /** * Destroy a Picture. @@ -176,16 +157,58 @@ 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 struct _win. */ -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); @@ -194,6 +217,19 @@ free_win_res(session_t *ps, win *w) { free(w->role); } +/** + * 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. */ @@ -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); @@ -490,6 +526,22 @@ get_frame_extents(session_t *ps, win *w, Window client); 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); @@ -887,31 +939,7 @@ 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. */ @@ -919,7 +947,7 @@ 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, ®_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 + +/** + * 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);