diff options
| author | allusive-dev <[email protected]> | 2023-09-19 17:46:20 +1000 |
|---|---|---|
| committer | allusive-dev <[email protected]> | 2023-09-19 17:46:20 +1000 |
| commit | 5650d887357bf2a3fac8c5fd4f467bf8795b5fc4 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/render.c | |
| parent | Update picom.sample.conf (diff) | |
| download | compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.tar.xz compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.zip | |
reset
Diffstat (limited to 'src/render.c')
| -rw-r--r-- | src/render.c | 1500 |
1 files changed, 0 insertions, 1500 deletions
diff --git a/src/render.c b/src/render.c deleted file mode 100644 index ac9b40e..0000000 --- a/src/render.c +++ /dev/null @@ -1,1500 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Yuxuan Shui <[email protected]> - -#include <stdlib.h> -#include <string.h> -#include <xcb/composite.h> -#include <xcb/render.h> -#include <xcb/sync.h> -#include <xcb/xcb_image.h> -#include <xcb/xcb_renderutil.h> - -#include "common.h" -#include "options.h" - -#ifdef CONFIG_OPENGL -#include "backend/gl/glx.h" -#include "opengl.h" - -#ifndef GLX_BACK_BUFFER_AGE_EXT -#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 -#endif - -#endif - -#include "compiler.h" -#include "config.h" -#include "kernel.h" -#include "log.h" -#include "region.h" -#include "types.h" -#include "utils.h" -#include "vsync.h" -#include "win.h" -#include "x.h" - -#include "backend/backend.h" -#include "backend/backend_common.h" -#include "render.h" - -#define XRFILTER_CONVOLUTION "convolution" -#define XRFILTER_GAUSSIAN "gaussian" -#define XRFILTER_BINOMIAL "binomial" - -/** - * Bind texture in paint_t if we are using GLX backend. - */ -static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, - bool repeat, int depth, xcb_visualid_t visual, bool force) { -#ifdef CONFIG_OPENGL - // XXX This is a mess. But this will go away after the backend refactor. - if (!ppaint->pixmap) - return false; - - struct glx_fbconfig_info *fbcfg; - if (!visual) { - assert(depth == 32); - if (!ps->argb_fbconfig) { - ps->argb_fbconfig = - glx_find_fbconfig(ps->dpy, ps->scr, - (struct xvisual_info){.red_size = 8, - .green_size = 8, - .blue_size = 8, - .alpha_size = 8, - .visual_depth = 32}); - } - if (!ps->argb_fbconfig) { - log_error("Failed to find appropriate FBConfig for 32 bit depth"); - return false; - } - fbcfg = ps->argb_fbconfig; - } else { - auto m = x_get_visual_info(ps->c, visual); - if (m.visual_depth < 0) { - return false; - } - - if (depth && depth != m.visual_depth) { - log_error("Mismatching visual depth: %d != %d", depth, m.visual_depth); - return false; - } - - if (!ppaint->fbcfg) { - ppaint->fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, m); - } - if (!ppaint->fbcfg) { - log_error("Failed to find appropriate FBConfig for X pixmap"); - return false; - } - fbcfg = ppaint->fbcfg; - } - - if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) - return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, - repeat, fbcfg); -#else - (void)ps; - (void)ppaint; - (void)wid; - (void)hei; - (void)repeat; - (void)depth; - (void)visual; - (void)force; -#endif - return true; -} - -/** - * Check if current backend uses XRender for rendering. - */ -static inline bool bkend_use_xrender(session_t *ps) { - return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; -} - -int maximum_buffer_age(session_t *ps) { - if (bkend_use_glx(ps) && ps->o.use_damage) { - return CGLX_MAX_BUFFER_AGE; - } - return 1; -} - -static int get_buffer_age(session_t *ps) { -#ifdef CONFIG_OPENGL - if (bkend_use_glx(ps)) { - if (!glxext.has_GLX_EXT_buffer_age && ps->o.use_damage) { - log_warn("GLX_EXT_buffer_age not supported by your driver," - "`use-damage` has to be disabled"); - ps->o.use_damage = false; - } - if (ps->o.use_damage) { - unsigned int val; - glXQueryDrawable(ps->dpy, get_tgt_window(ps), - GLX_BACK_BUFFER_AGE_EXT, &val); - return (int)val ?: -1; - } - return -1; - } -#endif - return ps->o.use_damage ? 1 : -1; -} - -/** - * Reset filter on a <code>Picture</code>. - */ -static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) { -#define FILTER "Nearest" - xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL); -#undef FILTER -} - -/// Set the input/output clip region of the target buffer (not the actual target!) -static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg) { - switch (ps->o.backend) { - case BKEND_XRENDER: - case BKEND_XR_GLX_HYBRID: - x_set_picture_clip_region(ps->c, ps->tgt_buffer.pict, 0, 0, reg); - break; -#ifdef CONFIG_OPENGL - case BKEND_GLX: glx_set_clip(ps, reg); break; -#endif - default: assert(false); - } -} - -/** - * Destroy a <code>Picture</code>. - */ -void free_picture(xcb_connection_t *c, xcb_render_picture_t *p) { - if (*p) { - xcb_render_free_picture(c, *p); - *p = XCB_NONE; - } -} - -/** - * Free paint_t. - */ -void free_paint(session_t *ps, paint_t *ppaint) { -#ifdef CONFIG_OPENGL - free_paint_glx(ps, ppaint); -#endif - free_picture(ps->c, &ppaint->pict); - if (ppaint->pixmap) - xcb_free_pixmap(ps->c, ppaint->pixmap); - ppaint->pixmap = XCB_NONE; -} - -uint32_t -make_circle(int cx, int cy, int radius, uint32_t max_ntraps, xcb_render_trapezoid_t traps[]) { - uint32_t n = 0, k = 0; - int y1, y2; - double w; - while (k < max_ntraps) { - y1 = (int)(-radius * cos(M_PI * k / max_ntraps)); - traps[n].top = (cy + y1) * 65536; - traps[n].left.p1.y = (cy + y1) * 65536; - traps[n].right.p1.y = (cy + y1) * 65536; - w = sqrt(radius * radius - y1 * y1) * 65536; - traps[n].left.p1.x = (int)((cx * 65536) - w); - traps[n].right.p1.x = (int)((cx * 65536) + w); - - do { - k++; - y2 = (int)(-radius * cos(M_PI * k / max_ntraps)); - } while (y1 == y2); - - traps[n].bottom = (cy + y2) * 65536; - traps[n].left.p2.y = (cy + y2) * 65536; - traps[n].right.p2.y = (cy + y2) * 65536; - w = sqrt(radius * radius - y2 * y2) * 65536; - traps[n].left.p2.x = (int)((cx * 65536) - w); - traps[n].right.p2.x = (int)((cx * 65536) + w); - n++; - } - return n; -} - -uint32_t make_rectangle(int x, int y, int wid, int hei, xcb_render_trapezoid_t traps[]) { - traps[0].top = y * 65536; - traps[0].left.p1.y = y * 65536; - traps[0].left.p1.x = x * 65536; - traps[0].left.p2.y = (y + hei) * 65536; - traps[0].left.p2.x = x * 65536; - traps[0].bottom = (y + hei) * 65536; - traps[0].right.p1.x = (x + wid) * 65536; - traps[0].right.p1.y = y * 65536; - traps[0].right.p2.x = (x + wid) * 65536; - traps[0].right.p2.y = (y + hei) * 65536; - return 1; -} - -uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps, - int cr, int wid, int hei) { - uint32_t n = make_circle(cr, cr, cr, max_ntraps, traps); - n += make_circle(wid - cr, cr, cr, max_ntraps, traps + n); - n += make_circle(wid - cr, hei - cr, cr, max_ntraps, traps + n); - n += make_circle(cr, hei - cr, cr, max_ntraps, traps + n); - n += make_rectangle(0, cr, wid, hei - 2 * cr, traps + n); - n += make_rectangle(cr, 0, wid - 2 * cr, cr, traps + n); - n += make_rectangle(cr, hei - cr, wid - 2 * cr, cr, traps + n); - return n; -} - -void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int fullwid, - int fullhei, double opacity, bool argb, bool neg, int cr, - xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint, - const glx_prog_main_t *pprogram, clip_t *clip) { - switch (ps->o.backend) { - case BKEND_XRENDER: - case BKEND_XR_GLX_HYBRID: { - auto alpha_step = (int)(opacity * MAX_ALPHA); - xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step]; - if (alpha_step != 0) { - if (cr) { - xcb_render_picture_t p_tmp = x_create_picture_with_standard( - ps->c, ps->root, fullwid, fullhei, - XCB_PICT_STANDARD_ARGB_32, 0, 0); - xcb_render_color_t trans = { - .red = 0, .blue = 0, .green = 0, .alpha = 0}; - const xcb_rectangle_t rect = { - .x = 0, - .y = 0, - .width = to_u16_checked(fullwid), - .height = to_u16_checked(fullhei)}; - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, - p_tmp, trans, 1, &rect); - - uint32_t max_ntraps = to_u32_checked(cr); - xcb_render_trapezoid_t traps[4 * max_ntraps + 3]; - - uint32_t n = make_rounded_window_shape( - traps, max_ntraps, cr, fullwid, fullhei); - - xcb_render_trapezoids( - ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp, - x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), - 0, 0, n, traps); - - xcb_render_composite( - ps->c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp, - ps->tgt_buffer.pict, to_i16_checked(x), - to_i16_checked(y), to_i16_checked(x), to_i16_checked(y), - to_i16_checked(dx), to_i16_checked(dy), - to_u16_checked(wid), to_u16_checked(hei)); - - xcb_render_free_picture(ps->c, p_tmp); - - } else { - xcb_render_picture_t p_tmp = alpha_pict; - if (clip) { - p_tmp = x_create_picture_with_standard( - ps->c, ps->root, wid, hei, - XCB_PICT_STANDARD_ARGB_32, 0, 0); - - xcb_render_color_t black = { - .red = 255, .blue = 255, .green = 255, .alpha = 255}; - const xcb_rectangle_t rect = { - .x = 0, - .y = 0, - .width = to_u16_checked(wid), - .height = to_u16_checked(hei)}; - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, - p_tmp, black, 1, &rect); - if (alpha_pict) { - xcb_render_composite( - ps->c, XCB_RENDER_PICT_OP_SRC, - alpha_pict, XCB_NONE, p_tmp, 0, 0, 0, - 0, 0, 0, to_u16_checked(wid), - to_u16_checked(hei)); - } - xcb_render_composite( - ps->c, XCB_RENDER_PICT_OP_OUT_REVERSE, - clip->pict, XCB_NONE, p_tmp, 0, 0, 0, 0, - to_i16_checked(clip->x), to_i16_checked(clip->y), - to_u16_checked(wid), to_u16_checked(hei)); - } - uint8_t op = ((!argb && !alpha_pict && !clip) - ? XCB_RENDER_PICT_OP_SRC - : XCB_RENDER_PICT_OP_OVER); - - xcb_render_composite( - ps->c, op, pict, p_tmp, ps->tgt_buffer.pict, - to_i16_checked(x), to_i16_checked(y), 0, 0, - to_i16_checked(dx), to_i16_checked(dy), - to_u16_checked(wid), to_u16_checked(hei)); - if (clip) { - xcb_render_free_picture(ps->c, p_tmp); - } - } - } - break; - } -#ifdef CONFIG_OPENGL - case BKEND_GLX: - glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb, - neg, reg_paint, pprogram); - ps->psglx->z += 1; - break; -#endif - default: assert(0); - } -#ifndef CONFIG_OPENGL - (void)neg; - (void)ptex; - (void)reg_paint; - (void)pprogram; -#endif -} - -static inline void -paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid, int hei, - double opacity, const region_t *reg_paint, xcb_render_picture_t pict) { - const int dx = (w ? w->g.x : 0) + x; - const int dy = (w ? w->g.y : 0) + y; - const int fullwid = w ? w->widthb : 0; - const int fullhei = w ? w->heightb : 0; - const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend)); - const bool neg = (w && w->invert_color); - - render(ps, x, y, dx, dy, wid, hei, fullwid, fullhei, opacity, argb, neg, - w ? w->corner_radius : 0, pict, - (w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint, -#ifdef CONFIG_OPENGL - w ? &ps->glx_prog_win : NULL -#else - NULL -#endif - , - XCB_NONE); -} - -/** - * Check whether a paint_t contains enough data. - */ -static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { - // Don't check for presence of Pixmap here, because older X Composite doesn't - // provide it - if (!ppaint) - return false; - - if (bkend_use_xrender(ps) && !ppaint->pict) - return false; - -#ifdef CONFIG_OPENGL - if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, XCB_NONE)) - return false; -#endif - - return true; -} - -/** - * Paint a window itself and dim it if asked. - */ -void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) { - // Fetch Pixmap - if (!w->paint.pixmap) { - w->paint.pixmap = x_new_id(ps->c); - set_ignore_cookie(ps, xcb_composite_name_window_pixmap(ps->c, w->base.id, - w->paint.pixmap)); - } - - xcb_drawable_t draw = w->paint.pixmap; - if (!draw) { - log_error("Failed to get pixmap from window %#010x (%s), window won't be " - "visible", - w->base.id, w->name); - return; - } - - // XRender: Build picture - if (bkend_use_xrender(ps) && !w->paint.pict) { - xcb_render_create_picture_value_list_t pa = { - .subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS, - }; - - w->paint.pict = x_create_picture_with_pictfmt_and_pixmap( - ps->c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); - } - - // GLX: Build texture - // Let glx_bind_pixmap() determine pixmap size, because if the user - // is resizing windows, the width and height we get may not be up-to-date, - // causing the jittering issue M4he reported in #7. - if (!paint_bind_tex(ps, &w->paint, 0, 0, false, 0, w->a.visual, - (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { - log_error("Failed to bind texture for window %#010x.", w->base.id); - } - w->pixmap_damaged = false; - - if (!paint_isvalid(ps, &w->paint)) { - log_error("Window %#010x is missing painting data.", w->base.id); - return; - } - - const int x = w->g.x; - const int y = w->g.y; - const uint16_t wid = to_u16_checked(w->widthb); - const uint16_t hei = to_u16_checked(w->heightb); - - xcb_render_picture_t pict = w->paint.pict; - - // Invert window color, if required - if (bkend_use_xrender(ps) && w->invert_color) { - xcb_render_picture_t newpict = x_create_picture_with_pictfmt( - ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL); - if (newpict) { - // Apply clipping region to save some CPU - if (reg_paint) { - region_t reg; - pixman_region32_init(®); - pixman_region32_copy(®, (region_t *)reg_paint); - pixman_region32_translate(®, -x, -y); - // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, - // 0, reg); - pixman_region32_fini(®); - } - - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE, - newpict, 0, 0, 0, 0, 0, 0, wid, hei); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, - ps->white_picture, XCB_NONE, newpict, 0, 0, - 0, 0, 0, 0, wid, hei); - // We use an extra PictOpInReverse operation to get correct - // pixel alpha. There could be a better solution. - if (win_has_alpha(w)) - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE, - pict, XCB_NONE, newpict, 0, 0, 0, 0, - 0, 0, wid, hei); - pict = newpict; - } - } - - if (w->frame_opacity == 1) { - paint_region(ps, w, 0, 0, wid, hei, w->opacity, reg_paint, pict); - } else { - // Painting parameters - const margin_t extents = win_calc_frame_extents(w); - const auto t = extents.top; - const auto l = extents.left; - const auto b = extents.bottom; - const auto r = extents.right; - -#define COMP_BDR(cx, cy, cwid, chei) \ - paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \ - reg_paint, pict) - - // Sanitize the margins, in case some broken WM makes - // top_width + bottom_width > height in some cases. - - do { - // top - int body_height = hei; - // ctop = checked top - // Make sure top margin is smaller than height - int ctop = min2(body_height, t); - if (ctop > 0) - COMP_BDR(0, 0, wid, ctop); - - body_height -= ctop; - if (body_height <= 0) - break; - - // bottom - // cbot = checked bottom - // Make sure bottom margin is not too large - int cbot = min2(body_height, b); - if (cbot > 0) - COMP_BDR(0, hei - cbot, wid, cbot); - - // Height of window exclude the margin - body_height -= cbot; - if (body_height <= 0) - break; - - // left - int body_width = wid; - int cleft = min2(body_width, l); - if (cleft > 0) - COMP_BDR(0, ctop, cleft, body_height); - - body_width -= cleft; - if (body_width <= 0) - break; - - // right - int cright = min2(body_width, r); - if (cright > 0) - COMP_BDR(wid - cright, ctop, cright, body_height); - - body_width -= cright; - if (body_width <= 0) - break; - - // body - paint_region(ps, w, cleft, ctop, body_width, body_height, - w->opacity, reg_paint, pict); - } while (0); - } - -#undef COMP_BDR - - if (pict != w->paint.pict) - free_picture(ps->c, &pict); - - // Dimming the window if needed - if (w->dim) { - double dim_opacity = ps->o.inactive_dim; - if (!ps->o.inactive_dim_fixed) - dim_opacity *= w->opacity; - - switch (ps->o.backend) { - case BKEND_XRENDER: - case BKEND_XR_GLX_HYBRID: { - auto cval = (uint16_t)(0xffff * dim_opacity); - - // Premultiply color - xcb_render_color_t color = { - .red = 0, - .green = 0, - .blue = 0, - .alpha = cval, - }; - - xcb_rectangle_t rect = { - .x = to_i16_checked(x), - .y = to_i16_checked(y), - .width = wid, - .height = hei, - }; - - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER, - ps->tgt_buffer.pict, color, 1, &rect); - } break; -#ifdef CONFIG_OPENGL - case BKEND_GLX: - glx_dim_dst(ps, x, y, wid, hei, (int)(ps->psglx->z - 0.7), - (float)dim_opacity, reg_paint); - break; -#endif - default: assert(false); - } - } -} - -extern const char *background_props_str[]; - -static bool get_root_tile(session_t *ps) { - /* - if (ps->o.paint_on_overlay) { - return ps->root_picture; - } */ - - assert(!ps->root_tile_paint.pixmap); - ps->root_tile_fill = false; - - bool fill = false; - xcb_pixmap_t pixmap = x_get_root_back_pixmap(ps->c, ps->root, ps->atoms); - - // Make sure the pixmap we got is valid - if (pixmap && !x_validate_pixmap(ps->c, pixmap)) - pixmap = XCB_NONE; - - // Create a pixmap if there isn't any - if (!pixmap) { - pixmap = x_create_pixmap(ps->c, (uint8_t)ps->depth, ps->root, 1, 1); - if (pixmap == XCB_NONE) { - log_error("Failed to create pixmaps for root tile."); - return false; - } - fill = true; - } - - // Create Picture - xcb_render_create_picture_value_list_t pa = { - .repeat = true, - }; - ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap( - ps->c, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa); - - // Fill pixmap if needed - if (fill) { - xcb_render_color_t col; - xcb_rectangle_t rect; - - col.red = col.green = col.blue = 0x8080; - col.alpha = 0xffff; - - rect.x = rect.y = 0; - rect.width = rect.height = 1; - - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, - ps->root_tile_paint.pict, col, 1, &rect); - } - - ps->root_tile_fill = fill; - ps->root_tile_paint.pixmap = pixmap; -#ifdef CONFIG_OPENGL - if (BKEND_GLX == ps->o.backend) - return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, ps->vis, false); -#endif - - return true; -} - -/** - * Paint root window content. - */ -static void paint_root(session_t *ps, const region_t *reg_paint) { - // If there is no root tile pixmap, try getting one. - // If that fails, give up. - if (!ps->root_tile_paint.pixmap && !get_root_tile(ps)) - return; - - paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, - ps->root_tile_paint.pict); -} - -/** - * Generate shadow <code>Picture</code> for a window. - */ -static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) { - const int width = w->widthb; - const int height = w->heightb; - // log_trace("(): building shadow for %s %d %d", w->name, width, height); - - xcb_image_t *shadow_image = NULL; - xcb_pixmap_t shadow_pixmap = XCB_NONE, shadow_pixmap_argb = XCB_NONE; - xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE; - xcb_gcontext_t gc = XCB_NONE; - - shadow_image = make_shadow(ps->c, ps->gaussian_map, opacity, width, height); - if (!shadow_image) { - log_error("failed to make shadow"); - return XCB_NONE; - } - - shadow_pixmap = - x_create_pixmap(ps->c, 8, ps->root, shadow_image->width, shadow_image->height); - shadow_pixmap_argb = - x_create_pixmap(ps->c, 32, ps->root, shadow_image->width, shadow_image->height); - - if (!shadow_pixmap || !shadow_pixmap_argb) { - log_error("failed to create shadow pixmaps"); - goto shadow_picture_err; - } - - shadow_picture = x_create_picture_with_standard_and_pixmap( - ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL); - shadow_picture_argb = x_create_picture_with_standard_and_pixmap( - ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL); - if (!shadow_picture || !shadow_picture_argb) - goto shadow_picture_err; - - gc = x_new_id(ps->c); - xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL); - - xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture, - shadow_picture, 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; - - xcb_free_gc(ps->c, gc); - xcb_image_destroy(shadow_image); - xcb_free_pixmap(ps->c, shadow_pixmap); - xcb_render_free_picture(ps->c, shadow_picture); - - return true; - -shadow_picture_err: - if (shadow_image) - xcb_image_destroy(shadow_image); - if (shadow_pixmap) - xcb_free_pixmap(ps->c, shadow_pixmap); - if (shadow_pixmap_argb) - xcb_free_pixmap(ps->c, shadow_pixmap_argb); - if (shadow_picture) - xcb_render_free_picture(ps->c, shadow_picture); - if (shadow_picture_argb) - xcb_render_free_picture(ps->c, shadow_picture_argb); - if (gc) - xcb_free_gc(ps->c, gc); - - return false; -} - -/** - * Paint the shadow of a window. - */ -static inline void -win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) { - // Bind shadow pixmap to GLX texture if needed - paint_bind_tex(ps, &w->shadow_paint, 0, 0, false, 32, 0, false); - - if (!paint_isvalid(ps, &w->shadow_paint)) { - log_error("Window %#010x is missing shadow data.", w->base.id); - return; - } - - xcb_render_picture_t td = XCB_NONE; - bool should_clip = - (w->corner_radius > 0) && (!ps->o.wintype_option[w->window_type].full_shadow); - if (should_clip) { - if (ps->o.backend == BKEND_XRENDER || ps->o.backend == BKEND_XR_GLX_HYBRID) { - uint32_t max_ntraps = to_u32_checked(w->corner_radius); - xcb_render_trapezoid_t traps[4 * max_ntraps + 3]; - uint32_t n = make_rounded_window_shape( - traps, max_ntraps, w->corner_radius, w->widthb, w->heightb); - - td = x_create_picture_with_standard( - ps->c, ps->root, w->widthb, w->heightb, - XCB_PICT_STANDARD_ARGB_32, 0, 0); - xcb_render_color_t trans = { - .red = 0, .blue = 0, .green = 0, .alpha = 0}; - const xcb_rectangle_t rect = {.x = 0, - .y = 0, - .width = to_u16_checked(w->widthb), - .height = to_u16_checked(w->heightb)}; - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td, - trans, 1, &rect); - - auto solid = solid_picture(ps->c, ps->root, false, 1, 0, 0, 0); - xcb_render_trapezoids( - ps->c, XCB_RENDER_PICT_OP_OVER, solid, td, - x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0, - 0, n, traps); - xcb_render_free_picture(ps->c, solid); - } else { - // Not implemented - } - } - - clip_t clip = { - .pict = td, - .x = -(w->shadow_dx), - .y = -(w->shadow_dy), - }; - render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width, - w->shadow_height, w->widthb, w->heightb, w->shadow_opacity, true, false, 0, - w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL, - should_clip ? &clip : NULL); - if (td) { - xcb_render_free_picture(ps->c, td); - } -} - -/** - * @brief Blur an area on a buffer. - * - * @param ps current session - * @param tgt_buffer a buffer as both source and destination - * @param x x pos - * @param y y pos - * @param wid width - * @param hei height - * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at - * least one kernel - * @param reg_clip a clipping region to be applied on intermediate buffers - * - * @return true if successful, false otherwise - */ -static bool -xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y, - uint16_t wid, uint16_t hei, struct x_convolution_kernel **blur_kerns, - int nkernels, const region_t *reg_clip, xcb_render_picture_t rounded) { - assert(blur_kerns); - assert(blur_kerns[0]); - - // Directly copying from tgt_buffer to it does not work, so we create a - // Picture in the middle. - xcb_render_picture_t tmp_picture = - x_create_picture_with_visual(ps->c, ps->root, wid, hei, ps->vis, 0, NULL); - - if (!tmp_picture) { - log_error("Failed to build intermediate Picture."); - return false; - } - - if (reg_clip && tmp_picture) - x_set_picture_clip_region(ps->c, tmp_picture, 0, 0, reg_clip); - - xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture; - for (int i = 0; i < nkernels; ++i) { - xcb_render_fixed_t *convolution_blur = blur_kerns[i]->kernel; - // `x / 65536.0` converts from X fixed point to double - int kwid = (int)((double)convolution_blur[0] / 65536.0), - khei = (int)((double)convolution_blur[1] / 65536.0); - bool rd_from_tgt = (tgt_buffer == src_pict); - - // Copy from source picture to destination. The filter must - // be applied on source picture, to get the nearby pixels outside the - // window. - xcb_render_set_picture_filter( - ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION, - (uint32_t)(kwid * khei + 2), convolution_blur); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, - dst_pict, (rd_from_tgt ? x : 0), - (rd_from_tgt ? y : 0), 0, 0, (rd_from_tgt ? 0 : x), - (rd_from_tgt ? 0 : y), wid, hei); - xrfilter_reset(ps, src_pict); - - { - xcb_render_picture_t tmp = src_pict; - src_pict = dst_pict; - dst_pict = tmp; - } - } - - if (src_pict != tgt_buffer) - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded, - tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); - - free_picture(ps->c, &tmp_picture); - - return true; -} - -/** - * Blur the background of a window. - */ -static inline void -win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t tgt_buffer, - const region_t *reg_paint) { - const int16_t x = w->g.x; - const int16_t y = w->g.y; - const auto wid = to_u16_checked(w->widthb); - const auto hei = to_u16_checked(w->heightb); - const int cr = w ? w->corner_radius : 0; - - double factor_center = 1.0; - // Adjust blur strength according to window opacity, to make it appear - // better during fading - if (!ps->o.blur_background_fixed) { - double pct = 1.0 - w->opacity * (1.0 - 1.0 / 9.0); - factor_center = pct * 8.0 / (1.1 - pct); - } - - switch (ps->o.backend) { - case BKEND_XRENDER: - case BKEND_XR_GLX_HYBRID: { - // Normalize blur kernels - for (int i = 0; i < ps->o.blur_kernel_count; i++) { - // Note: `x * 65536` converts double `x` to a X fixed point - // representation. `x / 65536` is the other way. - auto kern_src = ps->o.blur_kerns[i]; - auto kern_dst = ps->blur_kerns_cache[i]; - - assert(!kern_dst || (kern_src->w == kern_dst->kernel[0] / 65536 && - kern_src->h == kern_dst->kernel[1] / 65536)); - - // Skip for fixed factor_center if the cache exists already - if (ps->o.blur_background_fixed && kern_dst) { - continue; - } - - x_create_convolution_kernel(kern_src, factor_center, - &ps->blur_kerns_cache[i]); - } - - xcb_render_picture_t td = XCB_NONE; - if (cr) { - uint32_t max_ntraps = to_u32_checked(cr); - xcb_render_trapezoid_t traps[4 * max_ntraps + 3]; - uint32_t n = - make_rounded_window_shape(traps, max_ntraps, cr, wid, hei); - - td = x_create_picture_with_standard( - ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0); - xcb_render_color_t trans = { - .red = 0, .blue = 0, .green = 0, .alpha = 0}; - const xcb_rectangle_t rect = {.x = 0, - .y = 0, - .width = to_u16_checked(wid), - .height = to_u16_checked(hei)}; - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td, - trans, 1, &rect); - - auto solid = solid_picture(ps->c, ps->root, false, 1, 0, 0, 0); - - xcb_render_trapezoids( - ps->c, XCB_RENDER_PICT_OP_OVER, solid, td, - x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0, - 0, n, traps); - xcb_render_free_picture(ps->c, solid); - } - - // Minimize the region we try to blur, if the window itself is not - // opaque, only the frame is. - region_t reg_blur = win_get_bounding_shape_global_by_val(w); - if (w->mode == WMODE_FRAME_TRANS && !ps->o.force_win_blend) { - region_t reg_noframe; - pixman_region32_init(®_noframe); - win_get_region_noframe_local(w, ®_noframe); - pixman_region32_translate(®_noframe, w->g.x, w->g.y); - pixman_region32_subtract(®_blur, ®_blur, ®_noframe); - pixman_region32_fini(®_noframe); - } - - // Translate global coordinates to local ones - pixman_region32_translate(®_blur, -x, -y); - xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, - ps->o.blur_kernel_count, ®_blur, td); - if (td) { - xcb_render_free_picture(ps->c, td); - } - pixman_region32_clear(®_blur); - } break; -#ifdef CONFIG_OPENGL - case BKEND_GLX: - // TODO(compton) Handle frame opacity - glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5f, - (float)factor_center, reg_paint, &w->glx_blur_cache); - break; -#endif - default: assert(0); - } -#ifndef CONFIG_OPENGL - (void)reg_paint; -#endif -} - -/// paint all windows -/// region = ?? -/// region_real = the damage region -void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { - if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) { - if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) { - log_error("x_fence_sync failed, xrender-sync-fence will be " - "disabled from now on."); - xcb_sync_destroy_fence(ps->c, ps->sync_fence); - ps->sync_fence = XCB_NONE; - ps->o.xrender_sync_fence = false; - ps->xsync_exists = false; - } - } - - region_t region; - pixman_region32_init(®ion); - int buffer_age = get_buffer_age(ps); - if (buffer_age == -1 || buffer_age > ps->ndamage || ignore_damage) { - pixman_region32_copy(®ion, &ps->screen_reg); - } else { - for (int i = 0; i < get_buffer_age(ps); i++) { - auto curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage; - pixman_region32_union(®ion, ®ion, &ps->damage_ring[curr]); - } - } - - if (!pixman_region32_not_empty(®ion)) { - return; - } - -#ifdef DEBUG_REPAINT - static struct timespec last_paint = {0}; -#endif - - if (ps->o.resize_damage > 0) { - resize_region_in_place(®ion, ps->o.resize_damage, ps->o.resize_damage); - } - - // Remove the damaged area out of screen - pixman_region32_intersect(®ion, ®ion, &ps->screen_reg); - - if (!paint_isvalid(ps, &ps->tgt_buffer)) { - if (!ps->tgt_buffer.pixmap) { - free_paint(ps, &ps->tgt_buffer); - ps->tgt_buffer.pixmap = - x_create_pixmap(ps->c, (uint8_t)ps->depth, ps->root, - ps->root_width, ps->root_height); - if (ps->tgt_buffer.pixmap == XCB_NONE) { - log_fatal("Failed to allocate a screen-sized pixmap for" - "painting"); - exit(1); - } - } - - if (BKEND_GLX != ps->o.backend) - ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap( - ps->c, ps->vis, ps->tgt_buffer.pixmap, 0, 0); - } - - if (BKEND_XRENDER == ps->o.backend) { - x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, ®ion); - } - -#ifdef CONFIG_OPENGL - if (bkend_use_glx(ps)) { - ps->psglx->z = 0.0; - } -#endif - - region_t reg_tmp, *reg_paint; - pixman_region32_init(®_tmp); - if (t) { - // Calculate the region upon which the root window is to be - // painted based on the ignore region of the lowest window, if - // available - pixman_region32_subtract(®_tmp, ®ion, t->reg_ignore); - reg_paint = ®_tmp; - } else { - reg_paint = ®ion; - } - - // Region on screen we don't want any shadows on - region_t reg_shadow_clip; - pixman_region32_init(®_shadow_clip); - - set_tgt_clip(ps, reg_paint); - paint_root(ps, reg_paint); - - // Windows are sorted from bottom to top - // Each window has a reg_ignore, which is the region obscured by all the - // windows on top of that window. This is used to reduce the number of - // pixels painted. - // - // Whether this is beneficial is to be determined XXX - for (auto w = t; w; w = w->prev_trans) { - region_t bshape_no_corners = - win_get_bounding_shape_global_without_corners_by_val(w); - region_t bshape_corners = win_get_bounding_shape_global_by_val(w); - // Painting shadow - if (w->shadow) { - // Lazy shadow building - if (!w->shadow_paint.pixmap) - if (!win_build_shadow(ps, w, 1)) - log_error("build shadow failed"); - - // Shadow doesn't need to be painted underneath the body - // of the windows above. Because no one can see it - pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore); - - // Mask out the region we don't want shadow on - if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) - pixman_region32_subtract(®_tmp, ®_tmp, - &ps->shadow_exclude_reg); - if (pixman_region32_not_empty(®_shadow_clip)) { - pixman_region32_subtract(®_tmp, ®_tmp, ®_shadow_clip); - } - - // Might be worth while to crop the region to shadow - // border - assert(w->shadow_width >= 0 && w->shadow_height >= 0); - pixman_region32_intersect_rect( - ®_tmp, ®_tmp, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, - (uint)w->shadow_width, (uint)w->shadow_height); - - // Mask out the body of the window from the shadow if - // needed Doing it here instead of in make_shadow() for - // saving GPU power and handling shaped windows (XXX - // unconfirmed) - if (!ps->o.wintype_option[w->window_type].full_shadow) - pixman_region32_subtract(®_tmp, ®_tmp, &bshape_no_corners); - - if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 && - w->xinerama_scr < ps->xinerama_nscrs) - // There can be a window where number of screens - // is updated, but the screen number attached to - // the windows have not. - // - // Window screen number will be updated - // eventually, so here we just check to make sure - // we don't access out of bounds. - pixman_region32_intersect( - ®_tmp, ®_tmp, - &ps->xinerama_scr_regs[w->xinerama_scr]); - - // Detect if the region is empty before painting - if (pixman_region32_not_empty(®_tmp)) { - set_tgt_clip(ps, ®_tmp); - win_paint_shadow(ps, w, ®_tmp); - } - } - - // Only clip shadows above visible windows - if (w->opacity * MAX_ALPHA >= 1) { - if (w->clip_shadow_above) { - // Add window bounds to shadow-clip region - pixman_region32_union(®_shadow_clip, ®_shadow_clip, - &bshape_corners); - } else { - // Remove overlapping window bounds from shadow-clip - // region - pixman_region32_subtract( - ®_shadow_clip, ®_shadow_clip, &bshape_corners); - } - } - - // Calculate the paint region based on the reg_ignore of the current - // window and its bounding region. - // Remember, reg_ignore is the union of all windows above the current - // window. - pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore); - pixman_region32_intersect(®_tmp, ®_tmp, &bshape_corners); - pixman_region32_fini(&bshape_corners); - pixman_region32_fini(&bshape_no_corners); - - if (pixman_region32_not_empty(®_tmp)) { - set_tgt_clip(ps, ®_tmp); - -#ifdef CONFIG_OPENGL - // If rounded corners backup the region first - if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) { - const int16_t x = w->g.x; - const int16_t y = w->g.y; - const auto wid = to_u16_checked(w->widthb); - const auto hei = to_u16_checked(w->heightb); - glx_bind_texture(ps, &w->glx_texture_bg, x, y, wid, hei); - } -#endif - - // Blur window background - if (w->blur_background && - (w->mode == WMODE_TRANS || - (ps->o.blur_background_frame && w->mode == WMODE_FRAME_TRANS) || - ps->o.force_win_blend)) { - win_blur_background(ps, w, ps->tgt_buffer.pict, ®_tmp); - } - - // Painting the window - paint_one(ps, w, ®_tmp); - -#ifdef CONFIG_OPENGL - // Rounded corners for XRender is implemented inside render() - // Round window corners - if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) { - const auto wid = to_u16_checked(w->widthb); - const auto hei = to_u16_checked(w->heightb); - glx_round_corners_dst(ps, w, w->glx_texture_bg, w->g.x, - w->g.y, wid, hei, - (float)ps->psglx->z - 0.5F, - (float)w->corner_radius, ®_tmp); - } -#endif - } - } - - // Free up all temporary regions - pixman_region32_fini(®_tmp); - pixman_region32_fini(®_shadow_clip); - - // Move the head of the damage ring - ps->damage = ps->damage - 1; - if (ps->damage < ps->damage_ring) { - ps->damage = ps->damage_ring + ps->ndamage - 1; - } - pixman_region32_clear(ps->damage); - - // Do this as early as possible - set_tgt_clip(ps, &ps->screen_reg); - - if (ps->o.vsync) { - // Make sure all previous requests are processed to achieve best - // effect - x_sync(ps->c); -#ifdef CONFIG_OPENGL - if (glx_has_context(ps)) { - if (ps->o.vsync_use_glfinish) - glFinish(); - else - glFlush(); - glXWaitX(); - } -#endif - } - - if (ps->vsync_wait) { - ps->vsync_wait(ps); - } - - auto rwidth = to_u16_checked(ps->root_width); - auto rheight = to_u16_checked(ps->root_height); - switch (ps->o.backend) { - case BKEND_XRENDER: - if (ps->o.monitor_repaint) { - // Copy the screen content to a new picture, and highlight the - // paint region. This is not very efficient, but since it's for - // debug only, we don't really care - - // First we create a new picture, and copy content from the buffer - // to it - auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis); - xcb_render_picture_t new_pict = x_create_picture_with_pictfmt( - ps->c, ps->root, rwidth, rheight, pictfmt, 0, NULL); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, - ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, - 0, 0, 0, 0, 0, rwidth, rheight); - - // Next, we set the region of paint and highlight it - x_set_picture_clip_region(ps->c, new_pict, 0, 0, ®ion); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture, - ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, - 0, 0, 0, 0, 0, rwidth, rheight); - - // Finally, clear clip regions of new_pict and the screen, and put - // the whole thing on screen - x_set_picture_clip_region(ps->c, new_pict, 0, 0, &ps->screen_reg); - x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, &ps->screen_reg); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, new_pict, - XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0, - rwidth, rheight); - xcb_render_free_picture(ps->c, new_pict); - } else - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, - ps->tgt_buffer.pict, XCB_NONE, ps->tgt_picture, - 0, 0, 0, 0, 0, 0, rwidth, rheight); - break; -#ifdef CONFIG_OPENGL - case BKEND_XR_GLX_HYBRID: - x_sync(ps->c); - if (ps->o.vsync_use_glfinish) - glFinish(); - else - glFlush(); - glXWaitX(); - assert(ps->tgt_buffer.pixmap); - paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, - false, ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap); - if (ps->o.vsync_use_glfinish) - glFinish(); - else - glFlush(); - glXWaitX(); - glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, - ps->root_height, 0, 1.0, false, false, ®ion, NULL); - fallthrough(); - case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; -#endif - default: assert(0); - } - - x_sync(ps->c); - -#ifdef CONFIG_OPENGL - if (glx_has_context(ps)) { - glFlush(); - glXWaitX(); - } -#endif - -#ifdef DEBUG_REPAINT - struct timespec now = get_time_timespec(); - struct timespec diff = {0}; - timespec_subtract(&diff, &now, &last_paint); - log_trace("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); - last_paint = now; - log_trace("paint:"); - for (win *w = t; w; w = w->prev_trans) - log_trace(" %#010lx", w->id); -#endif - - // Free the paint region - pixman_region32_fini(®ion); -} - -/** - * Query needed X Render / OpenGL filters to check for their existence. - */ -static bool xr_init_blur(session_t *ps) { - // Query filters - xcb_render_query_filters_reply_t *pf = xcb_render_query_filters_reply( - ps->c, xcb_render_query_filters(ps->c, get_tgt_window(ps)), NULL); - if (pf) { - xcb_str_iterator_t iter = xcb_render_query_filters_filters_iterator(pf); - for (; iter.rem; xcb_str_next(&iter)) { - int len = xcb_str_name_length(iter.data); - char *name = xcb_str_name(iter.data); - // Check for the convolution filter - if (strlen(XRFILTER_CONVOLUTION) == len && - !memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION))) - ps->xrfilter_convolution_exists = true; - } - free(pf); - } - - // Turn features off if any required filter is not present - if (!ps->xrfilter_convolution_exists) { - log_error("Xrender convolution filter " - "unsupported by your X server. " - "Background blur is not possible."); - return false; - } - - return true; -} - -/** - * Pregenerate alpha pictures. - */ -static bool init_alpha_picts(session_t *ps) { - ps->alpha_picts = ccalloc(MAX_ALPHA + 1, xcb_render_picture_t); - - for (int i = 0; i <= MAX_ALPHA; ++i) { - double o = (double)i / MAX_ALPHA; - ps->alpha_picts[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0); - if (ps->alpha_picts[i] == XCB_NONE) - return false; - } - return true; -} - -bool init_render(session_t *ps) { - if (ps->o.backend == BKEND_DUMMY) { - return false; - } - - // Initialize OpenGL as early as possible -#ifdef CONFIG_OPENGL - glxext_init(ps->dpy, ps->scr); -#endif - if (bkend_use_glx(ps)) { -#ifdef CONFIG_OPENGL - if (!glx_init(ps, true)) - return false; -#else - log_error("GLX backend support not compiled in."); - return false; -#endif - } - - // Initialize VSync - if (!vsync_init(ps)) { - return false; - } - - // Initialize window GL shader - if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { -#ifdef CONFIG_OPENGL - if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win)) - return false; -#else - log_error("GLSL supported not compiled in, can't load " - "shader."); - return false; -#endif - } - - if (!init_alpha_picts(ps)) { - log_error("Failed to init alpha pictures."); - return false; - } - - // Blur filter - if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL) { - log_warn("Old backends only support blur method \"kernel\". Your blur " - "setting will not be applied"); - ps->o.blur_method = BLUR_METHOD_NONE; - } - - if (ps->o.blur_method == BLUR_METHOD_KERNEL) { - ps->blur_kerns_cache = - ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *); - - bool ret = false; - if (ps->o.backend == BKEND_GLX) { -#ifdef CONFIG_OPENGL - ret = glx_init_blur(ps); -#else - assert(false); -#endif - } else { - ret = xr_init_blur(ps); - } - if (!ret) { - return ret; - } - } - - ps->black_picture = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0); - ps->white_picture = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1); - - if (ps->black_picture == XCB_NONE || ps->white_picture == XCB_NONE) { - log_error("Failed to create solid xrender pictures."); - return false; - } - - // Generates another Picture for shadows if the color is modified by - // user - if (ps->o.shadow_red == 0 && ps->o.shadow_green == 0 && ps->o.shadow_blue == 0) { - ps->cshadow_picture = ps->black_picture; - } else { - ps->cshadow_picture = solid_picture(ps->c, ps->root, true, 1, ps->o.shadow_red, - ps->o.shadow_green, ps->o.shadow_blue); - if (ps->cshadow_picture == XCB_NONE) { - log_error("Failed to create shadow picture."); - return false; - } - } - - // Initialize our rounded corners fragment shader - if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) { -#ifdef CONFIG_OPENGL - if (!glx_init_rounded_corners(ps)) { - log_error("Failed to init rounded corners shader."); - return false; - } -#else - assert(false); -#endif - } - return true; -} - -/** - * Free root tile related things. - */ -void free_root_tile(session_t *ps) { - free_picture(ps->c, &ps->root_tile_paint.pict); -#ifdef CONFIG_OPENGL - free_texture(ps, &ps->root_tile_paint.ptex); -#else - assert(!ps->root_tile_paint.ptex); -#endif - if (ps->root_tile_fill) { - xcb_free_pixmap(ps->c, ps->root_tile_paint.pixmap); - ps->root_tile_paint.pixmap = XCB_NONE; - } - ps->root_tile_paint.pixmap = XCB_NONE; - ps->root_tile_fill = false; -} - -void deinit_render(session_t *ps) { - // Free alpha_picts - for (int i = 0; i <= MAX_ALPHA; ++i) - free_picture(ps->c, &ps->alpha_picts[i]); - free(ps->alpha_picts); - ps->alpha_picts = NULL; - - // Free cshadow_picture and black_picture - if (ps->cshadow_picture == ps->black_picture) - ps->cshadow_picture = XCB_NONE; - else - free_picture(ps->c, &ps->cshadow_picture); - - free_picture(ps->c, &ps->black_picture); - free_picture(ps->c, &ps->white_picture); - - // Free other X resources - free_root_tile(ps); - -#ifdef CONFIG_OPENGL - free(ps->root_tile_paint.fbcfg); - if (bkend_use_glx(ps)) { - glx_destroy(ps); - } -#endif - - if (ps->o.blur_method != BLUR_METHOD_NONE) { - for (int i = 0; i < ps->o.blur_kernel_count; i++) { - free(ps->blur_kerns_cache[i]); - } - free(ps->blur_kerns_cache); - } -} - -// vim: set ts=8 sw=8 noet : |