diff --git a/twin/compton-tde/c2.c b/twin/compton-tde/c2.c index de221c01d..6baf1337e 100644 --- a/twin/compton-tde/c2.c +++ b/twin/compton-tde/c2.c @@ -764,6 +764,7 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) { if (pleaf->predef) { switch (pleaf->predef) { case C2_L_PFOCUSED: ps->o.track_focus = true; break; + // case C2_L_PROUNDED: ps->o.detect_rounded_corners = true; break; case C2_L_PNAME: case C2_L_PCLASSG: case C2_L_PCLASSI: @@ -1057,6 +1058,8 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; case C2_L_PFOCUSED: tgt = win_is_focused_real(ps, w); break; case C2_L_PWMWIN: tgt = w->wmwin; break; + case C2_L_PBSHAPED: tgt = w->bounding_shaped; break; + case C2_L_PROUNDED: tgt = w->rounded_corners; break; case C2_L_PCLIENT: tgt = w->client_win; break; case C2_L_PLEADER: tgt = w->leader; break; default: *perr = true; assert(0); break; @@ -1292,6 +1295,8 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { bool c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache, void **pdata) { + assert(IsViewable == w->a.map_state); + // Check if the cached entry matches firstly if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) { if (pdata) diff --git a/twin/compton-tde/c2.h b/twin/compton-tde/c2.h index 129a5e739..9e04c09a8 100644 --- a/twin/compton-tde/c2.h +++ b/twin/compton-tde/c2.h @@ -113,6 +113,8 @@ struct _c2_l { C2_L_PARGB, C2_L_PFOCUSED, C2_L_PWMWIN, + C2_L_PBSHAPED, + C2_L_PROUNDED, C2_L_PCLIENT, C2_L_PWINDOWTYPE, C2_L_PLEADER, @@ -201,6 +203,8 @@ const static c2_predef_t C2_PREDEFS[] = { [C2_L_PARGB ] = { "argb" , C2_L_TCARDINAL , 0 }, [C2_L_PFOCUSED ] = { "focused" , C2_L_TCARDINAL , 0 }, [C2_L_PWMWIN ] = { "wmwin" , C2_L_TCARDINAL , 0 }, + [C2_L_PBSHAPED ] = { "bounding_shaped" , C2_L_TCARDINAL , 0 }, + [C2_L_PROUNDED ] = { "rounded_corners" , C2_L_TCARDINAL , 0 }, [C2_L_PCLIENT ] = { "client" , C2_L_TWINDOW , 0 }, [C2_L_PWINDOWTYPE ] = { "window_type" , C2_L_TSTRING , 0 }, [C2_L_PLEADER ] = { "leader" , C2_L_TWINDOW , 0 }, diff --git a/twin/compton-tde/common.h b/twin/compton-tde/common.h index fbdf49a89..4d9f41090 100644 --- a/twin/compton-tde/common.h +++ b/twin/compton-tde/common.h @@ -17,6 +17,7 @@ // === Options === // Debug options, enable them using -D in CFLAGS +// #define DEBUG_BACKTRACE 1 // #define DEBUG_REPAINT 1 // #define DEBUG_EVENTS 1 // #define DEBUG_RESTACK 1 @@ -83,6 +84,10 @@ #define COMPTON_VERSION "unknown" #endif +#if defined(DEBUG_ALLOC_REG) +#define DEBUG_BACKTRACE 1 +#endif + // === Includes === // For some special functions @@ -154,8 +159,6 @@ #define GLX_BACK_BUFFER_AGE_EXT 0x20F4 #endif -#endif - // === Macros === #define MSTR_(s) #s @@ -195,6 +198,11 @@ /// Macro used for shortening some debugging code. #define CASESTRRET(s) case s: return #s +// X resource checker +#ifdef DEBUG_XRC +#include "xrescheck.h" +#endif + // === Constants === #if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) #error libXcomposite version unsupported @@ -222,7 +230,7 @@ #define MS_PER_SEC 1000 #define XRFILTER_CONVOLUTION "convolution" -#define XRFILTER_GUASSIAN "gaussian" +#define XRFILTER_GAUSSIAN "gaussian" #define XRFILTER_BINOMIAL "binomial" /// @brief Maximum OpenGL FBConfig depth. @@ -454,7 +462,6 @@ struct _glx_texture { unsigned depth; bool y_inverted; }; -#endif #ifdef CONFIG_VSYNC_OPENGL_GLSL typedef struct { @@ -480,6 +487,26 @@ typedef struct { /// Height of the textures. int height; } glx_blur_cache_t; + +typedef struct { + /// GLSL program. + GLuint prog; + /// Location of uniform "opacity" in window GLSL program. + GLint unifm_opacity; + /// Location of uniform "invert_color" in blur GLSL program. + GLint unifm_invert_color; + /// Location of uniform "tex" in window GLSL program. + GLint unifm_tex; +} glx_prog_main_t; + +#define GLX_PROG_MAIN_INIT { \ + .prog = 0, \ + .unifm_opacity = -1, \ + .unifm_invert_color = -1, \ + .unifm_tex = -1, \ +} + +#endif #endif typedef struct { @@ -547,10 +574,12 @@ typedef struct _options_t { int glx_swap_method; /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. bool glx_use_gpushader4; - /// Whether to try to detect WM windows and mark them as focused. - bool mark_wmwin_focused; - /// Whether to mark override-redirect windows as focused. - bool mark_ovredir_focused; + /// Custom fragment shader for painting windows, as a string. + char *glx_fshader_win_str; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Custom GLX program used for painting window. + glx_prog_main_t glx_prog_win; +#endif /// Whether to fork to background. bool fork_after_register; /// Whether to detect rounded corners. @@ -558,6 +587,8 @@ typedef struct _options_t { /// Whether to paint on X Composite overlay window instead of root /// window. bool paint_on_overlay; + /// Force painting of window content with blending. + bool force_win_blend; /// Resize damage for a specific number of pixels. int resize_damage; /// Whether to unredirect all windows if a full-screen opaque window @@ -572,6 +603,10 @@ typedef struct _options_t { switch_t redirected_force; /// Whether to stop painting. Controlled through D-Bus. switch_t stoppaint_force; + /// Whether to re-redirect screen on root size change. + bool reredir_on_root_change; + /// Whether to reinitialize GLX on root size change. + bool glx_reinit_on_root_change; /// Whether to enable D-Bus support. bool dbus; /// Path to log file. @@ -582,8 +617,14 @@ typedef struct _options_t { Window benchmark_wid; /// A list of conditions of windows not to paint. c2_lptr_t *paint_blacklist; + /// Whether to avoid using XCompositeNameWindowPixmap(), for debugging. + bool no_name_pixmap; /// Whether to work under synchronized mode for debugging. bool synchronize; + /// Whether to show all X errors. + bool show_all_xerrors; + /// Whether to avoid acquiring X Selection. + bool no_x_selection; // === VSync & software optimization === /// User-specified refresh rate. @@ -631,6 +672,8 @@ typedef struct _options_t { time_ms_t fade_delta; /// Whether to disable fading on window open/close. bool no_fading_openclose; + /// Whether to disable fading on ARGB managed destroyed windows. + bool no_fading_destroyed_argb; /// Whether to disable fading on opacity change bool no_fading_opacitychange; /// Fading blacklist. A linked list of conditions. @@ -683,6 +726,10 @@ typedef struct _options_t { // === Focus related === /// Consider windows of specific types to be always focused. bool wintype_focus[NUM_WINTYPES]; + /// Whether to try to detect WM windows and mark them as focused. + bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + bool mark_ovredir_focused; /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; /// A list of windows always to be considered focused. @@ -701,6 +748,65 @@ typedef struct _options_t { bool track_leader; } options_t; +#ifdef CONFIG_VSYNC_OPENGL +/// Structure containing GLX-dependent data for a compton session. +typedef struct { + // === OpenGL related === + /// GLX context. + GLXContext context; + /// Whether we have GL_ARB_texture_non_power_of_two. + bool has_texture_non_power_of_two; + /// Pointer to glXGetVideoSyncSGI function. + f_GetVideoSync glXGetVideoSyncSGI; + /// Pointer to glXWaitVideoSyncSGI function. + f_WaitVideoSync glXWaitVideoSyncSGI; + /// Pointer to glXGetSyncValuesOML function. + f_GetSyncValuesOML glXGetSyncValuesOML; + /// Pointer to glXWaitForMscOML function. + f_WaitForMscOML glXWaitForMscOML; + /// Pointer to glXSwapIntervalSGI function. + f_SwapIntervalSGI glXSwapIntervalProc; + /// Pointer to glXSwapIntervalMESA function. + f_SwapIntervalMESA glXSwapIntervalMESAProc; + /// Pointer to glXBindTexImageEXT function. + f_BindTexImageEXT glXBindTexImageProc; + /// Pointer to glXReleaseTexImageEXT function. + f_ReleaseTexImageEXT glXReleaseTexImageProc; + /// Pointer to glXCopySubBufferMESA function. + f_CopySubBuffer glXCopySubBufferProc; +#ifdef CONFIG_GLX_SYNC + /// Pointer to the glFenceSync() function. + f_FenceSync glFenceSyncProc; + /// Pointer to the glIsSync() function. + f_IsSync glIsSyncProc; + /// Pointer to the glDeleteSync() function. + f_DeleteSync glDeleteSyncProc; + /// Pointer to the glClientWaitSync() function. + f_ClientWaitSync glClientWaitSyncProc; + /// Pointer to the glWaitSync() function. + f_WaitSync glWaitSyncProc; + /// Pointer to the glImportSyncEXT() function. + f_ImportSyncEXT glImportSyncEXT; +#endif +#ifdef DEBUG_GLX_MARK + /// Pointer to StringMarkerGREMEDY function. + f_StringMarkerGREMEDY glStringMarkerGREMEDY; + /// Pointer to FrameTerminatorGREMEDY function. + f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; +#endif + /// Current GLX Z value. + int z; + /// FBConfig-s for GLX pixmap of different depths. + glx_fbconfig_t *fbconfigs[OPENGL_MAX_DEPTH + 1]; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glx_blur_pass_t blur_passes[MAX_BLUR_PASS]; +#endif +} glx_session_t; + +#define CGLX_SESSION_INIT { .context = NULL } + +#endif + /// Structure containing all necessary data for a compton session. typedef struct _session_t { // === Display related === @@ -742,6 +848,10 @@ typedef struct _session_t { XdbeBackBuffer root_dbe; /// Window ID of the window we register as a symbol. Window reg_win; +#ifdef CONFIG_VSYNC_OPENGL + /// Pointer to GLX data. + glx_session_t *psglx; +#endif // === Operation related === /// Program options. @@ -784,10 +894,6 @@ typedef struct _session_t { /// 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 // Cached blur convolution kernels. XFixed *blur_kerns_cache[MAX_BLUR_PASS]; /// Reset program after next paint. @@ -847,57 +953,6 @@ typedef struct _session_t { int drm_fd; #endif -#ifdef CONFIG_VSYNC_OPENGL - // === OpenGL related === - /// GLX context. - GLXContext glx_context; - /// Whether we have GL_ARB_texture_non_power_of_two. - bool glx_has_texture_non_power_of_two; - /// Pointer to glXGetVideoSyncSGI function. - f_GetVideoSync glXGetVideoSyncSGI; - /// Pointer to glXWaitVideoSyncSGI function. - f_WaitVideoSync glXWaitVideoSyncSGI; - /// Pointer to glXGetSyncValuesOML function. - f_GetSyncValuesOML glXGetSyncValuesOML; - /// Pointer to glXWaitForMscOML function. - f_WaitForMscOML glXWaitForMscOML; - /// Pointer to glXSwapIntervalSGI function. - f_SwapIntervalSGI glXSwapIntervalProc; - /// Pointer to glXSwapIntervalMESA function. - f_SwapIntervalMESA glXSwapIntervalMESAProc; - /// Pointer to glXBindTexImageEXT function. - f_BindTexImageEXT glXBindTexImageProc; - /// Pointer to glXReleaseTexImageEXT function. - f_ReleaseTexImageEXT glXReleaseTexImageProc; - /// Pointer to glXCopySubBufferMESA function. - f_CopySubBuffer glXCopySubBufferProc; -#ifdef CONFIG_GLX_SYNC - /// Pointer to the glFenceSync() function. - f_FenceSync glFenceSyncProc; - /// Pointer to the glIsSync() function. - f_IsSync glIsSyncProc; - /// Pointer to the glDeleteSync() function. - f_DeleteSync glDeleteSyncProc; - /// Pointer to the glClientWaitSync() function. - f_ClientWaitSync glClientWaitSyncProc; - /// Pointer to the glWaitSync() function. - f_WaitSync glWaitSyncProc; - /// Pointer to the glImportSyncEXT() function. - f_ImportSyncEXT glImportSyncEXT; -#endif -#ifdef DEBUG_GLX_MARK - /// Pointer to StringMarkerGREMEDY function. - f_StringMarkerGREMEDY glStringMarkerGREMEDY; - /// Pointer to FrameTerminatorGREMEDY function. - f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; -#endif - /// FBConfig-s for GLX pixmap of different depths. - glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; -#ifdef CONFIG_VSYNC_OPENGL_GLSL - glx_blur_pass_t glx_blur_passes[MAX_BLUR_PASS]; -#endif -#endif - // === X extension related === /// Event base number for X Fixes extension. int xfixes_event; @@ -1130,6 +1185,8 @@ typedef struct _win { /// Do not fade if it's false. Change on window type change. /// Used by fading blacklist in the future. bool fade; + /// Fade state on last paint. + bool fade_last; /// Override value of window fade state. Set by D-Bus method calls. switch_t fade_force; /// Callback to be called after fading completed. @@ -1144,6 +1201,8 @@ typedef struct _win { // Shadow-related members /// Whether a window has shadow. Calculated. bool shadow; + /// Shadow state on last paint. + bool shadow_last; /// Override value of window shadow state. Set by D-Bus method calls. switch_t shadow_force; /// Opacity of the shadow. Affected by window opacity and frame opacity. @@ -1170,12 +1229,16 @@ typedef struct _win { /// Whether to invert window color. bool invert_color; + /// Color inversion state on last paint. + bool invert_color_last; /// Override value of window color inversion state. Set by D-Bus method /// calls. switch_t invert_color_force; /// Whether to blur window background. bool blur_background; + /// Background state on last paint. + bool blur_background_last; /// Whether to show black background bool show_black_background; @@ -1224,13 +1287,13 @@ extern session_t *ps_g; static inline void print_timestamp(session_t *ps); -#ifdef DEBUG_ALLOC_REG +#ifdef DEBUG_BACKTRACE #include -#define BACKTRACE_SIZE 5 +#define BACKTRACE_SIZE 25 /** - * Print current backtrace, excluding the first two items. + * Print current backtrace. * * Stolen from glibc manual. */ @@ -1243,12 +1306,14 @@ print_backtrace(void) { size = backtrace(array, BACKTRACE_SIZE); strings = backtrace_symbols(array, size); - for (size_t i = 2; i < size; i++) + for (size_t i = 0; i < size; i++) printf ("%s\n", strings[i]); free(strings); } +#ifdef DEBUG_ALLOC_REG + /** * Wrapper of XFixesCreateRegion, for debugging. */ @@ -1279,6 +1344,8 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, #define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) #endif +#endif + // === Functions === /** @@ -1873,6 +1940,18 @@ bkend_use_glx(session_t *ps) { || BKEND_XR_GLX_HYBRID == ps->o.backend; } +/** + * Check if there's a GLX context. + */ +static inline bool +glx_has_context(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL + return ps->psglx && ps->psglx->context; +#else + return false; +#endif +} + /** * Check if a window is really focused. */ @@ -1975,7 +2054,15 @@ rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { static inline bool win_is_fullscreen(session_t *ps, const win *w) { return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) - && !w->bounding_shaped; + && (!w->bounding_shaped || w->rounded_corners); +} + +/** + * Check if a window will be painted solid. + */ +static inline bool +win_is_solid(session_t *ps, const win *w) { + return WMODE_SOLID == w->mode && !ps->o.force_win_blend; } /** @@ -2080,12 +2167,22 @@ glx_init(session_t *ps, bool need_render); void glx_destroy(session_t *ps); +bool +glx_reinit(session_t *ps, bool need_render); + void glx_on_root_change(session_t *ps); bool glx_init_blur(session_t *ps); +#ifdef CONFIG_VSYNC_OPENGL_GLSL +bool +glx_load_prog_main(session_t *ps, + const char *vshader_str, const char *fshader_str, + glx_prog_main_t *pprogram); +#endif + bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, unsigned width, unsigned height, unsigned depth); @@ -2121,10 +2218,24 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); bool -glx_render(session_t *ps, const glx_texture_t *ptex, +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, const reg_data_t *pcache_reg); + double opacity, bool argb, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define \ + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) +#else +#define \ + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg) +#endif bool glx_render_specified_color(session_t *ps, int color, int dx, int dy, int width, int height, int z, @@ -2133,12 +2244,19 @@ glx_render_specified_color(session_t *ps, int color, int dx, int dy, int width, void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); +unsigned char * +glx_take_screenshot(session_t *ps, int *out_length); + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_program(const GLuint * const shaders, int nshaders); + +GLuint +glx_create_program_from_str(const char *vert_shader_str, + const char *frag_shader_str); #endif /** @@ -2147,7 +2265,7 @@ glx_create_program(const GLuint * const shaders, int nshaders); static inline void free_texture_r(session_t *ps, GLuint *ptexture) { if (*ptexture) { - assert(ps->glx_context); + assert(glx_has_context(ps)); glDeleteTextures(1, ptexture); *ptexture = 0; } @@ -2213,20 +2331,40 @@ free_texture(session_t *ps, glx_texture_t **pptex) { assert(!*pptex); } +/** + * Free GLX part of paint_t. + */ +static inline void +free_paint_glx(session_t *ps, paint_t *ppaint) { + free_texture(ps, &ppaint->ptex); +} + +/** + * Free GLX part of win. + */ +static inline void +free_win_res_glx(session_t *ps, win *w) { + free_paint_glx(ps, &w->paint); + free_paint_glx(ps, &w->shadow_paint); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + free_glx_bc(ps, &w->glx_blur_cache); +#endif +} + /** * Add a OpenGL debugging marker. */ static inline void glx_mark_(session_t *ps, const char *func, XID xid, bool start) { #ifdef DEBUG_GLX_MARK - if (bkend_use_glx(ps) && ps->glStringMarkerGREMEDY) { + if (glx_has_context(ps) && ps->psglx->glStringMarkerGREMEDY) { if (!func) func = "(unknown)"; const char *postfix = (start ? " (start)": " (end)"); char *str = malloc((strlen(func) + 12 + 2 + strlen(postfix) + 5) * sizeof(char)); strcpy(str, func); sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix); - ps->glStringMarkerGREMEDY(strlen(str), str); + ps->psglx->glStringMarkerGREMEDY(strlen(str), str); free(str); } #endif @@ -2240,8 +2378,8 @@ glx_mark_(session_t *ps, const char *func, XID xid, bool start) { static inline void glx_mark_frame(session_t *ps) { #ifdef DEBUG_GLX_MARK - if (bkend_use_glx(ps) && ps->glFrameTerminatorGREMEDY) - ps->glFrameTerminatorGREMEDY(); + if (glx_has_context(ps) && ps->psglx->glFrameTerminatorGREMEDY) + ps->psglx->glFrameTerminatorGREMEDY(); #endif } @@ -2388,6 +2526,28 @@ c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, #endif +/** + * @brief Dump the given data to a file. + */ +static inline bool +write_binary_data(const char *path, const unsigned char *data, int length) { + if (!data) + return false; + FILE *f = fopen(path, "wb"); + if (!f) { + printf_errf("(\"%s\"): Failed to open file for writing.", path); + return false; + } + int wrote_len = fwrite(data, sizeof(unsigned char), length, f); + fclose(f); + if (wrote_len != length) { + printf_errf("(\"%s\"): Failed to write all blocks: %d / %d", path, + wrote_len, length); + return false; + } + return true; +} + /** * @brief Dump raw bytes in HEX format. * @@ -2423,3 +2583,4 @@ hexdump(const char *data, int len) { fflush(stdout); } +#endif diff --git a/twin/compton-tde/compton.c b/twin/compton-tde/compton.c index 2c467e2c4..f49518b7b 100644 --- a/twin/compton-tde/compton.c +++ b/twin/compton-tde/compton.c @@ -621,20 +621,20 @@ win_build_shadow(session_t *ps, win *w, double opacity) { shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width, shadow_image->height); + assert(!w->shadow_paint.pixmap); w->shadow_paint.pixmap = shadow_pixmap_argb; + assert(!w->shadow_paint.pict); w->shadow_paint.pict = shadow_picture_argb; // Sync it once and only once xr_sync(ps, w->shadow_paint.pixmap, NULL); - bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32, true); - XFreeGC(ps->dpy, gc); XDestroyImage(shadow_image); XFreePixmap(ps->dpy, shadow_pixmap); XRenderFreePicture(ps->dpy, shadow_picture); - return success; + return true; shadow_picture_err: if (shadow_image) @@ -711,6 +711,9 @@ discard_ignore(session_t *ps, unsigned long sequence) { static void set_ignore(session_t *ps, unsigned long sequence) { + if (ps->o.show_all_xerrors) + return; + ignore_t *i = malloc(sizeof(ignore_t)); if (!i) return; @@ -779,6 +782,8 @@ wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, */ static void win_rounded_corners(session_t *ps, win *w) { + w->rounded_corners = false; + if (!w->bounding_shaped) return; @@ -808,11 +813,9 @@ win_rounded_corners(session_t *ps, win *w) { for (i = 0; i < nrects; ++i) if (rects[i].width >= minwidth && rects[i].height >= minheight) { w->rounded_corners = true; - cxfree(rects); - return; + break; } - w->rounded_corners = false; cxfree(rects); } @@ -1257,6 +1260,14 @@ paint_preprocess(session_t *ps, win *list) { free_region(ps, &w->reg_ignore); } + // Restore flags from last paint if the window is being faded out + if (IsUnmapped == w->a.map_state) { + win_set_shadow(ps, w, w->shadow_last); + w->fade = w->fade_last; + win_set_invert_color(ps, w, w->invert_color_last); + win_set_blur_background(ps, w, w->blur_background_last); + } + // Update window opacity target and dim state if asked if (WFLAG_OPCT_CHANGE & w->flags) { calc_opacity(ps, w); @@ -1336,7 +1347,7 @@ paint_preprocess(session_t *ps, win *list) { // If the window is solid, we add the window region to the // ignored region - if (WMODE_SOLID == w->mode) { + if (win_is_solid(ps, w)) { if (!w->frame_opacity) { if (w->border_size) w->reg_ignore = copy_region(ps, w->border_size); @@ -1370,7 +1381,7 @@ paint_preprocess(session_t *ps, win *list) { // is not correctly set. if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; - if (WMODE_SOLID == w->mode + if (win_is_solid(ps, w) && (!w->frame_opacity || !win_has_frame(w)) && win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) @@ -1393,8 +1404,17 @@ paint_preprocess(session_t *ps, win *list) { check_fade_fin(ps, w); } - if (!destroyed) + if (!destroyed) { w->to_paint = to_paint; + + if (w->to_paint) { + // Save flags + w->shadow_last = w->shadow; + w->fade_last = w->fade; + w->invert_color_last = w->invert_color; + w->blur_background_last = w->blur_background; + } + } } @@ -1430,6 +1450,9 @@ paint_preprocess(session_t *ps, win *list) { static inline void win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint, const reg_data_t *pcache_reg) { + // Bind shadow pixmap to GLX texture if needed + paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false); + if (!paint_isvalid(ps, &w->shadow_paint)) { printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); return; @@ -1437,7 +1460,7 @@ win_paint_shadow(session_t *ps, win *w, 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, pcache_reg); + w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL); } /** @@ -1529,6 +1552,20 @@ xr_blur_dst(session_t *ps, Picture tgt_buffer, return true; } +/* + * WORK-IN-PROGRESS! +static void +xr_take_screenshot(session_t *ps) { + XImage *img = XGetImage(ps->dpy, get_tgt_window(ps), 0, 0, + ps->root_width, ps->root_height, AllPlanes, XYPixmap); + if (!img) { + printf_errf("(): Failed to get XImage."); + return NULL; + } + assert(0 == img->xoffset); +} +*/ + /** * Blur the background of a window. */ @@ -1593,7 +1630,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. XserverRegion reg_noframe = None; - if (WMODE_SOLID == w->mode) { + if (win_is_solid(ps, w)) { XserverRegion reg_all = border_size(ps, w, false); reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); @@ -1607,7 +1644,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, #ifdef CONFIG_VSYNC_OPENGL_GLSL case BKEND_GLX: // TODO: Handle frame opacity - glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center, + glx_blur_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5, factor_center, reg_paint, pcache_reg, &w->glx_blur_cache); break; #endif @@ -1617,10 +1654,14 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, } static void -render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, +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, const reg_data_t *pcache_reg) { + XserverRegion reg_paint, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: @@ -1636,8 +1677,8 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->glx_z, opacity, neg, reg_paint, pcache_reg); - ps->glx_z += 1; + ps->psglx->z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); + ps->psglx->z += 1; break; #endif default: @@ -1818,7 +1859,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_dim_dst(ps, x, y, wid, hei, ps->glx_z - 0.7, dim_opacity, + glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity, reg_paint, pcache_reg); break; #endif @@ -1942,7 +1983,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // Painting shadow if (w->shadow) { // Lazy shadow building - if (!paint_isvalid(ps, &w->shadow_paint)) + if (!w->shadow_paint.pixmap) win_build_shadow(ps, w, 1); // Shadow is to be painted based on the ignore region of current @@ -2071,7 +2112,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } // Blur window background - if (w->blur_background && (WMODE_SOLID != w->mode + if (w->blur_background && (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity))) { win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); } @@ -2096,7 +2137,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // effect XSync(ps->dpy, False); #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context) { + if (glx_has_context(ps)) { if (ps->o.vsync_use_glfinish) glFinish(); else @@ -2152,7 +2193,8 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t glFlush(); glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, - ps->root_width, ps->root_height, 0, 1.0, false, region_real, NULL); + ps->root_width, ps->root_height, 0, 1.0, false, false, + region_real, NULL, NULL); // No break here! case BKEND_GLX: if (ps->o.glx_use_copysubbuffermesa) @@ -2172,7 +2214,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XFlush(ps->dpy); #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context) { + if (glx_has_context(ps)) { glFlush(); glXWaitX(); } @@ -2678,9 +2720,21 @@ static void win_determine_fade(session_t *ps, win *w) { if (UNSET != w->fade_force) w->fade = w->fade_force; - else if ((ps->o.no_fading_openclose && w->in_openclose) - || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst) - || (ps->o.no_fading_opacitychange && (!w->in_openclose))) + else if (ps->o.no_fading_opacitychange && (!w->in_openclose)) + w->fade = false; + else if (ps->o.no_fading_openclose && w->in_openclose) + w->fade = false; + else if (ps->o.no_fading_destroyed_argb && w->destroyed + && WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) { + w->fade = false; + // Prevent it from being overwritten by last-paint value + w->fade_last = false; + } + // Ignore other possible causes of fading state changes after window + // gets unmapped + else if (IsViewable != w->a.map_state) { + } + else if (win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) w->fade = false; else w->fade = ps->o.wintype_fade[w->window_type]; @@ -2708,8 +2762,7 @@ win_update_shape(session_t *ps, win *w) { win_update_shape_raw(ps, w); - // Shadow state could be changed - win_determine_shadow(ps, w); + win_on_factor_change(ps, w); /* // If clear_shadow state on the window possibly changed, destroy the old @@ -2754,38 +2807,55 @@ win_update_prop_shadow(session_t *ps, win *w) { win_determine_shadow(ps, w); } +static void +win_set_shadow(session_t *ps, win *w, bool shadow_new) { + if (w->shadow == shadow_new) return; + + w->shadow = shadow_new; + + // Window extents need update on shadow state change + // Shadow geometry currently doesn't change on shadow state change + // calc_shadow_geometry(ps, w); + if (w->extents) { + // Mark the old extents as damaged if the shadow is removed + if (!w->shadow) + add_damage(ps, w->extents); + else + free_region(ps, &w->extents); + w->extents = win_extents(ps, w); + // Mark the new extents as damaged if the shadow is added + if (w->shadow) + add_damage_win(ps, w); + } +} + /** * Determine if a window should have shadow, and update things depending * on shadow state. */ static void win_determine_shadow(session_t *ps, win *w) { - bool shadow_old = w->shadow; + bool shadow_new = w->shadow; - w->shadow = (UNSET == w->shadow_force ? - (ps->o.wintype_shadow[w->window_type] - && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) - && !(ps->o.shadow_ignore_shaped && w->bounding_shaped - && !w->rounded_corners) - && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)) - : w->shadow_force); + if (UNSET != w->shadow_force) + shadow_new = w->shadow_force; + else if (IsViewable == w->a.map_state) + shadow_new = (ps->o.wintype_shadow[w->window_type] + && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) + && !(ps->o.shadow_ignore_shaped && w->bounding_shaped + && !w->rounded_corners) + && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); - // Window extents need update on shadow state change - if (w->shadow != shadow_old) { - // Shadow geometry currently doesn't change on shadow state change - // calc_shadow_geometry(ps, w); - if (w->extents) { - // Mark the old extents as damaged if the shadow is removed - if (!w->shadow) - add_damage(ps, w->extents); - else - free_region(ps, &w->extents); - w->extents = win_extents(ps, w); - // Mark the new extents as damaged if the shadow is added - if (w->shadow) - add_damage_win(ps, w); - } - } + win_set_shadow(ps, w, shadow_new); +} + +static void +win_set_invert_color(session_t *ps, win *w, bool invert_color_new) { + if (w->invert_color == invert_color_new) return; + + w->invert_color = invert_color_new; + + add_damage_win(ps, w); } /** @@ -2793,19 +2863,27 @@ win_determine_shadow(session_t *ps, win *w) { */ static void win_determine_invert_color(session_t *ps, win *w) { - // Do not change window invert color state when the window is unmapped, - // unless it comes from w->invert_color_force. - if (UNSET == w->invert_color_force && IsViewable != w->a.map_state) - return; - - bool invert_color_old = w->invert_color; + bool invert_color_new = w->invert_color; if (UNSET != w->invert_color_force) - w->invert_color = w->invert_color_force; - else - w->invert_color = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); + invert_color_new = w->invert_color_force; + else if (IsViewable == w->a.map_state) + invert_color_new = win_match(ps, w, ps->o.invert_color_list, + &w->cache_ivclst); + + win_set_invert_color(ps, w, invert_color_new); +} - if (w->invert_color != invert_color_old) +static void +win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { + if (w->blur_background == blur_background_new) return; + + w->blur_background = blur_background_new; + + // Only consider window damaged if it's previously painted with background + // blurred + if (!win_is_solid(ps, w) + || (ps->o.blur_background_frame && w->frame_opacity)) add_damage_win(ps, w); } @@ -2814,16 +2892,13 @@ win_determine_invert_color(session_t *ps, win *w) { */ static void win_determine_blur_background(session_t *ps, win *w) { - bool blur_background_old = w->blur_background; + if (IsViewable != w->a.map_state) + return; - w->blur_background = ps->o.blur_background + bool blur_background_new = ps->o.blur_background && !win_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst); - // Only consider window damaged if it's previously painted with background - // blurred - if (w->blur_background != blur_background_old && (WMODE_SOLID != w->mode - || (ps->o.blur_background_frame && w->frame_opacity))) - add_damage_win(ps, w); + win_set_blur_background(ps, w, blur_background_new); } /** @@ -2831,6 +2906,10 @@ win_determine_blur_background(session_t *ps, win *w) { */ static void win_update_opacity_rule(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + +#ifdef CONFIG_C2 // If long is 32-bit, unfortunately there's no way could we express "unset", // so we just entirely don't distinguish "unset" and OPAQUE opacity_t opacity = OPAQUE; @@ -2846,6 +2925,7 @@ win_update_opacity_rule(session_t *ps, win *w) { else if (OPAQUE != w->opacity_set) wid_rm_opacity_prop(ps, w->id); w->opacity_set = opacity; +#endif } /** @@ -2879,10 +2959,10 @@ win_on_factor_change(session_t *ps, win *w) { win_determine_blur_background(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); - if (ps->o.paint_blacklist) + if (IsViewable == w->a.map_state && ps->o.paint_blacklist) w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst); - if (ps->o.unredir_if_possible_blacklist) + if (IsViewable == w->a.map_state && ps->o.unredir_if_possible_blacklist) w->unredir_if_possible_excluded = win_match(ps, w, ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); } @@ -3299,6 +3379,9 @@ restack_win(session_t *ps, win *w, Window new_above) { } } +static bool +init_filters(session_t *ps); + static void configure_win(session_t *ps, XConfigureEvent *ce) { // On root window changes @@ -3312,11 +3395,28 @@ configure_win(session_t *ps, XConfigureEvent *ce) { rebuild_shadow_exclude_reg(ps); free_all_damage_last(ps); + // Re-redirect screen if required + if (ps->o.reredir_on_root_change && ps->redirected) { + redir_stop(ps); + redir_start(ps); + } + #ifdef CONFIG_VSYNC_OPENGL + // Reinitialize GLX on root change + if (ps->o.glx_reinit_on_root_change && ps->psglx) { + if (!glx_reinit(ps, bkend_use_glx(ps))) + printf_errf("(): Failed to reinitialize GLX, troubles ahead."); + if (BKEND_GLX == ps->o.backend && !init_filters(ps)) + printf_errf("(): Failed to initialize filters."); + } + + // GLX root change callback if (BKEND_GLX == ps->o.backend) glx_on_root_change(ps); #endif + force_repaint(ps); + return; } @@ -3466,6 +3566,9 @@ destroy_win(session_t *ps, Window id) { w->destroyed = true; + if (ps->o.no_fading_destroyed_argb) + win_determine_fade(ps, w); + // Set fading callback set_fade_callback(ps, w, destroy_callback, false); @@ -3604,11 +3707,13 @@ xerror(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { { 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", + printf("error %4d %-12s request %4d minor %4d serial %6lu: \"%s\"\n", ev->error_code, name, ev->request_code, ev->minor_code, ev->serial, buf); } + // print_backtrace(); + return 0; } @@ -3659,7 +3764,7 @@ win_update_focused(session_t *ps, win *w) { || (ps->o.mark_wmwin_focused && w->wmwin) || (ps->o.mark_ovredir_focused && w->id == w->client_win && !w->wmwin) - || win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst)) + || (IsViewable == w->a.map_state && win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst))) w->focused = true; // If window grouping detection is enabled, mark the window active if @@ -4226,10 +4331,12 @@ ev_focus_report(XFocusChangeEvent* ev) { * Determine whether we should respond to a FocusIn/Out * event. */ +/* inline static bool ev_focus_accept(XFocusChangeEvent *ev) { return NotifyNormal == ev->mode || NotifyUngrab == ev->mode; } +*/ static inline void ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { @@ -4727,6 +4834,8 @@ usage(int ret) { " Daemonize process.\n" "-S\n" " Enable synchronous operation (for debugging).\n" + "--show-all-xerrors\n" + " Show all X errors (for debugging).\n" "-v\n" " Print version Number and exit\\n" "--config path\n" @@ -4755,13 +4864,21 @@ usage(int ret) { " Mark windows that have no WM frame as active.\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" + "--no-fading-destroyed-argb\n" + " Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n" + " in Openbox, Fluxbox, etc.\n" "--no-fading-opacitychange\n" " Do not fade on window opacity change.\n" "--shadow-ignore-shaped\n" - " Do not paint shadows on shaped windows.\n" + " Do not paint shadows on shaped windows. (Deprecated, use\n" + " --shadow-exclude \'bounding_shaped\' or\n" + " --shadow-exclude \'bounding_shaped && !rounded_corners\' instead.)\n" "--detect-rounded-corners\n" " Try to detect windows with rounded corners and don't consider\n" - " them shaped windows.\n" + " them shaped windows. Affects --shadow-ignore-shaped,\n" + " --unredir-if-possible, and possibly others. You need to turn this\n" + " on manually if you want to match against rounded_corners in\n" + " conditions.\n" "--detect-client-opacity\n" " Detect _NET_WM_OPACITY on client windows, useful for window\n" " managers not passing _NET_WM_OPACITY of client windows to frame\n" @@ -4861,7 +4978,7 @@ usage(int ret) { " The element in the center must not be included, it will be forever\n" " 1.0 or changing based on opacity, depending on whether you have\n" " --blur-background-fixed.\n" - " A 7x7 Guassian blur kernel looks like:\n" + " A 7x7 Gaussian blur kernel looks like:\n" " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" " Up to 4 blur kernels may be specified, separated with semicolon, for\n" " multi-pass blur.\n" @@ -4919,7 +5036,8 @@ usage(int ret) { "--glx-no-rebind-pixmap\n" " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" " could improve performance on rapid window content changes, but is\n" - " known to break things on some drivers.\n" + " known to break things on some drivers (LLVMpipe, xf86-video-intel,\n" + " etc.).\n" "--glx-swap-method undefined/copy/exchange/3/4/5/6/buffer-age\n" " GLX backend: GLX buffer swap method we assume. Could be\n" " undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1).\n" @@ -4949,6 +5067,12 @@ usage(int ret) { #else #define WARNING #endif + "--glx-fshader-win shader\n" + " GLX backend: Use specified GLSL fragment shader for rendering window\n" + " contents.\n" + "--force-win-blend\n" + " Force all windows to be painted with blending. Useful if you have a\n" + " --glx-fshader-win that could turn opaque pixels transparent.\n" "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" " man page for more details." WARNING "\n" @@ -5011,7 +5135,8 @@ register_cm(session_t *ps) { printf_errf("(): Failed to set COMPTON_VERSION."); } - { + // Acquire X Selection _NET_WM_CM_S? + if (!ps->o.no_x_selection) { unsigned len = strlen(REGISTER_PROP) + 2; int s = ps->scr; @@ -5066,7 +5191,7 @@ fork_after(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL // GLX context must be released and reattached on fork - if (ps->glx_context && !glXMakeCurrent(ps->dpy, None, NULL)) { + if (glx_has_context(ps) && !glXMakeCurrent(ps->dpy, None, NULL)) { printf_errf("(): Failed to detach GLx context."); return false; } @@ -5084,8 +5209,8 @@ fork_after(session_t *ps) { setsid(); #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context - && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + if (glx_has_context(ps) + && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->psglx->context)) { printf_errf("(): Failed to make GLX context current."); return false; } @@ -5399,6 +5524,7 @@ parse_geometry_end: */ static inline bool parse_rule_opacity(session_t *ps, const char *src) { +#ifdef CONFIG_C2 // Find opacity value char *endptr = NULL; long val = strtol(src, &endptr, 0); @@ -5423,6 +5549,10 @@ parse_rule_opacity(session_t *ps, const char *src) { // Parse pattern // I hope 1-100 is acceptable for (void *) return c2_parsed(ps, &ps->o.opacity_rules, endptr, (void *) val); +#else + printf_errf("(\"%s\"): Condition support not compiled in.", src); + return false; +#endif } #ifdef CONFIG_LIBCONFIG @@ -5650,6 +5780,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { wintype_arr_enable(ps->o.wintype_fade); // --no-fading-open-close lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); + // --no-fading-destroyed-argb + lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb", + &ps->o.no_fading_destroyed_argb); // --no-fading-opacitychange lcfg_lookup_bool(&cfg, "no-fading-opacitychange", &ps->o.no_fading_opacitychange); // --shadow-red @@ -5743,7 +5876,7 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); // --resize-damage - config_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); + lcfg_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); // --glx-no-stencil lcfg_lookup_bool(&cfg, "glx-no-stencil", &ps->o.glx_no_stencil); // --glx-copy-from-front @@ -5806,6 +5939,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "shadow-offset-y", required_argument, NULL, 't' }, { "fade-in-step", required_argument, NULL, 'I' }, { "fade-out-step", required_argument, NULL, 'O' }, + { "fade-delta", required_argument, NULL, 'D' }, { "menu-opacity", required_argument, NULL, 'm' }, { "shadow", no_argument, NULL, 'c' }, { "no-dock-shadow", no_argument, NULL, 'C' }, @@ -5813,6 +5947,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "fading", no_argument, NULL, 'f' }, { "inactive-opacity", required_argument, NULL, 'i' }, { "frame-opacity", required_argument, NULL, 'e' }, + { "daemon", no_argument, NULL, 'b' }, { "no-dnd-shadow", no_argument, NULL, 'G' }, { "shadow-red", required_argument, NULL, 257 }, { "shadow-green", required_argument, NULL, 258 }, @@ -5871,7 +6006,16 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "vsync-use-glfinish", no_argument, NULL, 311 }, { "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync-fence", no_argument, NULL, 313 }, - { "no-fading-opacitychange", no_argument, NULL, 314 }, + { "show-all-xerrors", no_argument, NULL, 314 }, + { "no-fading-destroyed-argb", no_argument, NULL, 315 }, + { "force-win-blend", no_argument, NULL, 316 }, + { "glx-fshader-win", required_argument, NULL, 317 }, + { "version", no_argument, NULL, 318 }, + { "no-x-selection", no_argument, NULL, 319 }, + { "no-name-pixmap", no_argument, NULL, 320 }, + { "no-fading-opacitychange", no_argument, NULL, 321 }, + { "reredir-on-root-change", no_argument, NULL, 731 }, + { "glx-reinit-on-root-change", no_argument, NULL, 732 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5892,6 +6036,14 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.display = mstrcpy(optarg); else if ('S' == o) ps->o.synchronize = true; + else if (314 == o) + ps->o.show_all_xerrors = true; + else if (318 == o) { + printf("%s\n", COMPTON_VERSION); + exit(0); + } + else if (320 == o) + ps->o.no_name_pixmap = true; else if ('?' == o || ':' == o) usage(1); } @@ -5946,6 +6098,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'v': fprintf (stderr, "%s v%-3.2f\n", argv[0], _TDE_COMP_MGR_VERSION_); my_exit_code=0; exit (0); case 'd': case 'S': + case 314: + case 318: + case 320: break; P_CASELONG('D', fade_delta); case 'I': @@ -6125,7 +6280,15 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(312, xrender_sync); P_CASEBOOL(313, xrender_sync_fence); - P_CASEBOOL(314, no_fading_opacitychange); + P_CASEBOOL(315, no_fading_destroyed_argb); + P_CASEBOOL(316, force_win_blend); + case 317: + ps->o.glx_fshader_win_str = mstrcpy(optarg); + break; + P_CASEBOOL(319, no_x_selection); + P_CASEBOOL(321, no_fading_opacitychange); + P_CASEBOOL(731, reredir_on_root_change); + P_CASEBOOL(732, glx_reinit_on_root_change); default: usage(1); break; @@ -6416,13 +6579,13 @@ vsync_opengl_init(session_t *ps) { return false; // Get video sync functions - if (!ps->glXGetVideoSyncSGI) - ps->glXGetVideoSyncSGI = (f_GetVideoSync) + if (!ps->psglx->glXGetVideoSyncSGI) + ps->psglx->glXGetVideoSyncSGI = (f_GetVideoSync) glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI"); - if (!ps->glXWaitVideoSyncSGI) - ps->glXWaitVideoSyncSGI = (f_WaitVideoSync) + if (!ps->psglx->glXWaitVideoSyncSGI) + ps->psglx->glXWaitVideoSyncSGI = (f_WaitVideoSync) glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI"); - if (!ps->glXWaitVideoSyncSGI || !ps->glXGetVideoSyncSGI) { + if (!ps->psglx->glXWaitVideoSyncSGI || !ps->psglx->glXGetVideoSyncSGI) { printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function."); return false; } @@ -6441,13 +6604,13 @@ vsync_opengl_oml_init(session_t *ps) { return false; // Get video sync functions - if (!ps->glXGetSyncValuesOML) - ps->glXGetSyncValuesOML = (f_GetSyncValuesOML) + if (!ps->psglx->glXGetSyncValuesOML) + ps->psglx->glXGetSyncValuesOML = (f_GetSyncValuesOML) glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML"); - if (!ps->glXWaitForMscOML) - ps->glXWaitForMscOML = (f_WaitForMscOML) + if (!ps->psglx->glXWaitForMscOML) + ps->psglx->glXWaitForMscOML = (f_WaitForMscOML) glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML"); - if (!ps->glXGetSyncValuesOML || !ps->glXWaitForMscOML) { + if (!ps->psglx->glXGetSyncValuesOML || !ps->psglx->glXWaitForMscOML) { printf_errf("(): Failed to get OML_sync_control functions."); return false; } @@ -6471,14 +6634,14 @@ vsync_opengl_swc_init(session_t *ps) { } // Get video sync functions - if (!ps->glXSwapIntervalProc) - ps->glXSwapIntervalProc = (f_SwapIntervalSGI) + if (!ps->psglx->glXSwapIntervalProc) + ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI) glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI"); - if (!ps->glXSwapIntervalProc) { + if (!ps->psglx->glXSwapIntervalProc) { printf_errf("(): Failed to get SGI_swap_control function."); return false; } - ps->glXSwapIntervalProc(1); + ps->psglx->glXSwapIntervalProc(1); return true; #else @@ -6499,14 +6662,14 @@ vsync_opengl_mswc_init(session_t *ps) { } // Get video sync functions - if (!ps->glXSwapIntervalMESAProc) - ps->glXSwapIntervalMESAProc = (f_SwapIntervalMESA) + if (!ps->psglx->glXSwapIntervalMESAProc) + ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA) glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA"); - if (!ps->glXSwapIntervalMESAProc) { + if (!ps->psglx->glXSwapIntervalMESAProc) { printf_errf("(): Failed to get MESA_swap_control function."); return false; } - ps->glXSwapIntervalMESAProc(1); + ps->psglx->glXSwapIntervalMESAProc(1); return true; #else @@ -6523,8 +6686,8 @@ static int vsync_opengl_wait(session_t *ps) { unsigned vblank_count = 0; - ps->glXGetVideoSyncSGI(&vblank_count); - ps->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count); + ps->psglx->glXGetVideoSyncSGI(&vblank_count); + ps->psglx->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count); // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required? return 0; @@ -6539,8 +6702,8 @@ static int vsync_opengl_oml_wait(session_t *ps) { int64_t ust = 0, msc = 0, sbc = 0; - ps->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc); - ps->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, + ps->psglx->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc); + ps->psglx->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc); return 0; @@ -6549,14 +6712,14 @@ vsync_opengl_oml_wait(session_t *ps) { static void vsync_opengl_swc_deinit(session_t *ps) { // The standard says it doesn't accept 0, but in fact it probably does - if (ps->glx_context && ps->glXSwapIntervalProc) - ps->glXSwapIntervalProc(0); + if (glx_has_context(ps) && ps->psglx->glXSwapIntervalProc) + ps->psglx->glXSwapIntervalProc(0); } static void vsync_opengl_mswc_deinit(session_t *ps) { - if (ps->glx_context && ps->glXSwapIntervalMESAProc) - ps->glXSwapIntervalMESAProc(0); + if (glx_has_context(ps) && ps->psglx->glXSwapIntervalMESAProc) + ps->psglx->glXSwapIntervalMESAProc(0); } #endif @@ -6593,7 +6756,6 @@ void vsync_deinit(session_t *ps) { if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync]) VSYNC_FUNCS_DEINIT[ps->o.vsync](ps); - ps->o.vsync = VSYNC_NONE; } /** @@ -6633,7 +6795,7 @@ init_dbe(session_t *ps) { /** * Initialize X composite overlay window. */ -static void +static bool init_overlay(session_t *ps) { ps->overlay = XCompositeGetOverlayWindow(ps->dpy, ps->root); if (ps->overlay) { @@ -6661,6 +6823,11 @@ init_overlay(session_t *ps) { "back to painting on root window.\n"); ps->o.paint_on_overlay = false; } +#ifdef DEBUG_REDIR + printf_dbgf("(): overlay = %#010lx\n", ps->overlay); +#endif + + return ps->overlay; } /** @@ -7071,6 +7238,9 @@ session_init(session_t *ps_old, int argc, char **argv) { .backend = BKEND_XRENDER, .glx_no_stencil = false, .glx_copy_from_front = false, +#ifdef CONFIG_VSYNC_OPENGL_GLSL + .glx_prog_win = GLX_PROG_MAIN_INIT, +#endif .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, @@ -7113,6 +7283,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, .no_fading_openclose = false, + .no_fading_destroyed_argb = false, .no_fading_opacitychange = false, .fade_blacklist = NULL, @@ -7187,15 +7358,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .drm_fd = -1, #endif -#ifdef CONFIG_VSYNC_OPENGL - .glx_context = None, - .glx_has_texture_non_power_of_two = false, - .glXGetVideoSyncSGI = NULL, - .glXWaitVideoSyncSGI = NULL, - .glXGetSyncValuesOML = NULL, - .glXWaitForMscOML = NULL, -#endif - .xfixes_event = 0, .xfixes_error = 0, .damage_event = 0, @@ -7247,14 +7409,6 @@ session_init(session_t *ps_old, int argc, char **argv) { // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); memcpy(ps, &s_def, sizeof(session_t)); -#ifdef CONFIG_VSYNC_OPENGL_GLSL - for (int i = 0; i < MAX_BLUR_PASS; ++i) { - glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; - ppass->unifm_factor_center = -1; - ppass->unifm_offset_x = -1; - ppass->unifm_offset_y = -1; - } -#endif ps_g = ps; ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); @@ -7319,7 +7473,8 @@ session_init(session_t *ps_old, int argc, char **argv) { XCompositeQueryVersion(ps->dpy, &composite_major, &composite_minor); - if (composite_major > 0 || composite_minor >= 2) { + if (!ps->o.no_name_pixmap + && (composite_major > 0 || composite_minor >= 2)) { ps->has_name_pixmap = true; } } @@ -7450,6 +7605,17 @@ session_init(session_t *ps_old, int argc, char **argv) { #endif } + // Initialize window GL shader + if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->o.glx_prog_win)) + exit(1); +#else + printf_errf("(): GLSL supported not compiled in, can't load shader."); + exit(1); +#endif + } + // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); @@ -7543,12 +7709,13 @@ session_init(session_t *ps_old, int argc, char **argv) { cxfree(children); } - if (ps->o.track_focus) { recheck_focus(ps); } XUngrabServer(ps->dpy); + // ALWAYS flush after XUngrabServer()! + XFlush(ps->dpy); // Initialize DBus if (ps->o.dbus) { @@ -7711,6 +7878,7 @@ session_destroy(session_t *ps) { free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); + free(ps->o.glx_fshader_win_str); free_xinerama_info(ps); #ifdef CONFIG_VSYNC_OPENGL @@ -7746,6 +7914,11 @@ session_destroy(session_t *ps) { // Flush all events XSync(ps->dpy, True); +#ifdef DEBUG_XRC + // Report about resource leakage + xrc_report_xid(); +#endif + // Free timeouts ps->tmout_unredir = NULL; timeout_clear(ps); @@ -7754,6 +7927,16 @@ session_destroy(session_t *ps) { ps_g = NULL; } +/* +static inline void +dump_img(session_t *ps) { + int len = 0; + unsigned char *d = glx_take_screenshot(ps, &len); + write_binary_data("/tmp/dump.raw", d, len); + free(d); +} +*/ + /** * Do the actual work. * diff --git a/twin/compton-tde/compton.h b/twin/compton-tde/compton.h index dfdbe53d3..c1c877056 100644 --- a/twin/compton-tde/compton.h +++ b/twin/compton-tde/compton.h @@ -273,7 +273,7 @@ free_reg_data(reg_data_t *pregd) { */ static inline void free_paint(session_t *ps, paint_t *ppaint) { - free_texture(ps, &ppaint->ptex); + free_paint_glx(ps, ppaint); free_picture(ps, &ppaint->pict); free_pixmap(ps, &ppaint->pixmap); } @@ -292,6 +292,7 @@ free_wpaint(session_t *ps, win *w) { */ static inline void free_win_res(session_t *ps, win *w) { + free_win_res_glx(ps, w); free_region(ps, &w->extents); free_paint(ps, &w->paint); free_region(ps, &w->border_size); @@ -302,9 +303,6 @@ free_win_res(session_t *ps, win *w) { free(w->class_instance); free(w->class_general); free(w->role); -#ifdef CONFIG_VSYNC_OPENGL_GLSL - free_glx_bc(ps, &w->glx_blur_cache); -#endif } /** @@ -356,7 +354,7 @@ isdamagenotify(session_t *ps, const XEvent *ev) { */ static inline XTextProperty * make_text_prop(session_t *ps, char *str) { - XTextProperty *pprop = cmalloc(1, XTextProperty); + XTextProperty *pprop = ccalloc(1, XTextProperty); if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { cxfree(pprop->value); @@ -678,20 +676,37 @@ 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, +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, const reg_data_t *pcache_reg); + XserverRegion reg_paint, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define \ + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) +#else +#define \ + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg) +#endif static inline void -win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, Picture pict) { +win_render(session_t *ps, win *w, int x, int y, int wid, int hei, + double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, + 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 argb = (w && (WMODE_ARGB == w->mode || ps->o.force_win_blend)); 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, pcache_reg); + pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), + reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); } static inline void @@ -837,12 +852,21 @@ win_update_prop_shadow_raw(session_t *ps, win *w); static void win_update_prop_shadow(session_t *ps, win *w); +static void +win_set_shadow(session_t *ps, win *w, bool shadow_new); + static void win_determine_shadow(session_t *ps, win *w); +static void +win_set_invert_color(session_t *ps, win *w, bool invert_color_new); + static void win_determine_invert_color(session_t *ps, win *w); +static void +win_set_blur_background(session_t *ps, win *w, bool blur_background_new); + static void win_determine_blur_background(session_t *ps, win *w); @@ -1204,10 +1228,10 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv); static inline bool ensure_glx_context(session_t *ps) { // Create GLX context - if (!ps->glx_context) + if (!glx_has_context(ps)) glx_init(ps, false); - return ps->glx_context; + return ps->psglx->context; } #endif @@ -1254,7 +1278,7 @@ init_alpha_picts(session_t *ps); static bool init_dbe(session_t *ps); -static void +static bool init_overlay(session_t *ps); static void diff --git a/twin/compton-tde/dbus.c b/twin/compton-tde/dbus.c index 8aec9ea82..874f565ca 100644 --- a/twin/compton-tde/dbus.c +++ b/twin/compton-tde/dbus.c @@ -901,6 +901,11 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); + // paint_on_overlay_id: Get ID of the X composite overlay window + if (!strcmp("paint_on_overlay_id", target)) { + cdbus_reply_uint32(ps, msg, ps->overlay); + return true; + } cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32); cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); diff --git a/twin/compton-tde/opengl.c b/twin/compton-tde/opengl.c index bbef9bf16..2b9ff6286 100644 --- a/twin/compton-tde/opengl.c +++ b/twin/compton-tde/opengl.c @@ -15,15 +15,15 @@ void xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { if (*pfence) { - // GLsync sync = ps->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - GLsync sync = ps->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); - /* GLenum ret = ps->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, + // GLsync sync = ps->psglx->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + GLsync sync = ps->psglx->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); + /* GLenum ret = ps->psglx->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000); assert(GL_CONDITION_SATISFIED == ret); */ XSyncTriggerFence(ps->dpy, *pfence); XFlush(ps->dpy); - ps->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); - // ps->glDeleteSyncProc(sync); + ps->psglx->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); + // ps->psglx->glDeleteSyncProc(sync); // XSyncResetFence(ps->dpy, *pfence); } glx_check_err(ps); @@ -99,10 +99,28 @@ glx_init(session_t *ps, bool need_render) { if (need_render && !glx_hasglxext(ps, "GLX_EXT_texture_from_pixmap")) goto glx_init_end; - if (!ps->glx_context) { + // Initialize GLX data structure + if (!ps->psglx) { + static const glx_session_t CGLX_SESSION_DEF = CGLX_SESSION_INIT; + ps->psglx = cmalloc(1, glx_session_t); + memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t)); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; + ppass->unifm_factor_center = -1; + ppass->unifm_offset_x = -1; + ppass->unifm_offset_y = -1; + } +#endif + } + + glx_session_t *psglx = ps->psglx; + + if (!psglx->context) { // Get GLX context #ifndef DEBUG_GLX_DEBUG_CONTEXT - ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); + psglx->context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); #else { GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); @@ -124,18 +142,18 @@ glx_init(session_t *ps, bool need_render) { GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None }; - ps->glx_context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, + psglx->context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, GL_TRUE, attrib_list); } #endif - if (!ps->glx_context) { + if (!psglx->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)) { + if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), psglx->context)) { printf_errf("(): Failed to attach GLX context."); goto glx_init_end; } @@ -170,52 +188,52 @@ glx_init(session_t *ps, bool need_render) { // Check GL_ARB_texture_non_power_of_two, requires a GLX context and // must precede FBConfig fetching if (need_render) - ps->glx_has_texture_non_power_of_two = glx_hasglext(ps, + psglx->has_texture_non_power_of_two = glx_hasglext(ps, "GL_ARB_texture_non_power_of_two"); // Acquire function addresses if (need_render) { #ifdef DEBUG_GLX_MARK - ps->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY) + psglx->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY) glXGetProcAddress((const GLubyte *) "glStringMarkerGREMEDY"); - ps->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY) + psglx->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY) glXGetProcAddress((const GLubyte *) "glFrameTerminatorGREMEDY"); #endif - ps->glXBindTexImageProc = (f_BindTexImageEXT) + psglx->glXBindTexImageProc = (f_BindTexImageEXT) glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); - ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) + psglx->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT"); - if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) { + if (!psglx->glXBindTexImageProc || !psglx->glXReleaseTexImageProc) { printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); goto glx_init_end; } if (ps->o.glx_use_copysubbuffermesa) { - ps->glXCopySubBufferProc = (f_CopySubBuffer) + psglx->glXCopySubBufferProc = (f_CopySubBuffer) glXGetProcAddress((const GLubyte *) "glXCopySubBufferMESA"); - if (!ps->glXCopySubBufferProc) { + if (!psglx->glXCopySubBufferProc) { printf_errf("(): Failed to acquire glXCopySubBufferMESA()."); goto glx_init_end; } } #ifdef CONFIG_GLX_SYNC - ps->glFenceSyncProc = (f_FenceSync) + psglx->glFenceSyncProc = (f_FenceSync) glXGetProcAddress((const GLubyte *) "glFenceSync"); - ps->glIsSyncProc = (f_IsSync) + psglx->glIsSyncProc = (f_IsSync) glXGetProcAddress((const GLubyte *) "glIsSync"); - ps->glDeleteSyncProc = (f_DeleteSync) + psglx->glDeleteSyncProc = (f_DeleteSync) glXGetProcAddress((const GLubyte *) "glDeleteSync"); - ps->glClientWaitSyncProc = (f_ClientWaitSync) + psglx->glClientWaitSyncProc = (f_ClientWaitSync) glXGetProcAddress((const GLubyte *) "glClientWaitSync"); - ps->glWaitSyncProc = (f_WaitSync) + psglx->glWaitSyncProc = (f_WaitSync) glXGetProcAddress((const GLubyte *) "glWaitSync"); - ps->glImportSyncEXT = (f_ImportSyncEXT) + psglx->glImportSyncEXT = (f_ImportSyncEXT) glXGetProcAddress((const GLubyte *) "glImportSyncEXT"); - if (!ps->glFenceSyncProc || !ps->glIsSyncProc || !ps->glDeleteSyncProc - || !ps->glClientWaitSyncProc || !ps->glWaitSyncProc - || !ps->glImportSyncEXT) { + if (!psglx->glFenceSyncProc || !psglx->glIsSyncProc || !psglx->glDeleteSyncProc + || !psglx->glClientWaitSyncProc || !psglx->glWaitSyncProc + || !psglx->glImportSyncEXT) { printf_errf("(): Failed to acquire GLX sync functions."); goto glx_init_end; } @@ -260,33 +278,86 @@ glx_init_end: return success; } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + +static void +glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) { + if (!pprogram) + return; + if (pprogram->prog) { + glDeleteProgram(pprogram->prog); + pprogram->prog = 0; + } + pprogram->unifm_opacity = -1; + pprogram->unifm_invert_color = -1; + pprogram->unifm_tex = -1; +} + +#endif + /** * Destroy GLX related resources. */ void glx_destroy(session_t *ps) { + if (!ps->psglx) + return; + + // Free all GLX resources of windows + for (win *w = ps->list; w; w = w->next) + free_win_res_glx(ps, w); + #ifdef CONFIG_VSYNC_OPENGL_GLSL // Free GLSL shaders/programs for (int i = 0; i < MAX_BLUR_PASS; ++i) { - glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; if (ppass->frag_shader) glDeleteShader(ppass->frag_shader); if (ppass->prog) glDeleteProgram(ppass->prog); } + + glx_free_prog_main(ps, &ps->o.glx_prog_win); + + glx_check_err(ps); #endif // Free FBConfigs for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { - free(ps->glx_fbconfigs[i]); - ps->glx_fbconfigs[i] = NULL; + free(ps->psglx->fbconfigs[i]); + ps->psglx->fbconfigs[i] = NULL; } // Destroy GLX context - if (ps->glx_context) { - glXDestroyContext(ps->dpy, ps->glx_context); - ps->glx_context = NULL; + if (ps->psglx->context) { + glXDestroyContext(ps->dpy, ps->psglx->context); + ps->psglx->context = NULL; } + + free(ps->psglx); + ps->psglx = NULL; +} + +/** + * Reinitialize GLX. + */ +bool +glx_reinit(session_t *ps, bool need_render) { + // Reinitialize VSync as well + vsync_deinit(ps); + + glx_destroy(ps); + if (!glx_init(ps, need_render)) { + printf_errf("(): Failed to initialize GLX."); + return false; + } + + if (!vsync_init(ps)) { + printf_errf("(): Failed to initialize VSync."); + return false; + } + + return true; } /** @@ -356,7 +427,7 @@ glx_init_blur(session_t *ps) { " gl_FragColor = sum / (factor_center + float(%.7g));\n" "}\n"; - const bool use_texture_rect = !ps->glx_has_texture_non_power_of_two; + const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; const char *sampler_type = (use_texture_rect ? "sampler2DRect": "sampler2D"); const char *texture_func = (use_texture_rect ? @@ -375,7 +446,7 @@ glx_init_blur(session_t *ps) { if (!kern) break; - glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; // Build shader { @@ -440,6 +511,7 @@ glx_init_blur(session_t *ps) { P_GET_UNIFM_LOC("offset_x", unifm_offset_x); P_GET_UNIFM_LOC("offset_y", unifm_offset_y); } + #undef P_GET_UNIFM_LOC } free(extension); @@ -459,6 +531,43 @@ glx_init_blur(session_t *ps) { #endif } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + +/** + * Load a GLSL main program from shader strings. + */ +bool +glx_load_prog_main(session_t *ps, + const char *vshader_str, const char *fshader_str, + glx_prog_main_t *pprogram) { + assert(pprogram); + + // Build program + pprogram->prog = glx_create_program_from_str(vshader_str, fshader_str); + if (!pprogram->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + + // Get uniform addresses +#define P_GET_UNIFM_LOC(name, target) { \ + pprogram->target = glGetUniformLocation(pprogram->prog, name); \ + if (pprogram->target < 0) { \ + printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \ + } \ + } + P_GET_UNIFM_LOC("opacity", unifm_opacity); + P_GET_UNIFM_LOC("invert_color", unifm_invert_color); + P_GET_UNIFM_LOC("tex", unifm_tex); +#undef P_GET_UNIFM_LOC + + glx_check_err(ps); + + return true; +} + +#endif + /** * @brief Update the FBConfig of given depth. */ @@ -469,15 +578,15 @@ glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) { return; // Compare new FBConfig with current one - if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) { + if (glx_cmp_fbconfig(ps, ps->psglx->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); + printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->psglx->fbconfigs[depth] ? (unsigned) ps->psglx->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]); + if (!ps->psglx->fbconfigs[depth]) { + ps->psglx->fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t)); + allocchk(ps->psglx->fbconfigs[depth]); } - (*ps->glx_fbconfigs[depth]) = *pfbcfg; + (*ps->psglx->fbconfigs[depth]) = *pfbcfg; } } @@ -559,19 +668,19 @@ glx_update_fbconfig(session_t *ps) { cxfree(pfbcfgs); // Sanity checks - if (!ps->glx_fbconfigs[ps->depth]) { + if (!ps->psglx->fbconfigs[ps->depth]) { printf_errf("(): No FBConfig found for default depth %d.", ps->depth); return false; } - if (!ps->glx_fbconfigs[32]) { + if (!ps->psglx->fbconfigs[32]) { printf_errf("(): No FBConfig found for depth 32. Expect crazy things."); } #ifdef DEBUG_GLX printf_dbgf("(): %d-bit: %#3x, 32-bit: %#3x\n", - ps->depth, (int) ps->glx_fbconfigs[ps->depth]->cfg, - (int) ps->glx_fbconfigs[32]->cfg); + ps->depth, (int) ps->psglx->fbconfigs[ps->depth]->cfg, + (int) ps->psglx->fbconfigs[32]->cfg); #endif return true; @@ -675,7 +784,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, } } - const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth]; + const glx_fbconfig_t *pcfg = ps->psglx->fbconfigs[depth]; if (!pcfg) { printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth); return false; @@ -686,7 +795,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, // pixmap-specific parameters, and this may change in the future GLenum tex_tgt = 0; if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts - && ps->glx_has_texture_non_power_of_two) + && ps->psglx->has_texture_non_power_of_two) tex_tgt = GLX_TEXTURE_2D_EXT; else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; @@ -751,9 +860,9 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, // 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->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); - ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); + ps->psglx->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); // Cleanup glBindTexture(ptex->target, 0); @@ -772,7 +881,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { // Release binding if (ptex->glpixmap && ptex->texture) { glBindTexture(ptex->target, ptex->texture); - ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + ps->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); glBindTexture(ptex->target, 0); } @@ -790,7 +899,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { */ void glx_paint_pre(session_t *ps, XserverRegion *preg) { - ps->glx_z = 0.0; + ps->psglx->z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Get buffer age @@ -1058,8 +1167,8 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg, glx_blur_cache_t *pbc) { - assert(ps->glx_blur_passes[0].prog); - const bool more_passes = ps->glx_blur_passes[1].prog; + assert(ps->psglx->blur_passes[0].prog); + const bool more_passes = ps->psglx->blur_passes[1].prog; const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); bool ret = false; @@ -1096,7 +1205,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, */ GLenum tex_tgt = GL_TEXTURE_RECTANGLE; - if (ps->glx_has_texture_non_power_of_two) + if (ps->psglx->has_texture_non_power_of_two) tex_tgt = GL_TEXTURE_2D; // Free textures if size inconsistency discovered @@ -1159,9 +1268,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, bool last_pass = false; for (int i = 0; !last_pass; ++i) { - last_pass = !ps->glx_blur_passes[i + 1].prog; + last_pass = !ps->psglx->blur_passes[i + 1].prog; assert(i < MAX_BLUR_PASS - 1); - const glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + const glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; assert(ppass->prog); assert(tex_scr); @@ -1315,10 +1424,14 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, * @brief Render a region with texture data. */ bool -glx_render(session_t *ps, const glx_texture_t *ptex, +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, const reg_data_t *pcache_reg) { + double opacity, bool argb, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ) { if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -1329,8 +1442,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; #endif - const bool argb = (GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->glx_fbconfigs[ptex->depth]->texture_fmt); + argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == + ps->psglx->fbconfigs[ptex->depth]->texture_fmt); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + const bool has_prog = pprogram && pprogram->prog; +#endif bool dual_texture = false; // It's required by legacy versions of OpenGL to enable texture target @@ -1351,77 +1467,95 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glColor4f(opacity, opacity, opacity, opacity); } - // Color negation - if (neg) { - // Simple color negation - if (!glIsEnabled(GL_BLEND)) { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(GL_COPY_INVERTED); - } - // ARGB texture color negation - else if (argb) { - dual_texture = true; - - // Use two texture stages because the calculation is too complicated, - // thanks to madsy for providing code - // Texture stage 0 - glActiveTexture(GL_TEXTURE0); - - // Negation for premultiplied color: color = A - C - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - // Pass texture alpha through - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - - // Texture stage 1 - glActiveTexture(GL_TEXTURE1); - glEnable(ptex->target); - glBindTexture(ptex->target, ptex->texture); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); - - glActiveTexture(GL_TEXTURE0); - } - // RGB blend color negation - else { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (!has_prog) +#endif + { + // Color negation + if (neg) { + // Simple color negation + if (!glIsEnabled(GL_BLEND)) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_COPY_INVERTED); + } + // ARGB texture color negation + else if (argb) { + dual_texture = true; + + // Use two texture stages because the calculation is too complicated, + // thanks to madsy for providing code + // Texture stage 0 + glActiveTexture(GL_TEXTURE0); + + // Negation for premultiplied color: color = A - C + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Pass texture alpha through + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + // Texture stage 1 + glActiveTexture(GL_TEXTURE1); + glEnable(ptex->target); + glBindTexture(ptex->target, ptex->texture); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + } + // RGB blend color negation + else { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } } } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + else { + // Programmable path + assert(pprogram->prog); + glUseProgram(pprogram->prog); + if (pprogram->unifm_opacity >= 0) + glUniform1f(pprogram->unifm_opacity, opacity); + if (pprogram->unifm_invert_color >= 0) + glUniform1i(pprogram->unifm_invert_color, neg); + if (pprogram->unifm_tex >= 0) + glUniform1i(pprogram->unifm_tex, 0); + } +#endif #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); @@ -1504,6 +1638,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glActiveTexture(GL_TEXTURE0); } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (has_prog) + glUseProgram(0); +#endif + glx_check_err(ps); return true; @@ -1641,7 +1780,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { #ifdef DEBUG_GLX printf_dbgf("(): %d, %d, %d, %d\n", x, y, wid, hei); #endif - ps->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); + ps->psglx->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); } } @@ -1650,6 +1789,32 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { cxfree(rects); } +/** + * @brief Get tightly packed RGB888 data from GL front buffer. + * + * Don't expect any sort of decent performance. + * + * @returns tightly packed RGB888 data of the size of the screen, + * to be freed with `free()` + */ +unsigned char * +glx_take_screenshot(session_t *ps, int *out_length) { + int length = 3 * ps->root_width * ps->root_height; + GLint unpack_align_old = 0; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_align_old); + assert(unpack_align_old > 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + unsigned char *buf = cmalloc(length, unsigned char); + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, ps->root_width, ps->root_height, GL_RGB, + GL_UNSIGNED_BYTE, buf); + glReadBuffer(GL_BACK); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_align_old); + if (out_length) + *out_length = sizeof(unsigned char) * length; + return buf; +} + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) { @@ -1661,7 +1826,7 @@ glx_create_shader(GLenum shader_type, const char *shader_str) { bool success = false; GLuint shader = glCreateShader(shader_type); if (!shader) { - printf_errf("(): Failed to create shader with type %d.", shader_type); + printf_errf("(): Failed to create shader with type %#x.", shader_type); goto glx_create_shader_end; } glShaderSource(shader, 1, &shader_str, NULL); @@ -1737,5 +1902,40 @@ glx_create_program_end: return program; } + +/** + * @brief Create a program from vertex and fragment shader strings. + */ +GLuint +glx_create_program_from_str(const char *vert_shader_str, + const char *frag_shader_str) { + GLuint vert_shader = 0; + GLuint frag_shader = 0; + GLuint prog = 0; + + if (vert_shader_str) + vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str); + if (frag_shader_str) + frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str); + + { + GLuint shaders[2]; + int count = 0; + if (vert_shader) + shaders[count++] = vert_shader; + if (frag_shader) + shaders[count++] = frag_shader; + assert(count <= sizeof(shaders) / sizeof(shaders[0])); + if (count) + prog = glx_create_program(shaders, count); + } + + if (vert_shader) + glDeleteShader(vert_shader); + if (frag_shader) + glDeleteShader(frag_shader); + + return prog; +} #endif diff --git a/twin/compton-tde/opengl.h b/twin/compton-tde/opengl.h index 8628e36d3..b4e04440a 100644 --- a/twin/compton-tde/opengl.h +++ b/twin/compton-tde/opengl.h @@ -41,10 +41,10 @@ glx_dump_err_str(GLenum err) { */ static inline void glx_check_err_(session_t *ps, const char *func, int line) { - if (!ps->glx_context) return; + if (!ps->psglx->context) return; GLenum err = GL_NO_ERROR; - + while (GL_NO_ERROR != (err = glGetError())) { print_timestamp(ps); printf("%s():%d: GLX error ", func, line); diff --git a/xrescheck.c b/xrescheck.c new file mode 100644 index 000000000..6a63bed36 --- /dev/null +++ b/xrescheck.c @@ -0,0 +1,65 @@ +#include "xrescheck.h" + +static xrc_xid_record_t *gs_xid_records = NULL; + +#define HASH_ADD_XID(head, xidfield, add) \ + HASH_ADD(hh, head, xidfield, sizeof(xid), add) + +#define HASH_FIND_XID(head, findxid, out) \ + HASH_FIND(hh, head, findxid, sizeof(xid), out) + +#define M_CPY_POS_DATA(prec) \ + prec->file = file; \ + prec->func = func; \ + prec->line = line; \ + +/** + * @brief Add a record of given XID to the allocation table. + */ +void +xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) { + xrc_xid_record_t *prec = cmalloc(1, xrc_xid_record_t); + prec->xid = xid; + prec->type = type; + M_CPY_POS_DATA(prec); + + HASH_ADD_XID(gs_xid_records, xid, prec); +} + +/** + * @brief Delete a record of given XID in the allocation table. + */ +void +xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) { + xrc_xid_record_t *prec = NULL; + HASH_FIND_XID(gs_xid_records, &xid, prec); + if (!prec) { + printf_err("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.", + file, line, func, xid); + return; + } + HASH_DEL(gs_xid_records, prec); + free(prec); +} + +/** + * @brief Report about issues found in the XID allocation table. + */ +void +xrc_report_xid(void) { + for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next) + printf_dbg("XRC: %s:%d %s(): %#010lx (%s) not freed.\n", + prec->file, prec->line, prec->func, prec->xid, prec->type); +} + +/** + * @brief Clear the XID allocation table. + */ +void +xrc_clear_xid(void) { + xrc_xid_record_t *prec = NULL, *ptmp = NULL; + HASH_ITER(hh, gs_xid_records, prec, ptmp) { + HASH_DEL(gs_xid_records, prec); + free(prec); + } +} diff --git a/xrescheck.h b/xrescheck.h new file mode 100644 index 000000000..48f254b20 --- /dev/null +++ b/xrescheck.h @@ -0,0 +1,70 @@ +#ifndef COMPTON_XRESCHECK_H +#define COMPTON_XRESCHECK_H + +#include "common.h" +#include + +typedef struct { + XID xid; + const char *type; + const char *file; + const char *func; + int line; + UT_hash_handle hh; +} xrc_xid_record_t; + +#define M_POS_DATA_PARAMS const char *file, int line, const char *func +#define M_POS_DATA_PASSTHROUGH file, line, func +#define M_POS_DATA __FILE__, __LINE__, __func__ + +void +xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS); + +#define xrc_add_xid(xid, type) xrc_add_xid_(xid, type, M_POS_DATA) + +void +xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS); + +#define xrc_delete_xid(xid) xrc_delete_xid_(xid, M_POS_DATA) + +void +xrc_report_xid(void); + +void +xrc_clear_xid(void); + +// Pixmap + +static inline Pixmap +XCreatePixmap_(Display *dpy, Drawable drawable, + unsigned int width, unsigned int height, unsigned int depth, + M_POS_DATA_PARAMS) { + Pixmap ret = XCreatePixmap(dpy, drawable, width, height, depth); + if (ret) + xrc_add_xid_(ret, "Pixmap", M_POS_DATA_PASSTHROUGH); + return ret; +} + +#define XCreatePixmap(dpy, drawable, width, height, depth) \ + XCreatePixmap_(dpy, drawable, width, height, depth, M_POS_DATA) + +static inline Pixmap +XCompositeNameWindowPixmap_(Display *dpy, Window window, M_POS_DATA_PARAMS) { + Pixmap ret = XCompositeNameWindowPixmap(dpy, window); + if (ret) + xrc_add_xid_(ret, "PixmapC", M_POS_DATA_PASSTHROUGH); + return ret; +} + +#define XCompositeNameWindowPixmap(dpy, window) \ + XCompositeNameWindowPixmap_(dpy, window, M_POS_DATA) + +static inline void +XFreePixmap_(Display *dpy, Pixmap pixmap, M_POS_DATA_PARAMS) { + XFreePixmap(dpy, pixmap); + xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH); +} + +#define XFreePixmap(dpy, pixmap) XFreePixmap_(dpy, pixmap, M_POS_DATA); + +#endif